diff options
Diffstat (limited to 'gallerycommon/src/com/android/gallery3d/exif/ExifTag.java')
-rw-r--r-- | gallerycommon/src/com/android/gallery3d/exif/ExifTag.java | 1798 |
1 files changed, 683 insertions, 1115 deletions
diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java b/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java index 753b18ce8..0b31c4df5 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java +++ b/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java @@ -16,488 +16,26 @@ package com.android.gallery3d.exif; -import android.util.SparseArray; - import java.nio.charset.Charset; 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 + * This class stores information of an EXIF tag. For more information about + * defined EXIF tags, please read the Jeita EXIF 2.2 standard. Tags should be + * instantiated using {@link ExifInterface#buildTag}. + * + * @see ExifInterface */ 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_DATE_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. + * 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; /** @@ -509,13 +47,13 @@ public class ExifTag { */ 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. + * 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. + * 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; /** @@ -524,12 +62,18 @@ public class ExifTag { */ 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. + * 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 Charset US_ASCII = Charset.forName("US-ASCII"); private static final int TYPE_TO_SIZE_MAP[] = new int[11]; + 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; + static { TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_BYTE] = 1; TYPE_TO_SIZE_MAP[TYPE_ASCII] = 1; @@ -541,391 +85,69 @@ public class ExifTag { 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_DATE_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); - } + static final int SIZE_UNDEFINED = 0; - private static Charset US_ASCII = Charset.forName("US-ASCII"); + // Exif TagId private final short mTagId; + // Exif Tag Type private final short mDataType; - private final int mIfd; - private final boolean mComponentCountDefined; - private int mComponentCount; + // If tag has defined count + private boolean mHasDefinedDefaultComponentCount; + // Actual data count in tag (should be number of elements in value array) + private int mComponentCountActual; + // The ifd that this tag should be put in + private int mIfd; + // The value (array of elements of type Tag Type) private Object mValue; + // Value offset in exif header. 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)); - } + private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy:MM:dd kk:mm:ss"); /** - * Create a tag related to thumbnail with given ID. - * @exception IllegalArgumentException If the ID is invalid. + * Returns true if the given IFD is a valid IFD. */ - 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); + public static boolean isValidIfd(int ifdId) { + return ifdId == IfdId.TYPE_IFD_0 || ifdId == IfdId.TYPE_IFD_1 + || ifdId == IfdId.TYPE_IFD_EXIF || ifdId == IfdId.TYPE_IFD_INTEROPERABILITY + || ifdId == IfdId.TYPE_IFD_GPS; } /** - * Create a tag related to interoperability with given ID. - * @exception IllegalArgumentException If the ID is invalid. + * Returns true if a given type is a valid tag type. */ - 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); - } - - static boolean isValidType(short type) { + public static boolean isValidType(short type) { return type == TYPE_UNSIGNED_BYTE || type == TYPE_ASCII || type == TYPE_UNSIGNED_SHORT || type == TYPE_UNSIGNED_LONG || type == TYPE_UNSIGNED_RATIONAL || type == TYPE_UNDEFINED || type == TYPE_LONG || type == TYPE_RATIONAL; } - ExifTag(short tagId, short type, int componentCount, int ifd) { + // Use builtTag in ExifInterface instead of constructor. + ExifTag(short tagId, short type, int componentCount, int ifd, + boolean hasDefinedComponentCount) { mTagId = tagId; mDataType = type; - mComponentCount = componentCount; - mComponentCountDefined = getComponentCountDefined(tagId, ifd); + mComponentCountActual = componentCount; + mHasDefinedDefaultComponentCount = hasDefinedComponentCount; mIfd = ifd; + mValue = null; + } + + /** + * Gets the element size of the given data type in bytes. + * + * @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]; } /** @@ -941,8 +163,12 @@ public class ExifTag { return mIfd; } + protected void setIfd(int ifdId) { + mIfd = ifdId; + } + /** - * Gets the ID of this tag. + * Gets the TID of this tag. */ public short getTagId() { return mTagId; @@ -974,349 +200,589 @@ public class ExifTag { /** * Gets the component count of this tag. */ + + // TODO: fix integer overflows with this public int getComponentCount() { - return mComponentCount; + return mComponentCountActual; } /** - * Sets the component count of this tag. - * Call this function before setValue() if the length of value does not - * match the component count. + * Sets the component count of this tag. Call this function before + * setValue() if the length of value does not match the component count. */ - public void setComponentCount(int count) { - mComponentCount = count; + protected void forceSetComponentCount(int count) { + mComponentCountActual = count; } /** - * 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() + * Returns true if this ExifTag contains value; otherwise, this tag will + * contain an offset value that is determined when the tag is written. */ 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. + * Sets integer values into this tag. This method should be used for tags of + * type {@link #TYPE_UNSIGNED_SHORT}. This method will fail if: + * <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 component count in the definition + * for this tag.</li> + * </ul> */ - public int getOffset() { - return mOffset; + public boolean setValue(int[] value) { + if (checkBadComponentCount(value.length)) { + return false; + } + if (mDataType != TYPE_UNSIGNED_SHORT && mDataType != TYPE_LONG && + mDataType != TYPE_UNSIGNED_LONG) { + return false; + } + if (mDataType == TYPE_UNSIGNED_SHORT && checkOverflowForUnsignedShort(value)) { + return false; + } else if (mDataType == TYPE_UNSIGNED_LONG && checkOverflowForUnsignedLong(value)) { + return false; + } + + long[] data = new long[value.length]; + System.arraycopy(value, 0, data, 0, value.length); + mValue = data; + mComponentCountActual = value.length; + return true; } /** - * Sets the offset of this tag. + * Sets integer value into this tag. This method should be used for tags of + * type {@link #TYPE_UNSIGNED_SHORT}, or {@link #TYPE_LONG}. This method + * will fail if: + * <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 this tag is not 1.</li> + * </ul> */ - void setOffset(int offset) { - mOffset = offset; + public boolean setValue(int value) { + return setValue(new int[] { + value + }); } - 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)"); + /** + * Sets long values into this tag. This method should be used for tags of + * type {@link #TYPE_UNSIGNED_LONG}. This method will fail if: + * <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 component count in the definition + * for this tag.</li> + * </ul> + */ + public boolean setValue(long[] value) { + if (checkBadComponentCount(value.length) || mDataType != TYPE_UNSIGNED_LONG) { + return false; + } + if (checkOverflowForUnsignedLong(value)) { + return false; } + mValue = value; + mComponentCountActual = value.length; + return true; } - private void throwTypeNotMatchedException(String className) - throws IllegalArgumentException { - throw new IllegalArgumentException("Tag " + mTagId + ": expect type " + - convertTypeToString(mDataType) + " but got " + className); + /** + * Sets long values into this tag. This method should be used for tags of + * type {@link #TYPE_UNSIGNED_LONG}. This method will fail if: + * <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 for this tag is not 1.</li> + * </ul> + */ + public boolean setValue(long value) { + return setValue(new long[] { + value + }); } - 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 ""; + /** + * Sets a string value into this tag. This method should be used for tags of + * type {@link #TYPE_ASCII}. The string is converted to an ASCII string. + * Characters that cannot be converted are replaced with '?'. The length of + * the string must be equal to either (component count -1) or (component + * count). The final byte will be set to the string null terminator '\0', + * overwriting the last character in the string if the value.length is equal + * to the component count. This method will fail if: + * <ul> + * <li>The data type is not {@link #TYPE_ASCII} or {@link #TYPE_UNDEFINED}.</li> + * <li>The length of the string is not equal to (component count -1) or + * (component count) in the definition for this tag.</li> + * </ul> + */ + public boolean setValue(String value) { + if (mDataType != TYPE_ASCII && mDataType != TYPE_UNDEFINED) { + return false; } - } - 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"); - } + byte[] buf = value.getBytes(US_ASCII); + byte[] finalBuf = (buf[buf.length - 1] == 0 || mDataType == TYPE_UNDEFINED) ? buf : Arrays + .copyOf(buf, buf.length + 1); + int count = finalBuf.length; + if (checkBadComponentCount(count)) { + return false; } + mComponentCountActual = count; + mValue = finalBuf; + return true; } - 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"); - } + /** + * Sets Rational values into this tag. This method should be used for tags + * of type {@link #TYPE_UNSIGNED_RATIONAL}, or {@link #TYPE_RATIONAL}. This + * method will fail if: + * <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 component count in the definition + * for this tag.</li> + * </ul> + * + * @see Rational + */ + public boolean setValue(Rational[] value) { + if (checkBadComponentCount(value.length)) { + return false; } - } - - 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"); - } + if (mDataType != TYPE_UNSIGNED_RATIONAL && mDataType != TYPE_RATIONAL) { + return false; } - } - - 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"); - } + if (mDataType == TYPE_UNSIGNED_RATIONAL && checkOverflowForUnsignedRational(value)) { + return false; + } else if (mDataType == TYPE_RATIONAL && checkOverflowForRational(value)) { + return false; } + + mValue = value; + mComponentCountActual = value.length; + return true; } - 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 a Rational value into this tag. This method should be used for tags + * of type {@link #TYPE_UNSIGNED_RATIONAL}, or {@link #TYPE_RATIONAL}. This + * method will fail if: + * <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 for this tag is not 1.</li> + * </ul> + * + * @see Rational + */ + public boolean setValue(Rational value) { + return setValue(new Rational[] { + value + }); } /** - * Sets integer values into this tag. - * @exception IllegalArgumentException for the following situation: + * Sets byte values into this tag. This method should be used for tags of + * type {@link #TYPE_UNSIGNED_BYTE} or {@link #TYPE_UNDEFINED}. This method + * will fail if: * <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> + * <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 component count in the definition for + * this tag.</li> * </ul> */ - public void setValue(int[] value) { - checkComponentCountOrThrow(value.length); - if (mDataType != TYPE_UNSIGNED_SHORT && mDataType != TYPE_LONG && - mDataType != TYPE_UNSIGNED_LONG) { - throwTypeNotMatchedException("int"); + public boolean setValue(byte[] value, int offset, int length) { + if (checkBadComponentCount(length)) { + return false; } - if (mDataType == TYPE_UNSIGNED_SHORT) { - checkOverflowForUnsignedShort(value); - } else if (mDataType == TYPE_UNSIGNED_LONG) { - checkOverflowForUnsignedLong(value); + if (mDataType != TYPE_UNSIGNED_BYTE && mDataType != TYPE_UNDEFINED) { + return false; } + mValue = new byte[length]; + System.arraycopy(value, offset, mValue, 0, length); + mComponentCountActual = length; + return true; + } - long[] data = new long[value.length]; - for (int i = 0; i < value.length; i++) { - data[i] = value[i]; - } - mValue = data; - mComponentCount = value.length; + /** + * Equivalent to setValue(value, 0, value.length). + */ + public boolean setValue(byte[] value) { + return setValue(value, 0, value.length); } /** - * Sets integer values into this tag. - * @exception IllegalArgumentException For the following situation: + * Sets byte value into this tag. This method should be used for tags of + * type {@link #TYPE_UNSIGNED_BYTE} or {@link #TYPE_UNDEFINED}. This method + * will fail if: * <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> + * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_BYTE} or + * {@link #TYPE_UNDEFINED} .</li> + * <li>The component count in the definition for this tag is not 1.</li> * </ul> */ - public void setValue(int value) { - checkComponentCountOrThrow(1); - setValue(new int[] {value}); + public boolean setValue(byte value) { + return setValue(new byte[] { + value + }); } /** - * Sets long values into this tag. - * @exception IllegalArgumentException For the following situation: + * Sets the value for this tag using an appropriate setValue method for the + * given object. This method will fail if: * <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> + * <li>The corresponding setValue method for the class of the object passed + * in would fail.</li> + * <li>There is no obvious way to cast the object passed in into an EXIF tag + * type.</li> * </ul> */ - public void setValue(long[] value) { - checkComponentCountOrThrow(value.length); - if (mDataType != TYPE_UNSIGNED_LONG) { - throwTypeNotMatchedException("long"); + public boolean setValue(Object obj) { + if (obj == null) { + return false; + } else if (obj instanceof Short) { + return setValue(((Short) obj).shortValue() & 0x0ffff); + } else if (obj instanceof String) { + return setValue((String) obj); + } else if (obj instanceof int[]) { + return setValue((int[]) obj); + } else if (obj instanceof long[]) { + return setValue((long[]) obj); + } else if (obj instanceof Rational) { + return setValue((Rational) obj); + } else if (obj instanceof Rational[]) { + return setValue((Rational[]) obj); + } else if (obj instanceof byte[]) { + return setValue((byte[]) obj); + } else if (obj instanceof Integer) { + return setValue(((Integer) obj).intValue()); + } else if (obj instanceof Long) { + return setValue(((Long) obj).longValue()); + } else if (obj instanceof Byte) { + return setValue(((Byte) obj).byteValue()); + } else if (obj instanceof Short[]) { + // Nulls in this array are treated as zeroes. + Short[] arr = (Short[]) obj; + int[] fin = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + fin[i] = (arr[i] == null) ? 0 : arr[i].shortValue() & 0x0ffff; + } + return setValue(fin); + } else if (obj instanceof Integer[]) { + // Nulls in this array are treated as zeroes. + Integer[] arr = (Integer[]) obj; + int[] fin = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + fin[i] = (arr[i] == null) ? 0 : arr[i].intValue(); + } + return setValue(fin); + } else if (obj instanceof Long[]) { + // Nulls in this array are treated as zeroes. + Long[] arr = (Long[]) obj; + long[] fin = new long[arr.length]; + for (int i = 0; i < arr.length; i++) { + fin[i] = (arr[i] == null) ? 0 : arr[i].longValue(); + } + return setValue(fin); + } else if (obj instanceof Byte[]) { + // Nulls in this array are treated as zeroes. + Byte[] arr = (Byte[]) obj; + byte[] fin = new byte[arr.length]; + for (int i = 0; i < arr.length; i++) { + fin[i] = (arr[i] == null) ? 0 : arr[i].byteValue(); + } + return setValue(fin); + } else { + return false; } - 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> + * 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)}. This + * method will fail if the data type is not {@link #TYPE_ASCII} or the + * component count of this tag is not 20 or undefined. + * + * @param time the number of milliseconds since Jan. 1, 1970 GMT + * @return true on success */ - public void setValue(long value) { - setValue(new long[] {value}); + public boolean setTimeValue(long time) { + // synchronized on TIME_FORMAT as SimpleDateFormat is not thread safe + synchronized (TIME_FORMAT) { + return setValue(TIME_FORMAT.format(new Date(time))); + } } /** - * Sets a string value into this tag. The value is treated as an ASCII string where we only - * preserve the lower byte of each character. The length of the string should be equal - * to either (component count -1) or (component count). A "0" byte will be appeneded while - * written to the EXIF file. If the length equals (component count), the final byte will be - * replaced by a "0" byte. + * Gets the value as a String. This method should be used for tags of type + * {@link #TYPE_ASCII}. * - * @exception IllegalArgumentException If the data type is not {@link #TYPE_ASCII} - * or the length of the string is not equal to (component count -1) and (component count) - */ - public void setValue(String value) { - if (mDataType != TYPE_ASCII) { - throwTypeNotMatchedException("String"); + * @return the value as a String, or null if the tag's value does not exist + * or cannot be converted to a String. + */ + public String getValueAsString() { + if (mValue == null) { + return null; + } else if (mValue instanceof String) { + return (String) mValue; + } else if (mValue instanceof byte[]) { + return new String((byte[]) mValue, US_ASCII); } + return null; + } - byte[] buf = new byte[value.length()]; - for (int i = 0, n = value.length(); i < n; i++) { - buf[i] = (byte) value.charAt(i); + /** + * Gets the value as a String. This method should be used for tags of type + * {@link #TYPE_ASCII}. + * + * @param defaultValue the String to return if the tag's value does not + * exist or cannot be converted to a String. + * @return the tag's value as a String, or the defaultValue. + */ + public String getValueAsString(String defaultValue) { + String s = getValueAsString(); + if (s == null) { + return defaultValue; } + return s; + } - int count = buf.length; - if (mComponentCountDefined) { - if (mComponentCount != count && mComponentCount != count + 1) { - throw new IllegalArgumentException("Tag " + mTagId + ": Required " - + mComponentCount + " or " + (mComponentCount + 1) - + " components but was given " + count - + " component(s)"); - } - } else { - mComponentCount = buf[count - 1] == 0 ? count : count + 1; + /** + * Gets the value as a byte array. This method should be used for tags of + * type {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}. + * + * @return the value as a byte array, or null if the tag's value does not + * exist or cannot be converted to a byte array. + */ + public byte[] getValueAsBytes() { + if (mValue instanceof byte[]) { + return (byte[]) mValue; } - mValue = buf; + return null; } /** - * 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> + * Gets the value as a byte. If there are more than 1 bytes in this value, + * gets the first byte. This method should be used for tags of type + * {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}. + * + * @param defaultValue the byte to return if tag's value does not exist or + * cannot be converted to a byte. + * @return the tag's value as a byte, or the defaultValue. + */ + public byte getValueAsByte(byte defaultValue) { + byte[] b = getValueAsBytes(); + if (b == null || b.length < 1) { + return defaultValue; + } + return b[0]; + } + + /** + * Gets the value as an array of Rationals. This method should be used for + * tags of type {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}. + * + * @return the value as as an array of Rationals, or null if the tag's value + * does not exist or cannot be converted to an array of Rationals. */ - public void setValue(Rational[] value) { - if (mDataType == TYPE_UNSIGNED_RATIONAL) { - checkOverflowForUnsignedRational(value); - } else if (mDataType == TYPE_RATIONAL) { - checkOverflowForRational(value); - } else { - throwTypeNotMatchedException("Rational"); + public Rational[] getValueAsRationals() { + if (mValue instanceof Rational[]) { + return (Rational[]) mValue; } - checkComponentCountOrThrow(value.length); - mValue = value; - mComponentCount = value.length; + return null; } /** - * 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}); + * Gets the value as a Rational. If there are more than 1 Rationals in this + * value, gets the first one. This method should be used for tags of type + * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}. + * + * @param defaultValue the Rational to return if tag's value does not exist + * or cannot be converted to a Rational. + * @return the tag's value as a Rational, or the defaultValue. + */ + public Rational getValueAsRational(Rational defaultValue) { + Rational[] r = getValueAsRationals(); + if (r == null || r.length < 1) { + return defaultValue; + } + return r[0]; } /** - * 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"); + * Gets the value as a Rational. If there are more than 1 Rationals in this + * value, gets the first one. This method should be used for tags of type + * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}. + * + * @param defaultValue the numerator of the Rational to return if tag's + * value does not exist or cannot be converted to a Rational (the + * denominator will be 1). + * @return the tag's value as a Rational, or the defaultValue. + */ + public Rational getValueAsRational(long defaultValue) { + Rational defaultVal = new Rational(defaultValue, 1); + return getValueAsRational(defaultVal); + } + + /** + * Gets the value as an array of ints. This method should be used for tags + * of type {@link #TYPE_UNSIGNED_SHORT}, {@link #TYPE_UNSIGNED_LONG}. + * + * @return the value as as an array of ints, or null if the tag's value does + * not exist or cannot be converted to an array of ints. + */ + public int[] getValueAsInts() { + if (mValue == null) { + return null; + } else if (mValue instanceof long[]) { + long[] val = (long[]) mValue; + int[] arr = new int[val.length]; + for (int i = 0; i < val.length; i++) { + arr[i] = (int) val[i]; // Truncates + } + return arr; } - mValue = new byte[length]; - System.arraycopy(value, offset, mValue, 0, length); - mComponentCount = length; + return null; } /** - * Equivalent to setValue(value, 0, value.length). + * Gets the value as an int. If there are more than 1 ints in this value, + * gets the first one. This method should be used for tags of type + * {@link #TYPE_UNSIGNED_SHORT}, {@link #TYPE_UNSIGNED_LONG}. + * + * @param defaultValue the int to return if tag's value does not exist or + * cannot be converted to an int. + * @return the tag's value as a int, or the defaultValue. + */ + public int getValueAsInt(int defaultValue) { + int[] i = getValueAsInts(); + if (i == null || i.length < 1) { + return defaultValue; + } + return i[0]; + } + + /** + * Gets the value as an array of longs. This method should be used for tags + * of type {@link #TYPE_UNSIGNED_LONG}. + * + * @return the value as as an array of longs, or null if the tag's value + * does not exist or cannot be converted to an array of longs. */ - public void setValue(byte[] value) { - setValue(value, 0, value.length); + public long[] getValueAsLongs() { + if (mValue instanceof long[]) { + return (long[]) mValue; + } + return null; } - private static final SimpleDateFormat TIME_FORMAT = - new SimpleDateFormat("yyyy:MM:dd kk:mm:ss"); + /** + * Gets the value or null if none exists. If there are more than 1 longs in + * this value, gets the first one. This method should be used for tags of + * type {@link #TYPE_UNSIGNED_LONG}. + * + * @param defaultValue the long to return if tag's value does not exist or + * cannot be converted to a long. + * @return the tag's value as a long, or the defaultValue. + */ + public long getValueAsLong(long defaultValue) { + long[] l = getValueAsLongs(); + if (l == null || l.length < 1) { + return defaultValue; + } + return l[0]; + } /** - * 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)}. + * Gets the tag's value or null if none exists. + */ + public Object getValue() { + return mValue; + } + + /** + * Gets a long representation of the value. * - * @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 + * @param defaultValue value to return if there is no value or value is a + * rational with a denominator of 0. + * @return the tag's value as a long, or defaultValue if no representation + * exists. + */ + public long forceGetValueAsLong(long defaultValue) { + long[] l = getValueAsLongs(); + if (l != null && l.length >= 1) { + return l[0]; + } + byte[] b = getValueAsBytes(); + if (b != null && b.length >= 1) { + return b[0]; + } + Rational[] r = getValueAsRationals(); + if (r != null && r.length >= 1 && r[0].getDenominator() != 0) { + return (long) r[0].toDouble(); + } + return defaultValue; + } + + /** + * Gets a string representation of the value. */ - 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))); + public String forceGetValueAsString() { + if (mValue == null) { + return ""; + } else if (mValue instanceof byte[]) { + if (mDataType == TYPE_ASCII) { + return new String((byte[]) mValue, US_ASCII); + } else { + return Arrays.toString((byte[]) mValue); + } + } else if (mValue instanceof long[]) { + if (((long[]) mValue).length == 1) { + return String.valueOf(((long[]) mValue)[0]); + } else { + return Arrays.toString((long[]) mValue); + } + } else if (mValue instanceof Object[]) { + if (((Object[]) mValue).length == 1) { + Object val = ((Object[]) mValue)[0]; + if (val == null) { + return ""; + } else { + return val.toString(); + } + } else { + return Arrays.toString((Object[]) mValue); + } + } else { + return mValue.toString(); } } + /** * Gets the value for type {@link #TYPE_ASCII}, {@link #TYPE_LONG}, - * {@link #TYPE_UNDEFINED}, {@link #TYPE_UNSIGNED_BYTE}, {@link #TYPE_UNSIGNED_LONG}, or - * {@link #TYPE_UNSIGNED_SHORT}. For {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}, - * call {@link #getRational(int)} instead. + * {@link #TYPE_UNDEFINED}, {@link #TYPE_UNSIGNED_BYTE}, + * {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_UNSIGNED_SHORT}. For + * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}, call + * {@link #getRational(int)} instead. * - * @exception IllegalArgumentException if the data type is {@link #TYPE_RATIONAL} or - * {@link #TYPE_UNSIGNED_RATIONAL}. + * @exception IllegalArgumentException if the data type is + * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}. */ - public long getValueAt(int index) { + protected long getValueAt(int index) { if (mValue instanceof long[]) { - return ((long[]) mValue) [index]; + return ((long[]) mValue)[index]; } else if (mValue instanceof byte[]) { - return ((byte[]) mValue) [index]; + return ((byte[]) mValue)[index]; } throw new IllegalArgumentException("Cannot get integer value from " + convertTypeToString(mDataType)); @@ -1324,9 +790,11 @@ public class ExifTag { /** * Gets the {@link #TYPE_ASCII} data. - * @exception IllegalArgumentException If the type is NOT {@link #TYPE_ASCII}. + * + * @exception IllegalArgumentException If the type is NOT + * {@link #TYPE_ASCII}. */ - public String getString() { + protected String getString() { if (mDataType != TYPE_ASCII) { throw new IllegalArgumentException("Cannot get ASCII value from " + convertTypeToString(mDataType)); @@ -1337,27 +805,28 @@ public class ExifTag { /* * Get the converted ascii byte. Used by ExifOutputStream. */ - byte[] getStringByte() { + protected byte[] getStringByte() { return (byte[]) 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}. + * + * @exception IllegalArgumentException If the type is NOT + * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}. */ - public Rational getRational(int index) { + protected 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]; + return ((Rational[]) mValue)[index]; } /** * Equivalent to getBytes(buffer, 0, buffer.length). */ - public void getBytes(byte[] buf) { + protected void getBytes(byte[] buf) { getBytes(buf, 0, buf.length); } @@ -1366,56 +835,147 @@ public class ExifTag { * * @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}. + * @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) { + protected 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); + (length > mComponentCountActual) ? mComponentCountActual : length); } /** - * 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} + * 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. */ - 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; + protected int getOffset() { + return mOffset; } /** - * Returns true if the ID is one of the following: {@link #TAG_EXIF_IFD}, - * {@link #TAG_GPS_IFD}, {@link #TAG_INTEROPERABILITY_IFD} + * Sets the offset of this tag. */ - static boolean isSubIfdOffsetTag(short tagId) { - return tagId == TAG_EXIF_IFD - || tagId == TAG_GPS_IFD - || tagId == TAG_INTEROPERABILITY_IFD; + protected void setOffset(int offset) { + mOffset = offset; + } + + protected void setHasDefinedCount(boolean d) { + mHasDefinedDefaultComponentCount = d; + } + + private boolean checkBadComponentCount(int count) { + if (mHasDefinedDefaultComponentCount && (mComponentCountActual != count)) { + return true; + } + return false; + } + + 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 boolean checkOverflowForUnsignedShort(int[] value) { + for (int v : value) { + if (v > UNSIGNED_SHORT_MAX || v < 0) { + return true; + } + } + return false; } + + private boolean checkOverflowForUnsignedLong(long[] value) { + for (long v : value) { + if (v < 0 || v > UNSIGNED_LONG_MAX) { + return true; + } + } + return false; + } + + private boolean checkOverflowForUnsignedLong(int[] value) { + for (int v : value) { + if (v < 0) { + return true; + } + } + return false; + } + + private boolean checkOverflowForUnsignedRational(Rational[] value) { + for (Rational v : value) { + if (v.getNumerator() < 0 || v.getDenominator() < 0 + || v.getNumerator() > UNSIGNED_LONG_MAX + || v.getDenominator() > UNSIGNED_LONG_MAX) { + return true; + } + } + return false; + } + + private boolean checkOverflowForRational(Rational[] value) { + for (Rational v : value) { + if (v.getNumerator() < LONG_MIN || v.getDenominator() < LONG_MIN + || v.getNumerator() > LONG_MAX + || v.getDenominator() > LONG_MAX) { + return true; + } + } + return false; + } + @Override public boolean equals(Object obj) { + if (obj == null) { + return false; + } if (obj instanceof ExifTag) { ExifTag tag = (ExifTag) obj; + if (tag.mTagId != this.mTagId + || tag.mComponentCountActual != this.mComponentCountActual + || tag.mDataType != this.mDataType) { + return false; + } if (mValue != null) { - if (mValue instanceof long[]) { - if (!(tag.mValue instanceof long[])) return false; + if (tag.mValue == null) { + return false; + } else 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; + 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; + if (!(tag.mValue instanceof byte[])) { + return false; + } return Arrays.equals((byte[]) mValue, (byte[]) tag.mValue); } else { return mValue.equals(tag.mValue); @@ -1426,4 +986,12 @@ public class ExifTag { } return false; } + + @Override + public String toString() { + return String.format("tag id: %04X\n", mTagId) + "ifd id: " + mIfd + "\ntype: " + + convertTypeToString(mDataType) + "\ncount: " + mComponentCountActual + + "\noffset: " + mOffset + "\nvalue: " + forceGetValueAsString() + "\n"; + } + } |