/* * 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.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 */ 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. *

* e.g. *

* 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 sTagInfo = null; private static volatile SparseArray sInteroperTagInfo = null; private static final int SIZE_UNDEFINED = 0; private static SparseArray getTagInfo() { if (sTagInfo == null) { synchronized(ExifTag.class) { if (sTagInfo == null) { sTagInfo = new SparseArray(); initTagInfo(); } } } return sTagInfo; } private static SparseArray getInteroperTagInfo() { if (sInteroperTagInfo == null) { synchronized(ExifTag.class) { if (sInteroperTagInfo == null) { sInteroperTagInfo = new SparseArray(); 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); } private static Charset US_ASCII = Charset.forName("US-ASCII"); 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); } 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) { 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; } /** * 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; } /** * 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: *

*/ 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: * */ public void setValue(int value) { checkComponentCountOrThrow(1); setValue(new int[] {value}); } /** * Sets long values into this tag. * @exception IllegalArgumentException For the following situation: * */ 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: * */ public void setValue(long value) { setValue(new long[] {value}); } /** * 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. * * @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"); } byte[] buf = new byte[value.length()]; for (int i = 0, n = value.length(); i < n; i++) { buf[i] = (byte) value.charAt(i); } 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; } mValue = buf; } /** * Sets Rational values into this tag. * @exception IllegalArgumentException For the following situation: * */ 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: * * */ public void setValue(Rational value) { setValue(new Rational[] {value}); } /** * Sets byte values into this tag. * @exception IllegalArgumentException For the following situation: * * */ 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 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. * * @exception IllegalArgumentException if the data type is {@link #TYPE_RATIONAL} or * {@link #TYPE_UNSIGNED_RATIONAL}. */ public long getValueAt(int index) { if (mValue instanceof long[]) { return ((long[]) mValue) [index]; } else if (mValue instanceof byte[]) { return ((byte[]) mValue) [index]; } throw new IllegalArgumentException("Cannot get integer value from " + convertTypeToString(mDataType)); } /** * 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 new String((byte[]) mValue, US_ASCII); } /* * Get the converted ascii byte. Used by ExifOutputStream. */ 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}. */ 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 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; } /** * Returns true if the ID is one of the following: {@link #TAG_EXIF_IFD}, * {@link #TAG_GPS_IFD}, {@link #TAG_INTEROPERABILITY_IFD} */ static boolean isSubIfdOffsetTag(short tagId) { return tagId == TAG_EXIF_IFD || tagId == TAG_GPS_IFD || 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; } }