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