diff options
Diffstat (limited to 'gallerycommon/src/com/android/gallery3d/exif/ExifTag.java')
-rw-r--r-- | gallerycommon/src/com/android/gallery3d/exif/ExifTag.java | 1441 |
1 files changed, 1441 insertions, 0 deletions
diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java b/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java new file mode 100644 index 000000000..49cb6edbc --- /dev/null +++ b/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java @@ -0,0 +1,1441 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.exif; + +import android.util.SparseArray; + +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; + +/** + * This class stores information of an EXIF tag. + * @see ExifParser + * @see ExifReader + * @see IfdData + * @see ExifData + */ +public class ExifTag { + // Tiff Tags + public static final short TAG_IMAGE_WIDTH = 0x100; + /* + * The height of the image. + */ + public static final short TAG_IMAGE_LENGTH = 0x101; + public static final short TAG_BITS_PER_SAMPLE = 0x102; + public static final short TAG_COMPRESSION = 0x103; + public static final short TAG_PHOTOMETRIC_INTERPRETATION = 0x106; + public static final short TAG_IMAGE_DESCRIPTION = 0x10E; + public static final short TAG_MAKE = 0x10F; + public static final short TAG_MODEL = 0x110; + public static final short TAG_STRIP_OFFSETS = 0x111; + public static final short TAG_ORIENTATION = 0x112; + public static final short TAG_SAMPLES_PER_PIXEL = 0x115; + public static final short TAG_ROWS_PER_STRIP = 0x116; + public static final short TAG_STRIP_BYTE_COUNTS = 0x117; + public static final short TAG_X_RESOLUTION = 0x11A; + public static final short TAG_Y_RESOLUTION = 0x11B; + public static final short TAG_PLANAR_CONFIGURATION = 0x11C; + public static final short TAG_RESOLUTION_UNIT = 0x128; + public static final short TAG_TRANSFER_FUNCTION = 0x12D; + public static final short TAG_SOFTWARE = 0x131; + public static final short TAG_DATE_TIME = 0x132; + public static final short TAG_ARTIST = 0x13B; + public static final short TAG_WHITE_POINT = 0x13E; + public static final short TAG_PRIMARY_CHROMATICITIES = 0x13F; + public static final short TAG_JPEG_INTERCHANGE_FORMAT = 0x201; + public static final short TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = 0x202; + public static final short TAG_Y_CB_CR_COEFFICIENTS = 0x211; + public static final short TAG_Y_CB_CR_SUB_SAMPLING = 0x212; + public static final short TAG_Y_CB_CR_POSITIONING = 0x213; + public static final short TAG_REFERENCE_BLACK_WHITE = 0x214; + public static final short TAG_COPYRIGHT = (short) 0x8298; + public static final short TAG_EXIF_IFD = (short) 0x8769; + public static final short TAG_GPS_IFD = (short) 0x8825; + + // Exif Tags + public static final short TAG_EXPOSURE_TIME = (short) 0x829A; + public static final short TAG_F_NUMBER = (short) 0x829D; + public static final short TAG_EXPOSURE_PROGRAM = (short) 0x8822; + public static final short TAG_SPECTRAL_SENSITIVITY = (short) 0x8824; + public static final short TAG_ISO_SPEED_RATINGS = (short) 0x8827; + public static final short TAG_OECF = (short) 0x8828; + public static final short TAG_EXIF_VERSION = (short) 0x9000; + public static final short TAG_DATE_TIME_ORIGINAL = (short) 0x9003; + public static final short TAG_DATE_TIME_DIGITIZED = (short) 0x9004; + public static final short TAG_COMPONENTS_CONFIGURATION = (short) 0x9101; + public static final short TAG_COMPRESSED_BITS_PER_PIXEL = (short) 0x9102; + public static final short TAG_SHUTTER_SPEED_VALUE = (short) 0x9201; + public static final short TAG_APERTURE_VALUE = (short) 0x9202; + public static final short TAG_BRIGHTNESS_VALUE = (short) 0x9203; + public static final short TAG_EXPOSURE_BIAS_VALUE = (short) 0x9204; + public static final short TAG_MAX_APERTURE_VALUE = (short) 0x9205; + public static final short TAG_SUBJECT_DISTANCE = (short) 0x9206; + public static final short TAG_METERING_MODE = (short) 0x9207; + public static final short TAG_LIGHT_SOURCE = (short) 0x9208; + public static final short TAG_FLASH = (short) 0x9209; + public static final short TAG_FOCAL_LENGTH = (short) 0x920A; + public static final short TAG_SUBJECT_AREA = (short) 0x9214; + public static final short TAG_MAKER_NOTE = (short) 0x927C; + public static final short TAG_USER_COMMENT = (short) 0x9286; + public static final short TAG_SUB_SEC_TIME = (short) 0x9290; + public static final short TAG_SUB_SEC_TIME_ORIGINAL = (short) 0x9291; + public static final short TAG_SUB_SEC_TIME_DIGITIZED = (short) 0x9292; + public static final short TAG_FLASHPIX_VERSION = (short) 0xA000; + public static final short TAG_COLOR_SPACE = (short) 0xA001; + public static final short TAG_PIXEL_X_DIMENSION = (short) 0xA002; + public static final short TAG_PIXEL_Y_DIMENSION = (short) 0xA003; + public static final short TAG_RELATED_SOUND_FILE = (short) 0xA004; + public static final short TAG_INTEROPERABILITY_IFD = (short) 0xA005; + public static final short TAG_FLASH_ENERGY = (short) 0xA20B; + public static final short TAG_SPATIAL_FREQUENCY_RESPONSE = (short) 0xA20C; + public static final short TAG_FOCAL_PLANE_X_RESOLUTION = (short) 0xA20E; + public static final short TAG_FOCAL_PLANE_Y_RESOLUTION = (short) 0xA20F; + public static final short TAG_FOCAL_PLANE_RESOLUTION_UNIT = (short) 0xA210; + public static final short TAG_SUBJECT_LOCATION = (short) 0xA214; + public static final short TAG_EXPOSURE_INDEX = (short) 0xA215; + public static final short TAG_SENSING_METHOD = (short) 0xA217; + public static final short TAG_FILE_SOURCE = (short) 0xA300; + public static final short TAG_SCENE_TYPE = (short) 0xA301; + public static final short TAG_CFA_PATTERN = (short) 0xA302; + public static final short TAG_CUSTOM_RENDERED = (short) 0xA401; + public static final short TAG_EXPOSURE_MODE = (short) 0xA402; + public static final short TAG_WHITE_BALANCE = (short) 0xA403; + public static final short TAG_DIGITAL_ZOOM_RATIO = (short) 0xA404; + public static final short TAG_FOCAL_LENGTH_IN_35_MM_FILE = (short) 0xA405; + public static final short TAG_SCENE_CAPTURE_TYPE = (short) 0xA406; + public static final short TAG_GAIN_CONTROL = (short) 0xA407; + public static final short TAG_CONTRAST = (short) 0xA408; + public static final short TAG_SATURATION = (short) 0xA409; + public static final short TAG_SHARPNESS = (short) 0xA40A; + public static final short TAG_DEVICE_SETTING_DESCRIPTION = (short) 0xA40B; + public static final short TAG_SUBJECT_DISTANCE_RANGE = (short) 0xA40C; + public static final short TAG_IMAGE_UNIQUE_ID = (short) 0xA420; + + // GPS tags + public static final short TAG_GPS_VERSION_ID = 0; + public static final short TAG_GPS_LATITUDE_REF = 1; + public static final short TAG_GPS_LATITUDE = 2; + public static final short TAG_GPS_LONGITUDE_REF = 3; + public static final short TAG_GPS_LONGITUDE = 4; + public static final short TAG_GPS_ALTITUDE_REF = 5; + public static final short TAG_GPS_ALTITUDE = 6; + public static final short TAG_GPS_TIME_STAMP = 7; + public static final short TAG_GPS_SATTELLITES = 8; + public static final short TAG_GPS_STATUS = 9; + public static final short TAG_GPS_MEASURE_MODE = 10; + public static final short TAG_GPS_DOP = 11; + public static final short TAG_GPS_SPEED_REF = 12; + public static final short TAG_GPS_SPEED = 13; + public static final short TAG_GPS_TRACK_REF = 14; + public static final short TAG_GPS_TRACK = 15; + public static final short TAG_GPS_IMG_DIRECTION_REF = 16; + public static final short TAG_GPS_IMG_DIRECTION = 17; + public static final short TAG_GPS_MAP_DATUM = 18; + public static final short TAG_GPS_DEST_LATITUDE_REF = 19; + public static final short TAG_GPS_DEST_LATITUDE = 20; + public static final short TAG_GPS_DEST_LONGITUDE_REF = 21; + public static final short TAG_GPS_DEST_LONGITUDE = 22; + public static final short TAG_GPS_DEST_BEARING_REF = 23; + public static final short TAG_GPS_DEST_BEARING = 24; + public static final short TAG_GPS_DEST_DISTANCE_REF = 25; + public static final short TAG_GPS_DEST_DISTANCE = 26; + public static final short TAG_GPS_PROCESSING_METHOD = 27; + public static final short TAG_GPS_AREA_INFORMATION = 28; + public static final short TAG_GPS_DATA_STAMP = 29; + public static final short TAG_GPS_DIFFERENTIAL = 30; + + // Interoperability tag + public static final short TAG_INTEROPERABILITY_INDEX = 1; + + /** + * Constants for {@link #TAG_ORIENTATION} + */ + public static interface Orientation { + public static final short TOP_LEFT = 1; + public static final short TOP_RIGHT = 2; + public static final short BOTTOM_LEFT = 3; + public static final short BOTTOM_RIGHT = 4; + public static final short LEFT_TOP = 5; + public static final short RIGHT_TOP = 6; + public static final short LEFT_BOTTOM = 7; + public static final short RIGHT_BOTTOM = 8; + } + + /** + * Constants for {@link #TAG_Y_CB_CR_POSITIONING} + */ + public static interface YCbCrPositioning { + public static final short CENTERED = 1; + public static final short CO_SITED = 2; + } + + /** + * Constants for {@link #TAG_COMPRESSION} + */ + public static interface Compression { + public static final short UNCOMPRESSION = 1; + public static final short JPEG = 6; + } + + /** + * Constants for {@link #TAG_RESOLUTION_UNIT} + */ + public static interface ResolutionUnit { + public static final short INCHES = 2; + public static final short CENTIMETERS = 3; + } + + /** + * Constants for {@link #TAG_PHOTOMETRIC_INTERPRETATION} + */ + public static interface PhotometricInterpretation { + public static final short RGB = 2; + public static final short YCBCR = 6; + } + + /** + * Constants for {@link #TAG_PLANAR_CONFIGURATION} + */ + public static interface PlanarConfiguration { + public static final short CHUNKY = 1; + public static final short PLANAR = 2; + } + + /** + * Constants for {@link #TAG_EXPOSURE_PROGRAM} + */ + public static interface ExposureProgram { + public static final short NOT_DEFINED = 0; + public static final short MANUAL = 1; + public static final short NORMAL_PROGRAM = 2; + public static final short APERTURE_PRIORITY = 3; + public static final short SHUTTER_PRIORITY = 4; + public static final short CREATIVE_PROGRAM = 5; + public static final short ACTION_PROGRAM = 6; + public static final short PROTRAIT_MODE = 7; + public static final short LANDSCAPE_MODE = 8; + } + + /** + * Constants for {@link #TAG_METERING_MODE} + */ + public static interface MeteringMode { + public static final short UNKNOWN = 0; + public static final short AVERAGE = 1; + public static final short CENTER_WEIGHTED_AVERAGE = 2; + public static final short SPOT = 3; + public static final short MULTISPOT = 4; + public static final short PATTERN = 5; + public static final short PARTAIL = 6; + public static final short OTHER = 255; + } + + /** + * Constants for {@link #TAG_FLASH} As the definition in Jeita EXIF 2.2 standard, we can + * treat this constant as bitwise flag. + * <p> + * e.g. + * <p> + * short flash = FIRED | RETURN_STROBE_RETURN_LIGHT_DETECTED | MODE_AUTO_MODE + */ + public static interface Flash { + // LSB + public static final short DID_NOT_FIRED = 0; + public static final short FIRED = 1; + // 1st~2nd bits + public static final short RETURN_NO_STROBE_RETURN_DETECTION_FUNCTION = 0 << 1; + public static final short RETURN_STROBE_RETURN_LIGHT_NOT_DETECTED = 2 << 1; + public static final short RETURN_STROBE_RETURN_LIGHT_DETECTED = 3 << 1; + // 3rd~4th bits + public static final short MODE_UNKNOWN = 0 << 3; + public static final short MODE_COMPULSORY_FLASH_FIRING = 1 << 3; + public static final short MODE_COMPULSORY_FLASH_SUPPRESSION = 2 << 3; + public static final short MODE_AUTO_MODE = 3 << 3; + // 5th bit + public static final short FUNCTION_PRESENT = 0 << 5; + public static final short FUNCTION_NO_FUNCTION = 1 << 5; + // 6th bit + public static final short RED_EYE_REDUCTION_NO_OR_UNKNOWN = 0 << 6; + public static final short RED_EYE_REDUCTION_SUPPORT = 1 << 6; + } + + /** + * Constants for {@link #TAG_COLOR_SPACE} + */ + public static interface ColorSpace { + public static final short SRGB = 1; + public static final short UNCALIBRATED = (short) 0xFFFF; + } + + /** + * Constants for {@link #TAG_EXPOSURE_MODE} + */ + public static interface ExposureMode { + public static final short AUTO_EXPOSURE = 0; + public static final short MANUAL_EXPOSURE = 1; + public static final short AUTO_BRACKET = 2; + } + + /** + * Constants for {@link #TAG_WHITE_BALANCE} + */ + public static interface WhiteBalance { + public static final short AUTO = 0; + public static final short MANUAL = 1; + } + + /** + * Constants for {@link #TAG_SCENE_CAPTURE_TYPE} + */ + public static interface SceneCapture { + public static final short STANDARD = 0; + public static final short LANDSCAPE = 1; + public static final short PROTRAIT = 2; + public static final short NIGHT_SCENE = 3; + } + + /** + * Constants for {@link #TAG_COMPONENTS_CONFIGURATION} + */ + public static interface ComponentsConfiguration { + public static final short NOT_EXIST = 0; + public static final short Y = 1; + public static final short CB = 2; + public static final short CR = 3; + public static final short R = 4; + public static final short G = 5; + public static final short B = 6; + } + + /** + * Constants for {@link #TAG_LIGHT_SOURCE} + */ + public static interface LightSource { + public static final short UNKNOWN = 0; + public static final short DAYLIGHT = 1; + public static final short FLUORESCENT = 2; + public static final short TUNGSTEN = 3; + public static final short FLASH = 4; + public static final short FINE_WEATHER = 9; + public static final short CLOUDY_WEATHER = 10; + public static final short SHADE = 11; + public static final short DAYLIGHT_FLUORESCENT = 12; + public static final short DAY_WHITE_FLUORESCENT = 13; + public static final short COOL_WHITE_FLUORESCENT = 14; + public static final short WHITE_FLUORESCENT = 15; + public static final short STANDARD_LIGHT_A = 17; + public static final short STANDARD_LIGHT_B = 18; + public static final short STANDARD_LIGHT_C = 19; + public static final short D55 = 20; + public static final short D65 = 21; + public static final short D75 = 22; + public static final short D50 = 23; + public static final short ISO_STUDIO_TUNGSTEN = 24; + public static final short OTHER = 255; + } + + /** + * Constants for {@link #TAG_SENSING_METHOD} + */ + public static interface SensingMethod { + public static final short NOT_DEFINED = 1; + public static final short ONE_CHIP_COLOR = 2; + public static final short TWO_CHIP_COLOR = 3; + public static final short THREE_CHIP_COLOR = 4; + public static final short COLOR_SEQUENTIAL_AREA = 5; + public static final short TRILINEAR = 7; + public static final short COLOR_SEQUENTIAL_LINEAR = 8; + } + + /** + * Constants for {@link #TAG_FILE_SOURCE} + */ + public static interface FileSource { + public static final short DSC = 3; + } + + /** + * Constants for {@link #TAG_SCENE_TYPE} + */ + public static interface SceneType { + public static final short DIRECT_PHOTOGRAPHED = 1; + } + + /** + * Constants for {@link #TAG_GAIN_CONTROL} + */ + public static interface GainControl { + public static final short NONE = 0; + public static final short LOW_UP = 1; + public static final short HIGH_UP = 2; + public static final short LOW_DOWN = 3; + public static final short HIGH_DOWN = 4; + } + + /** + * Constants for {@link #TAG_CONTRAST} + */ + public static interface Contrast { + public static final short NORMAL = 0; + public static final short SOFT = 1; + public static final short HARD = 2; + } + + /** + * Constants for {@link #TAG_SATURATION} + */ + public static interface Saturation { + public static final short NORMAL = 0; + public static final short LOW = 1; + public static final short HIGH = 2; + } + + /** + * Constants for {@link #TAG_SHARPNESS} + */ + public static interface Sharpness { + public static final short NORMAL = 0; + public static final short SOFT = 1; + public static final short HARD = 2; + } + + /** + * Constants for {@link #TAG_SUBJECT_DISTANCE} + */ + public static interface SubjectDistance { + public static final short UNKNOWN = 0; + public static final short MACRO = 1; + public static final short CLOSE_VIEW = 2; + public static final short DISTANT_VIEW = 3; + } + + /** + * Constants for {@link #TAG_GPS_LATITUDE_REF}, {@link #TAG_GPS_DEST_LATITUDE_REF} + */ + public static interface GpsLatitudeRef { + public static final String NORTH = "N"; + public static final String SOUTH = "S"; + } + + /** + * Constants for {@link #TAG_GPS_LONGITUDE_REF}, {@link #TAG_GPS_DEST_LONGITUDE_REF} + */ + public static interface GpsLongitudeRef { + public static final String EAST = "E"; + public static final String WEST = "W"; + } + + /** + * Constants for {@link #TAG_GPS_ALTITUDE_REF} + */ + public static interface GpsAltitudeRef { + public static final short SEA_LEVEL = 0; + public static final short SEA_LEVEL_NEGATIVE = 1; + } + + /** + * Constants for {@link #TAG_GPS_STATUS} + */ + public static interface GpsStatus { + public static final String IN_PROGRESS = "A"; + public static final String INTEROPERABILITY = "V"; + } + + /** + * Constants for {@link #TAG_GPS_MEASURE_MODE} + */ + public static interface GpsMeasureMode { + public static final String MODE_2_DIMENSIONAL = "2"; + public static final String MODE_3_DIMENSIONAL = "3"; + } + + /** + * Constants for {@link #TAG_GPS_SPEED_REF}, {@link #TAG_GPS_DEST_DISTANCE_REF} + */ + public static interface GpsSpeedRef { + public static final String KILOMETERS = "K"; + public static final String MILES = "M"; + public static final String KNOTS = "N"; + } + + /** + * Constants for {@link #TAG_GPS_TRACK_REF}, {@link #TAG_GPS_IMG_DIRECTION_REF}, + * {@link #TAG_GPS_DEST_BEARING_REF} + */ + public static interface GpsTrackRef { + public static final String TRUE_DIRECTION = "T"; + public static final String MAGNETIC_DIRECTION = "M"; + } + + /** + * Constants for {@link #TAG_GPS_DIFFERENTIAL} + */ + public static interface GpsDifferential { + public static final short WITHOUT_DIFFERENTIAL_CORRECTION = 0; + public static final short DIFFERENTIAL_CORRECTION_APPLIED = 1; + } + + /** + * The BYTE type in the EXIF standard. An 8-bit unsigned integer. + */ + public static final short TYPE_UNSIGNED_BYTE = 1; + /** + * The ASCII type in the EXIF standard. An 8-bit byte containing one 7-bit ASCII code. + * The final byte is terminated with NULL. + */ + public static final short TYPE_ASCII = 2; + /** + * The SHORT type in the EXIF standard. A 16-bit (2-byte) unsigned integer + */ + public static final short TYPE_UNSIGNED_SHORT = 3; + /** + * The LONG type in the EXIF standard. A 32-bit (4-byte) unsigned integer + */ + public static final short TYPE_UNSIGNED_LONG = 4; + /** + * The RATIONAL type of EXIF standard. It consists of two LONGs. The first one is the numerator + * and the second one expresses the denominator. + */ + public static final short TYPE_UNSIGNED_RATIONAL = 5; + /** + * The UNDEFINED type in the EXIF standard. An 8-bit byte that can take any value + * depending on the field definition. + */ + public static final short TYPE_UNDEFINED = 7; + /** + * The SLONG type in the EXIF standard. A 32-bit (4-byte) signed integer + * (2's complement notation). + */ + public static final short TYPE_LONG = 9; + /** + * The SRATIONAL type of EXIF standard. It consists of two SLONGs. The first one is the + * numerator and the second one is the denominator. + */ + public static final short TYPE_RATIONAL = 10; + + private static final int TYPE_TO_SIZE_MAP[] = new int[11]; + static { + TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_BYTE] = 1; + TYPE_TO_SIZE_MAP[TYPE_ASCII] = 1; + TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_SHORT] = 2; + TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_LONG] = 4; + TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_RATIONAL] = 8; + TYPE_TO_SIZE_MAP[TYPE_UNDEFINED] = 1; + TYPE_TO_SIZE_MAP[TYPE_LONG] = 4; + TYPE_TO_SIZE_MAP[TYPE_RATIONAL] = 8; + } + + /** + * Gets the element size of the given data type. + * + * @see #TYPE_ASCII + * @see #TYPE_LONG + * @see #TYPE_RATIONAL + * @see #TYPE_UNDEFINED + * @see #TYPE_UNSIGNED_BYTE + * @see #TYPE_UNSIGNED_LONG + * @see #TYPE_UNSIGNED_RATIONAL + * @see #TYPE_UNSIGNED_SHORT + */ + public static int getElementSize(short type) { + return TYPE_TO_SIZE_MAP[type]; + } + + private static volatile SparseArray<Integer> sTagInfo = null; + private static volatile SparseArray<Integer> sInteroperTagInfo = null; + private static final int SIZE_UNDEFINED = 0; + + private static SparseArray<Integer> getTagInfo() { + if (sTagInfo == null) { + synchronized(ExifTag.class) { + if (sTagInfo == null) { + sTagInfo = new SparseArray<Integer>(); + initTagInfo(); + } + } + } + return sTagInfo; + } + + private static SparseArray<Integer> getInteroperTagInfo() { + if (sInteroperTagInfo == null) { + synchronized(ExifTag.class) { + if (sInteroperTagInfo == null) { + sInteroperTagInfo = new SparseArray<Integer>(); + sInteroperTagInfo.put(TAG_INTEROPERABILITY_INDEX, + (IfdId.TYPE_IFD_INTEROPERABILITY << 24) + | TYPE_ASCII << 16 | SIZE_UNDEFINED); + } + } + } + return sInteroperTagInfo; + } + + private static void initTagInfo() { + /** + * We put tag information in a 4-bytes integer. The first byte is the + * IFD of the tag, and the second byte is the default data type. The + * last two byte are a short value indicating the component count of this + * tag. + */ + sTagInfo.put(TAG_MAKE, + (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_IMAGE_WIDTH, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1); + sTagInfo.put(TAG_IMAGE_LENGTH, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1); + sTagInfo.put(TAG_BITS_PER_SAMPLE, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 3); + sTagInfo.put(TAG_COMPRESSION, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_PHOTOMETRIC_INTERPRETATION, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_ORIENTATION, (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_SAMPLES_PER_PIXEL, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_PLANAR_CONFIGURATION, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_Y_CB_CR_SUB_SAMPLING, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 2); + sTagInfo.put(TAG_Y_CB_CR_POSITIONING, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_X_RESOLUTION, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_Y_RESOLUTION, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_RESOLUTION_UNIT, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_STRIP_OFFSETS, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_ROWS_PER_STRIP, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1); + sTagInfo.put(TAG_STRIP_BYTE_COUNTS, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_JPEG_INTERCHANGE_FORMAT, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1); + sTagInfo.put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1); + sTagInfo.put(TAG_TRANSFER_FUNCTION, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_SHORT << 16 | 3 * 256); + sTagInfo.put(TAG_WHITE_POINT, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 2); + sTagInfo.put(TAG_PRIMARY_CHROMATICITIES, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 6); + sTagInfo.put(TAG_Y_CB_CR_COEFFICIENTS, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 3); + sTagInfo.put(TAG_REFERENCE_BLACK_WHITE, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 6); + sTagInfo.put(TAG_DATE_TIME, + (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | 20); + sTagInfo.put(TAG_IMAGE_DESCRIPTION, + (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_MAKE, + (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_MODEL, + (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_SOFTWARE, + (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_ARTIST, + (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_COPYRIGHT, + (IfdId.TYPE_IFD_0 << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_EXIF_IFD, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1); + sTagInfo.put(TAG_GPS_IFD, + (IfdId.TYPE_IFD_0 << 24) | TYPE_UNSIGNED_LONG << 16 | 1); + + // EXIF TAG + sTagInfo.put(TAG_EXIF_VERSION, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | 4); + sTagInfo.put(TAG_FLASHPIX_VERSION, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | 4); + sTagInfo.put(TAG_COLOR_SPACE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_COMPONENTS_CONFIGURATION, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | 4); + sTagInfo.put(TAG_COMPRESSED_BITS_PER_PIXEL, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_PIXEL_X_DIMENSION, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_LONG << 16 | 1); + sTagInfo.put(TAG_PIXEL_Y_DIMENSION, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_LONG << 16 | 1); + sTagInfo.put(TAG_MAKER_NOTE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_USER_COMMENT, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_RELATED_SOUND_FILE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | 13); + sTagInfo.put(TAG_DATE_TIME_ORIGINAL, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | 20); + sTagInfo.put(TAG_DATE_TIME_DIGITIZED, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | 20); + sTagInfo.put(TAG_SUB_SEC_TIME, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_SUB_SEC_TIME_ORIGINAL, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_SUB_SEC_TIME_DIGITIZED, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_IMAGE_UNIQUE_ID, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | 33); + sTagInfo.put(TAG_EXPOSURE_TIME, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_F_NUMBER, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_EXPOSURE_PROGRAM, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_SPECTRAL_SENSITIVITY, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_ISO_SPEED_RATINGS, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_OECF, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_SHUTTER_SPEED_VALUE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_RATIONAL << 16 | 1); + sTagInfo.put(TAG_APERTURE_VALUE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_BRIGHTNESS_VALUE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_RATIONAL << 16 | 1); + sTagInfo.put(TAG_EXPOSURE_BIAS_VALUE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_RATIONAL << 16 | 1); + sTagInfo.put(TAG_MAX_APERTURE_VALUE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_SUBJECT_DISTANCE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_METERING_MODE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_LIGHT_SOURCE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_FLASH, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_FOCAL_LENGTH, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_SUBJECT_AREA, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_FLASH_ENERGY, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_SPATIAL_FREQUENCY_RESPONSE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_FOCAL_PLANE_X_RESOLUTION, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_FOCAL_PLANE_Y_RESOLUTION, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_FOCAL_PLANE_RESOLUTION_UNIT, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_SUBJECT_LOCATION, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 2); + sTagInfo.put(TAG_EXPOSURE_INDEX, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_SENSING_METHOD, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_FILE_SOURCE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | 1); + sTagInfo.put(TAG_SCENE_TYPE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | 1); + sTagInfo.put(TAG_CFA_PATTERN, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_CUSTOM_RENDERED, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_EXPOSURE_MODE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_WHITE_BALANCE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_DIGITAL_ZOOM_RATIO, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_FOCAL_LENGTH_IN_35_MM_FILE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_SCENE_CAPTURE_TYPE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_GAIN_CONTROL, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_CONTRAST, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_SATURATION, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_SHARPNESS, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + sTagInfo.put(TAG_DEVICE_SETTING_DESCRIPTION, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_SUBJECT_DISTANCE_RANGE, + (IfdId.TYPE_IFD_EXIF << 24) | TYPE_UNSIGNED_SHORT << 16 | 1); + // GPS tag + sTagInfo.put(TAG_GPS_VERSION_ID, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_BYTE << 16 | 4); + sTagInfo.put(TAG_GPS_LATITUDE_REF, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2); + sTagInfo.put(TAG_GPS_LONGITUDE_REF, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2); + sTagInfo.put(TAG_GPS_LATITUDE, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_RATIONAL << 16 | 3); + sTagInfo.put(TAG_GPS_LONGITUDE, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_RATIONAL << 16 | 3); + sTagInfo.put(TAG_GPS_ALTITUDE_REF, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_BYTE << 16 | 1); + sTagInfo.put(TAG_GPS_ALTITUDE, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_GPS_TIME_STAMP, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 3); + sTagInfo.put(TAG_GPS_SATTELLITES, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_GPS_STATUS, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2); + sTagInfo.put(TAG_GPS_MEASURE_MODE, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2); + sTagInfo.put(TAG_GPS_DOP, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_GPS_SPEED_REF, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2); + sTagInfo.put(TAG_GPS_SPEED, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_GPS_TRACK_REF, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2); + sTagInfo.put(TAG_GPS_TRACK, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_GPS_IMG_DIRECTION_REF, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2); + sTagInfo.put(TAG_GPS_IMG_DIRECTION, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_GPS_MAP_DATUM, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_GPS_DEST_LATITUDE_REF, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2); + sTagInfo.put(TAG_GPS_DEST_LATITUDE, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_GPS_DEST_BEARING_REF, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2); + sTagInfo.put(TAG_GPS_DEST_BEARING, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_GPS_DEST_DISTANCE_REF, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 2); + sTagInfo.put(TAG_GPS_DEST_DISTANCE, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_RATIONAL << 16 | 1); + sTagInfo.put(TAG_GPS_PROCESSING_METHOD, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_GPS_AREA_INFORMATION, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNDEFINED << 16 | SIZE_UNDEFINED); + sTagInfo.put(TAG_GPS_DATA_STAMP, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_ASCII << 16 | 11); + sTagInfo.put(TAG_GPS_DIFFERENTIAL, + (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_SHORT << 16 | 11); + } + + private final short mTagId; + private final short mDataType; + private final int mIfd; + private final boolean mComponentCountDefined; + private int mComponentCount; + private Object mValue; + private int mOffset; + + static private short getTypeFromInfo(int info) { + return (short) ((info >> 16) & 0xff); + } + + static private int getComponentCountFromInfo(int info) { + return info & 0xffff; + } + + static private int getIfdIdFromInfo(int info) { + return (info >> 24) & 0xff; + } + + static private boolean getComponentCountDefined(short tagId, int ifd) { + Integer info = (ifd == IfdId.TYPE_IFD_INTEROPERABILITY) ? + getInteroperTagInfo().get(tagId) : getTagInfo().get(tagId); + if (info == null) return false; + return getComponentCountFromInfo(info) != SIZE_UNDEFINED; + } + + static int getIfdIdFromTagId(short tagId) { + Integer info = getTagInfo().get(tagId); + if (info == null) { + throw new IllegalArgumentException("Unknown Tag ID: " + tagId); + } + return getIfdIdFromInfo(info); + } + + /** + * Create a tag with given ID. For tags related to interoperability and thumbnail, call + * {@link #buildInteroperabilityTag(short)} and {@link #buildThumbnailTag(short)} respectively. + * @exception IllegalArgumentException If the ID is invalid. + */ + static public ExifTag buildTag(short tagId) { + Integer info = getTagInfo().get(tagId); + if (info == null) { + throw new IllegalArgumentException("Unknown Tag ID: " + tagId); + } + return new ExifTag(tagId, getTypeFromInfo(info), + getComponentCountFromInfo(info), + getIfdIdFromInfo(info)); + } + + /** + * Create a tag related to thumbnail with given ID. + * @exception IllegalArgumentException If the ID is invalid. + */ + static public ExifTag buildThumbnailTag(short tagId) { + Integer info = getTagInfo().get(tagId); + if (info == null || getIfdIdFromInfo(info) != IfdId.TYPE_IFD_0) { + throw new IllegalArgumentException("Unknown Thumnail Tag ID: " + tagId); + } + return new ExifTag(tagId, getTypeFromInfo(info), + getComponentCountFromInfo(info), + IfdId.TYPE_IFD_1); + } + + /** + * Create a tag related to interoperability with given ID. + * @exception IllegalArgumentException If the ID is invalid. + */ + static public ExifTag buildInteroperabilityTag(short tagId) { + Integer info = getInteroperTagInfo().get(tagId); + if (info == null || getIfdIdFromInfo(info) != IfdId.TYPE_IFD_INTEROPERABILITY) { + throw new RuntimeException("Unknown Interoperability Tag ID: " + tagId); + } + return new ExifTag(tagId, getTypeFromInfo(info), + getComponentCountFromInfo(info), + IfdId.TYPE_IFD_INTEROPERABILITY); + } + + ExifTag(short tagId, short type, int componentCount, int ifd) { + mTagId = tagId; + mDataType = type; + mComponentCount = componentCount; + mComponentCountDefined = getComponentCountDefined(tagId, ifd); + mIfd = ifd; + } + + /** + * Returns the ID of the IFD this tag belongs to. + * + * @see IfdId#TYPE_IFD_0 + * @see IfdId#TYPE_IFD_1 + * @see IfdId#TYPE_IFD_EXIF + * @see IfdId#TYPE_IFD_GPS + * @see IfdId#TYPE_IFD_INTEROPERABILITY + */ + public int getIfd() { + return mIfd; + } + + /** + * Gets the ID of this tag. + */ + public short getTagId() { + return mTagId; + } + + /** + * Gets the data type of this tag + * + * @see #TYPE_ASCII + * @see #TYPE_LONG + * @see #TYPE_RATIONAL + * @see #TYPE_UNDEFINED + * @see #TYPE_UNSIGNED_BYTE + * @see #TYPE_UNSIGNED_LONG + * @see #TYPE_UNSIGNED_RATIONAL + * @see #TYPE_UNSIGNED_SHORT + */ + public short getDataType() { + return mDataType; + } + + /** + * Gets the total data size in bytes of the value of this tag. + */ + public int getDataSize() { + return getComponentCount() * getElementSize(getDataType()); + } + + /** + * Gets the component count of this tag. + */ + public int getComponentCount() { + return mComponentCount; + } + + /** + * Returns true if this ExifTag contains value; otherwise, this tag will contain an offset value + * that links to the area where the actual value is located. + * + * @see #getOffset() + */ + public boolean hasValue() { + return mValue != null; + } + + /** + * Gets the offset of this tag. This is only valid if this data size > 4 and contains an offset + * to the location of the actual value. + */ + public int getOffset() { + return mOffset; + } + + /** + * Sets the offset of this tag. + */ + void setOffset(int offset) { + mOffset = offset; + } + + private void checkComponentCountOrThrow(int count) + throws IllegalArgumentException { + if (mComponentCountDefined && (mComponentCount != count)) { + throw new IllegalArgumentException("Tag " + mTagId + ": Required " + + mComponentCount + " components but was given " + count + + " component(s)"); + } + } + + private void throwTypeNotMatchedException(String className) + throws IllegalArgumentException { + throw new IllegalArgumentException("Tag " + mTagId + ": expect type " + + convertTypeToString(mDataType) + " but got " + className); + } + + private static String convertTypeToString(short type) { + switch (type) { + case TYPE_UNSIGNED_BYTE: + return "UNSIGNED_BYTE"; + case TYPE_ASCII: + return "ASCII"; + case TYPE_UNSIGNED_SHORT: + return "UNSIGNED_SHORT"; + case TYPE_UNSIGNED_LONG: + return "UNSIGNED_LONG"; + case TYPE_UNSIGNED_RATIONAL: + return "UNSIGNED_RATIONAL"; + case TYPE_UNDEFINED: + return "UNDEFINED"; + case TYPE_LONG: + return "LONG"; + case TYPE_RATIONAL: + return "RATIONAL"; + default: + return ""; + } + } + + private static final int UNSIGNED_SHORT_MAX = 65535; + private static final long UNSIGNED_LONG_MAX = 4294967295L; + private static final long LONG_MAX = Integer.MAX_VALUE; + private static final long LONG_MIN = Integer.MIN_VALUE; + + private void checkOverflowForUnsignedShort(int[] value) { + for (int v : value) { + if (v > UNSIGNED_SHORT_MAX || v < 0) { + throw new IllegalArgumentException( + "Tag " + mTagId+ ": Value" + v + + " is illegal for type UNSIGNED_SHORT"); + } + } + } + + private void checkOverflowForUnsignedLong(long[] value) { + for (long v: value) { + if (v < 0 || v > UNSIGNED_LONG_MAX) { + throw new IllegalArgumentException( + "Tag " + mTagId+ ": Value" + v + + " is illegal for type UNSIGNED_LONG"); + } + } + } + + private void checkOverflowForUnsignedLong(int[] value) { + for (int v: value) { + if (v < 0) { + throw new IllegalArgumentException( + "Tag " + mTagId+ ": Value" + v + + " is illegal for type UNSIGNED_LONG"); + } + } + } + + private void checkOverflowForUnsignedRational(Rational[] value) { + for (Rational v: value) { + if (v.getNominator() < 0 || v.getDenominator() < 0 + || v.getNominator() > UNSIGNED_LONG_MAX + || v.getDenominator() > UNSIGNED_LONG_MAX) { + throw new IllegalArgumentException( + "Tag " + mTagId+ ": Value" + v + + " is illegal for type UNSIGNED_RATIONAL"); + } + } + } + + private void checkOverflowForRational(Rational[] value) { + for (Rational v: value) { + if (v.getNominator() < LONG_MIN || v.getDenominator() < LONG_MIN + || v.getNominator() > LONG_MAX + || v.getDenominator() > LONG_MAX) { + throw new IllegalArgumentException( + "Tag " + mTagId+ ": Value" + v + + " is illegal for type RATIONAL"); + } + } + } + + /** + * Sets integer values into this tag. + * @exception IllegalArgumentException for the following situation: + * <ul> + * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT}, + * {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.</li> + * <li>The value overflows. </li> + * <li>The value.length does NOT match the definition of component count in + * EXIF standard.</li> + * </ul> + */ + public void setValue(int[] value) { + checkComponentCountOrThrow(value.length); + if (mDataType != TYPE_UNSIGNED_SHORT && mDataType != TYPE_LONG && + mDataType != TYPE_UNSIGNED_LONG) { + throwTypeNotMatchedException("int"); + } + if (mDataType == TYPE_UNSIGNED_SHORT) { + checkOverflowForUnsignedShort(value); + } else if (mDataType == TYPE_UNSIGNED_LONG) { + checkOverflowForUnsignedLong(value); + } + + long[] data = new long[value.length]; + for (int i = 0; i < value.length; i++) { + data[i] = value[i]; + } + mValue = data; + mComponentCount = value.length; + } + + /** + * Sets integer values into this tag. + * @exception IllegalArgumentException For the following situation: + * <ul> + * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT}, + * {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.</li> + * <li>The value overflows.</li> + * <li>The component count in the definition of EXIF standard is not 1.</li> + * </ul> + */ + public void setValue(int value) { + checkComponentCountOrThrow(1); + setValue(new int[] {value}); + } + + /** + * Sets long values into this tag. + * @exception IllegalArgumentException For the following situation: + * <ul> + * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.</li> + * <li>The value overflows. </li> + * <li>The value.length does NOT match the definition of component count in + * EXIF standard.</li> + * </ul> + */ + public void setValue(long[] value) { + checkComponentCountOrThrow(value.length); + if (mDataType != TYPE_UNSIGNED_LONG) { + throwTypeNotMatchedException("long"); + } + checkOverflowForUnsignedLong(value); + mValue = value; + mComponentCount = value.length; + } + + /** + * Sets long values into this tag. + * @exception IllegalArgumentException For the following situation: + * <ul> + * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.</li> + * <li>The value overflows. </li> + * <li>The component count in the definition of EXIF standard is not 1.</li> + * </ul> + */ + public void setValue(long value) { + setValue(new long[] {value}); + } + + /** + * Sets string values into this tag. + * @exception IllegalArgumentException If the data type is not {@link #TYPE_ASCII} + * or value.length() + 1 does NOT fit the definition of the component count in the + * EXIF standard. + */ + public void setValue(String value) { + checkComponentCountOrThrow(value.length() + 1); + if (mDataType != TYPE_ASCII) { + throwTypeNotMatchedException("String"); + } + mComponentCount = value.length() + 1; + mValue = value; + } + + /** + * Sets Rational values into this tag. + * @exception IllegalArgumentException For the following situation: + * <ul> + * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL} or + * {@link #TYPE_RATIONAL} .</li> + * <li>The value overflows. </li> + * <li>The value.length does NOT match the definition of component count in + * EXIF standard.</li> + * </ul> + */ + public void setValue(Rational[] value) { + if (mDataType == TYPE_UNSIGNED_RATIONAL) { + checkOverflowForUnsignedRational(value); + } else if (mDataType == TYPE_RATIONAL) { + checkOverflowForRational(value); + } else { + throwTypeNotMatchedException("Rational"); + } + checkComponentCountOrThrow(value.length); + mValue = value; + mComponentCount = value.length; + } + + /** + * Sets Rational values into this tag. + * @exception IllegalArgumentException For the following situation: + * <ul> + * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL} or + * {@link #TYPE_RATIONAL} .</li> + * <li>The value overflows. </li> + * <li>The component count in the definition of EXIF standard is not 1.</li> + * </ul> + * */ + public void setValue(Rational value) { + setValue(new Rational[] {value}); + } + + /** + * Sets byte values into this tag. + * @exception IllegalArgumentException For the following situation: + * <ul> + * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_BYTE} or + * {@link #TYPE_UNDEFINED} .</li> + * <li>The length does NOT match the definition of component count in EXIF standard.</li> + * </ul> + * */ + public void setValue(byte[] value, int offset, int length) { + checkComponentCountOrThrow(length); + if (mDataType != TYPE_UNSIGNED_BYTE && mDataType != TYPE_UNDEFINED) { + throwTypeNotMatchedException("byte"); + } + mValue = new byte[length]; + System.arraycopy(value, offset, mValue, 0, length); + mComponentCount = length; + } + + /** + * Equivalent to setValue(value, 0, value.length). + */ + public void setValue(byte[] value) { + setValue(value, 0, value.length); + } + + private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy:MM:dd kk:mm:ss"); + + /** + * Sets a timestamp to this tag. The method converts the timestamp with the format of + * "yyyy:MM:dd kk:mm:ss" and calls {@link #setValue(String)}. + * + * @param time the number of milliseconds since Jan. 1, 1970 GMT + * @exception IllegalArgumentException If the data type is not {@link #TYPE_ASCII} + * or the component count of this tag is not 20 or undefined + */ + public void setTimeValue(long time) { + // synchronized on TIME_FORMAT as SimpleDateFormat is not thread safe + synchronized (TIME_FORMAT) { + setValue(TIME_FORMAT.format(new Date(time))); + } + } + + /** + * Gets the {@link #TYPE_UNSIGNED_SHORT} data. + * @exception IllegalArgumentException If the type is NOT {@link #TYPE_UNSIGNED_SHORT}. + */ + public int getUnsignedShort(int index) { + if (mDataType != TYPE_UNSIGNED_SHORT) { + throw new IllegalArgumentException("Cannot get UNSIGNED_SHORT value from " + + convertTypeToString(mDataType)); + } + return (int) (((long[]) mValue) [index]); + } + + /** + * Gets the {@link #TYPE_LONG} data. + * @exception IllegalArgumentException If the type is NOT {@link #TYPE_LONG}. + */ + public int getLong(int index) { + if (mDataType != TYPE_LONG) { + throw new IllegalArgumentException("Cannot get LONG value from " + + convertTypeToString(mDataType)); + } + return (int) (((long[]) mValue) [index]); + } + + /** + * Gets the {@link #TYPE_UNSIGNED_LONG} data. + * @exception IllegalArgumentException If the type is NOT {@link #TYPE_UNSIGNED_LONG}. + */ + public long getUnsignedLong(int index) { + if (mDataType != TYPE_UNSIGNED_LONG) { + throw new IllegalArgumentException("Cannot get UNSIGNED LONG value from " + + convertTypeToString(mDataType)); + } + return ((long[]) mValue) [index]; + } + + /** + * Gets the {@link #TYPE_ASCII} data. + * @exception IllegalArgumentException If the type is NOT {@link #TYPE_ASCII}. + */ + public String getString() { + if (mDataType != TYPE_ASCII) { + throw new IllegalArgumentException("Cannot get ASCII value from " + + convertTypeToString(mDataType)); + } + return (String) mValue; + } + + /** + * Gets the {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL} data. + * @exception IllegalArgumentException If the type is NOT {@link #TYPE_RATIONAL} or + * {@link #TYPE_UNSIGNED_RATIONAL}. + */ + public Rational getRational(int index) { + if ((mDataType != TYPE_RATIONAL) && (mDataType != TYPE_UNSIGNED_RATIONAL)) { + throw new IllegalArgumentException("Cannot get RATIONAL value from " + + convertTypeToString(mDataType)); + } + return ((Rational[]) mValue) [index]; + } + + /** + * Equivalent to getBytes(buffer, 0, buffer.length). + */ + public void getBytes(byte[] buf) { + getBytes(buf, 0, buf.length); + } + + /** + * Gets the {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE} data. + * + * @param buf the byte array in which to store the bytes read. + * @param offset the initial position in buffer to store the bytes. + * @param length the maximum number of bytes to store in buffer. If length > component count, + * only the valid bytes will be stored. + * + * @exception IllegalArgumentException If the type is NOT {@link #TYPE_UNDEFINED} or + * {@link #TYPE_UNSIGNED_BYTE}. + */ + public void getBytes(byte[] buf, int offset, int length) { + if ((mDataType != TYPE_UNDEFINED) && (mDataType != TYPE_UNSIGNED_BYTE)) { + throw new IllegalArgumentException("Cannot get BYTE value from " + + convertTypeToString(mDataType)); + } + System.arraycopy(mValue, 0, buf, offset, + (length > mComponentCount) ? mComponentCount : length); + } + + /** + * Returns a string representation of the value of this tag. + */ + public String valueToString() { + StringBuilder sbuilder = new StringBuilder(); + switch (getDataType()) { + case ExifTag.TYPE_UNDEFINED: + case ExifTag.TYPE_UNSIGNED_BYTE: + byte buf[] = new byte[getComponentCount()]; + getBytes(buf); + for(int i = 0, n = getComponentCount(); i < n; i++) { + if(i != 0) sbuilder.append(" "); + sbuilder.append(String.format("%02x", buf[i])); + } + break; + case ExifTag.TYPE_ASCII: + sbuilder.append(getString()); + break; + case ExifTag.TYPE_UNSIGNED_LONG: + for(int i = 0, n = getComponentCount(); i < n; i++) { + if(i != 0) sbuilder.append(" "); + sbuilder.append(getUnsignedLong(i)); + } + break; + case ExifTag.TYPE_RATIONAL: + case ExifTag.TYPE_UNSIGNED_RATIONAL: + for(int i = 0, n = getComponentCount(); i < n; i++) { + Rational r = getRational(i); + if(i != 0) sbuilder.append(" "); + sbuilder.append(r.getNominator()).append("/").append(r.getDenominator()); + } + break; + case ExifTag.TYPE_UNSIGNED_SHORT: + for(int i = 0, n = getComponentCount(); i < n; i++) { + if(i != 0) sbuilder.append(" "); + sbuilder.append(getUnsignedShort(i)); + } + break; + case ExifTag.TYPE_LONG: + for(int i = 0, n = getComponentCount(); i < n; i++) { + if(i != 0) sbuilder.append(" "); + sbuilder.append(getLong(i)); + } + break; + } + return sbuilder.toString(); + } + + /** + * Returns true if the ID is one of the following: {@link #TAG_EXIF_IFD}, + * {@link #TAG_GPS_IFD}, {@link #TAG_JPEG_INTERCHANGE_FORMAT}, + * {@link #TAG_STRIP_OFFSETS}, {@link #TAG_INTEROPERABILITY_IFD} + */ + static boolean isOffsetTag(short tagId) { + return tagId == TAG_EXIF_IFD + || tagId == TAG_GPS_IFD + || tagId == TAG_JPEG_INTERCHANGE_FORMAT + || tagId == TAG_STRIP_OFFSETS + || tagId == TAG_INTEROPERABILITY_IFD; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ExifTag) { + ExifTag tag = (ExifTag) obj; + if (mValue != null) { + if (mValue instanceof long[]) { + if (!(tag.mValue instanceof long[])) return false; + return Arrays.equals((long[]) mValue, (long[]) tag.mValue); + } else if (mValue instanceof Rational[]) { + if (!(tag.mValue instanceof Rational[])) return false; + return Arrays.equals((Rational[]) mValue, (Rational[]) tag.mValue); + } else if (mValue instanceof byte[]) { + if (!(tag.mValue instanceof byte[])) return false; + return Arrays.equals((byte[]) mValue, (byte[]) tag.mValue); + } else { + return mValue.equals(tag.mValue); + } + } else { + return tag.mValue == null; + } + } + return false; + } +} |