| /* |
| * Copyright (C) 2013 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.camera.exif; |
| |
| import android.graphics.Bitmap; |
| import android.graphics.BitmapFactory; |
| import android.util.SparseIntArray; |
| import com.android.camera.debug.Log; |
| |
| import java.io.BufferedInputStream; |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.Closeable; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.RandomAccessFile; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.nio.channels.FileChannel.MapMode; |
| import java.text.DateFormat; |
| import java.text.SimpleDateFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Calendar; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.TimeZone; |
| |
| /** |
| * This class provides methods and constants for reading and writing jpeg file |
| * metadata. It contains a collection of ExifTags, and a collection of |
| * definitions for creating valid ExifTags. The collection of ExifTags can be |
| * updated by: reading new ones from a file, deleting or adding existing ones, |
| * or building new ExifTags from a tag definition. These ExifTags can be written |
| * to a valid jpeg image as exif metadata. |
| * <p> |
| * Each ExifTag has a tag ID (TID) and is stored in a specific image file |
| * directory (IFD) as specified by the exif standard. A tag definition can be |
| * looked up with a constant that is a combination of TID and IFD. This |
| * definition has information about the type, number of components, and valid |
| * IFDs for a tag. |
| * |
| * @see ExifTag |
| */ |
| public class ExifInterface { |
| public static final int TAG_NULL = -1; |
| public static final int IFD_NULL = -1; |
| public static final int DEFINITION_NULL = 0; |
| |
| /** |
| * Tag constants for Jeita EXIF 2.2 |
| */ |
| public static final String EXIF_VERSION = "0220"; |
| |
| // IFD 0 |
| public static final int TAG_IMAGE_WIDTH = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0100); |
| public static final int TAG_IMAGE_LENGTH = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0101); // Image height |
| public static final int TAG_BITS_PER_SAMPLE = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0102); |
| public static final int TAG_COMPRESSION = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0103); |
| public static final int TAG_PHOTOMETRIC_INTERPRETATION = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0106); |
| public static final int TAG_IMAGE_DESCRIPTION = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x010E); |
| public static final int TAG_MAKE = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x010F); |
| public static final int TAG_MODEL = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0110); |
| public static final int TAG_STRIP_OFFSETS = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0111); |
| public static final int TAG_ORIENTATION = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0112); |
| public static final int TAG_SAMPLES_PER_PIXEL = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0115); |
| public static final int TAG_ROWS_PER_STRIP = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0116); |
| public static final int TAG_STRIP_BYTE_COUNTS = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0117); |
| public static final int TAG_X_RESOLUTION = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x011A); |
| public static final int TAG_Y_RESOLUTION = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x011B); |
| public static final int TAG_PLANAR_CONFIGURATION = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x011C); |
| public static final int TAG_RESOLUTION_UNIT = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0128); |
| public static final int TAG_TRANSFER_FUNCTION = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x012D); |
| public static final int TAG_SOFTWARE = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0131); |
| public static final int TAG_DATE_TIME = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0132); |
| public static final int TAG_ARTIST = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x013B); |
| public static final int TAG_WHITE_POINT = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x013E); |
| public static final int TAG_PRIMARY_CHROMATICITIES = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x013F); |
| public static final int TAG_Y_CB_CR_COEFFICIENTS = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0211); |
| public static final int TAG_Y_CB_CR_SUB_SAMPLING = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0212); |
| public static final int TAG_Y_CB_CR_POSITIONING = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0213); |
| public static final int TAG_REFERENCE_BLACK_WHITE = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x0214); |
| public static final int TAG_COPYRIGHT = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x8298); |
| public static final int TAG_EXIF_IFD = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x8769); |
| public static final int TAG_GPS_IFD = |
| defineTag(IfdId.TYPE_IFD_0, (short) 0x8825); |
| // IFD 1 |
| public static final int TAG_JPEG_INTERCHANGE_FORMAT = |
| defineTag(IfdId.TYPE_IFD_1, (short) 0x0201); |
| public static final int TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = |
| defineTag(IfdId.TYPE_IFD_1, (short) 0x0202); |
| // IFD Exif Tags |
| public static final int TAG_EXPOSURE_TIME = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x829A); |
| public static final int TAG_F_NUMBER = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x829D); |
| public static final int TAG_EXPOSURE_PROGRAM = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8822); |
| public static final int TAG_SPECTRAL_SENSITIVITY = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8824); |
| public static final int TAG_ISO_SPEED_RATINGS = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8827); |
| public static final int TAG_OECF = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8828); |
| public static final int TAG_EXIF_VERSION = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9000); |
| public static final int TAG_DATE_TIME_ORIGINAL = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9003); |
| public static final int TAG_DATE_TIME_DIGITIZED = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9004); |
| public static final int TAG_COMPONENTS_CONFIGURATION = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9101); |
| public static final int TAG_COMPRESSED_BITS_PER_PIXEL = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9102); |
| public static final int TAG_SHUTTER_SPEED_VALUE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9201); |
| public static final int TAG_APERTURE_VALUE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9202); |
| public static final int TAG_BRIGHTNESS_VALUE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9203); |
| public static final int TAG_EXPOSURE_BIAS_VALUE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9204); |
| public static final int TAG_MAX_APERTURE_VALUE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9205); |
| public static final int TAG_SUBJECT_DISTANCE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9206); |
| public static final int TAG_METERING_MODE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9207); |
| public static final int TAG_LIGHT_SOURCE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9208); |
| public static final int TAG_FLASH = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9209); |
| public static final int TAG_FOCAL_LENGTH = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x920A); |
| public static final int TAG_SUBJECT_AREA = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9214); |
| public static final int TAG_MAKER_NOTE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x927C); |
| public static final int TAG_USER_COMMENT = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9286); |
| public static final int TAG_SUB_SEC_TIME = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9290); |
| public static final int TAG_SUB_SEC_TIME_ORIGINAL = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9291); |
| public static final int TAG_SUB_SEC_TIME_DIGITIZED = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9292); |
| public static final int TAG_FLASHPIX_VERSION = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA000); |
| public static final int TAG_COLOR_SPACE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA001); |
| public static final int TAG_PIXEL_X_DIMENSION = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA002); |
| public static final int TAG_PIXEL_Y_DIMENSION = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA003); |
| public static final int TAG_RELATED_SOUND_FILE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA004); |
| public static final int TAG_INTEROPERABILITY_IFD = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA005); |
| public static final int TAG_FLASH_ENERGY = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20B); |
| public static final int TAG_SPATIAL_FREQUENCY_RESPONSE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20C); |
| public static final int TAG_FOCAL_PLANE_X_RESOLUTION = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20E); |
| public static final int TAG_FOCAL_PLANE_Y_RESOLUTION = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20F); |
| public static final int TAG_FOCAL_PLANE_RESOLUTION_UNIT = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA210); |
| public static final int TAG_SUBJECT_LOCATION = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA214); |
| public static final int TAG_EXPOSURE_INDEX = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA215); |
| public static final int TAG_SENSING_METHOD = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA217); |
| public static final int TAG_FILE_SOURCE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA300); |
| public static final int TAG_SCENE_TYPE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA301); |
| public static final int TAG_CFA_PATTERN = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA302); |
| public static final int TAG_CUSTOM_RENDERED = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA401); |
| public static final int TAG_EXPOSURE_MODE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA402); |
| public static final int TAG_WHITE_BALANCE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA403); |
| public static final int TAG_DIGITAL_ZOOM_RATIO = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA404); |
| public static final int TAG_FOCAL_LENGTH_IN_35_MM_FILE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA405); |
| public static final int TAG_SCENE_CAPTURE_TYPE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA406); |
| public static final int TAG_GAIN_CONTROL = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA407); |
| public static final int TAG_CONTRAST = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA408); |
| public static final int TAG_SATURATION = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA409); |
| public static final int TAG_SHARPNESS = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA40A); |
| public static final int TAG_DEVICE_SETTING_DESCRIPTION = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA40B); |
| public static final int TAG_SUBJECT_DISTANCE_RANGE = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA40C); |
| public static final int TAG_IMAGE_UNIQUE_ID = |
| defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA420); |
| // IFD GPS tags |
| public static final int TAG_GPS_VERSION_ID = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 0); |
| public static final int TAG_GPS_LATITUDE_REF = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 1); |
| public static final int TAG_GPS_LATITUDE = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 2); |
| public static final int TAG_GPS_LONGITUDE_REF = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 3); |
| public static final int TAG_GPS_LONGITUDE = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 4); |
| public static final int TAG_GPS_ALTITUDE_REF = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 5); |
| public static final int TAG_GPS_ALTITUDE = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 6); |
| public static final int TAG_GPS_TIME_STAMP = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 7); |
| public static final int TAG_GPS_SATTELLITES = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 8); |
| public static final int TAG_GPS_STATUS = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 9); |
| public static final int TAG_GPS_MEASURE_MODE = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 10); |
| public static final int TAG_GPS_DOP = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 11); |
| public static final int TAG_GPS_SPEED_REF = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 12); |
| public static final int TAG_GPS_SPEED = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 13); |
| public static final int TAG_GPS_TRACK_REF = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 14); |
| public static final int TAG_GPS_TRACK = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 15); |
| public static final int TAG_GPS_IMG_DIRECTION_REF = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 16); |
| public static final int TAG_GPS_IMG_DIRECTION = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 17); |
| public static final int TAG_GPS_MAP_DATUM = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 18); |
| public static final int TAG_GPS_DEST_LATITUDE_REF = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 19); |
| public static final int TAG_GPS_DEST_LATITUDE = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 20); |
| public static final int TAG_GPS_DEST_LONGITUDE_REF = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 21); |
| public static final int TAG_GPS_DEST_LONGITUDE = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 22); |
| public static final int TAG_GPS_DEST_BEARING_REF = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 23); |
| public static final int TAG_GPS_DEST_BEARING = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 24); |
| public static final int TAG_GPS_DEST_DISTANCE_REF = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 25); |
| public static final int TAG_GPS_DEST_DISTANCE = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 26); |
| public static final int TAG_GPS_PROCESSING_METHOD = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 27); |
| public static final int TAG_GPS_AREA_INFORMATION = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 28); |
| public static final int TAG_GPS_DATE_STAMP = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 29); |
| public static final int TAG_GPS_DIFFERENTIAL = |
| defineTag(IfdId.TYPE_IFD_GPS, (short) 30); |
| // IFD Interoperability tags |
| public static final int TAG_INTEROPERABILITY_INDEX = |
| defineTag(IfdId.TYPE_IFD_INTEROPERABILITY, (short) 1); |
| private static final Log.Tag TAG = new Log.Tag("ExifInterface"); |
| |
| /** |
| * Tags that contain offset markers. These are included in the banned |
| * defines. |
| */ |
| private static HashSet<Short> sOffsetTags = new HashSet<Short>(); |
| static { |
| sOffsetTags.add(getTrueTagKey(TAG_GPS_IFD)); |
| sOffsetTags.add(getTrueTagKey(TAG_EXIF_IFD)); |
| sOffsetTags.add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT)); |
| sOffsetTags.add(getTrueTagKey(TAG_INTEROPERABILITY_IFD)); |
| sOffsetTags.add(getTrueTagKey(TAG_STRIP_OFFSETS)); |
| } |
| |
| /** |
| * Tags with definitions that cannot be overridden (banned defines). |
| */ |
| protected static HashSet<Short> sBannedDefines = new HashSet<Short>(sOffsetTags); |
| static { |
| sBannedDefines.add(getTrueTagKey(TAG_NULL)); |
| sBannedDefines.add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)); |
| sBannedDefines.add(getTrueTagKey(TAG_STRIP_BYTE_COUNTS)); |
| } |
| |
| /** |
| * Returns the constant representing a tag with a given TID and default IFD. |
| */ |
| public static int defineTag(int ifdId, short tagId) { |
| return (tagId & 0x0000ffff) | (ifdId << 16); |
| } |
| |
| /** |
| * Returns the TID for a tag constant. |
| */ |
| public static short getTrueTagKey(int tag) { |
| // Truncate |
| return (short) tag; |
| } |
| |
| /** |
| * Returns the default IFD for a tag constant. |
| */ |
| public static int getTrueIfd(int tag) { |
| return tag >>> 16; |
| } |
| |
| /** |
| * Constants for {@link TAG_ORIENTATION}. They can be interpreted as |
| * follows: |
| * <ul> |
| * <li>TOP_LEFT is the normal orientation.</li> |
| * <li>TOP_RIGHT is a left-right mirror.</li> |
| * <li>BOTTOM_LEFT is a 180 degree rotation.</li> |
| * <li>BOTTOM_RIGHT is a top-bottom mirror.</li> |
| * <li>LEFT_TOP is mirrored about the top-left<->bottom-right axis.</li> |
| * <li>RIGHT_TOP is a 90 degree clockwise rotation.</li> |
| * <li>LEFT_BOTTOM is mirrored about the top-right<->bottom-left axis.</li> |
| * <li>RIGHT_BOTTOM is a 270 degree clockwise rotation.</li> |
| * </ul> |
| */ |
| 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_FIRE = 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; |
| } |
| |
| private static final String NULL_ARGUMENT_STRING = "Argument is null"; |
| private ExifData mData = new ExifData(DEFAULT_BYTE_ORDER); |
| public static final ByteOrder DEFAULT_BYTE_ORDER = ByteOrder.BIG_ENDIAN; |
| |
| public ExifInterface() { |
| mGPSDateStampFormat.setTimeZone(TimeZone.getTimeZone("UTC")); |
| } |
| |
| /** |
| * Reads the exif tags from a byte array, clearing this ExifInterface |
| * object's existing exif tags. |
| * |
| * @param jpeg a byte array containing a jpeg compressed image. |
| * @throws IOException |
| */ |
| public void readExif(byte[] jpeg) throws IOException { |
| readExif(new ByteArrayInputStream(jpeg)); |
| } |
| |
| /** |
| * Reads the exif tags from an InputStream, clearing this ExifInterface |
| * object's existing exif tags. |
| * |
| * @param inStream an InputStream containing a jpeg compressed image. |
| * @throws IOException |
| */ |
| public void readExif(InputStream inStream) throws IOException { |
| if (inStream == null) { |
| throw new IllegalArgumentException(NULL_ARGUMENT_STRING); |
| } |
| ExifData d = null; |
| try { |
| d = new ExifReader(this).read(inStream); |
| } catch (ExifInvalidFormatException e) { |
| throw new IOException("Invalid exif format : " + e); |
| } |
| mData = d; |
| } |
| |
| /** |
| * Reads the exif tags from a file, clearing this ExifInterface object's |
| * existing exif tags. |
| * |
| * @param inFileName a string representing the filepath to jpeg file. |
| * @throws FileNotFoundException |
| * @throws IOException |
| */ |
| public void readExif(String inFileName) throws FileNotFoundException, IOException { |
| if (inFileName == null) { |
| throw new IllegalArgumentException(NULL_ARGUMENT_STRING); |
| } |
| InputStream is = null; |
| |
| is = new BufferedInputStream(new FileInputStream(inFileName)); |
| readExif(is); |
| |
| is.close(); |
| } |
| |
| /** |
| * Sets the exif tags, clearing this ExifInterface object's existing exif |
| * tags. |
| * |
| * @param tags a collection of exif tags to set. |
| */ |
| public void setExif(Collection<ExifTag> tags) { |
| clearExif(); |
| setTags(tags); |
| } |
| |
| /** |
| * Clears this ExifInterface object's existing exif tags. |
| */ |
| public void clearExif() { |
| mData = new ExifData(DEFAULT_BYTE_ORDER); |
| } |
| |
| /** |
| * Writes the tags from this ExifInterface object into a jpeg image, |
| * removing prior exif tags. |
| * |
| * @param jpeg a byte array containing a jpeg compressed image. |
| * @param exifOutStream an OutputStream to which the jpeg image with added |
| * exif tags will be written. |
| * @throws IOException |
| */ |
| public void writeExif(byte[] jpeg, OutputStream exifOutStream) throws IOException { |
| if (jpeg == null || exifOutStream == null) { |
| throw new IllegalArgumentException(NULL_ARGUMENT_STRING); |
| } |
| OutputStream s = getExifWriterStream(exifOutStream); |
| s.write(jpeg, 0, jpeg.length); |
| s.flush(); |
| } |
| |
| /** |
| * Writes the tags from this ExifInterface object into a jpeg compressed |
| * bitmap, removing prior exif tags. |
| * |
| * @param bmap a bitmap to compress and write exif into. |
| * @param exifOutStream the OutputStream to which the jpeg image with added |
| * exif tags will be written. |
| * @throws IOException |
| */ |
| public void writeExif(Bitmap bmap, OutputStream exifOutStream) throws IOException { |
| if (bmap == null || exifOutStream == null) { |
| throw new IllegalArgumentException(NULL_ARGUMENT_STRING); |
| } |
| OutputStream s = getExifWriterStream(exifOutStream); |
| bmap.compress(Bitmap.CompressFormat.JPEG, 90, s); |
| s.flush(); |
| } |
| |
| /** |
| * Writes the tags from this ExifInterface object into a jpeg stream, |
| * removing prior exif tags. |
| * |
| * @param jpegStream an InputStream containing a jpeg compressed image. |
| * @param exifOutStream an OutputStream to which the jpeg image with added |
| * exif tags will be written. |
| * @throws IOException |
| */ |
| public void writeExif(InputStream jpegStream, OutputStream exifOutStream) throws IOException { |
| if (jpegStream == null || exifOutStream == null) { |
| throw new IllegalArgumentException(NULL_ARGUMENT_STRING); |
| } |
| OutputStream s = getExifWriterStream(exifOutStream); |
| doExifStreamIO(jpegStream, s); |
| s.flush(); |
| } |
| |
| /** |
| * Writes the tags from this ExifInterface object into a jpeg image, |
| * removing prior exif tags. |
| * |
| * @param jpeg a byte array containing a jpeg compressed image. |
| * @param exifOutFileName a String containing the filepath to which the jpeg |
| * image with added exif tags will be written. |
| * @throws FileNotFoundException |
| * @throws IOException |
| */ |
| public void writeExif(byte[] jpeg, String exifOutFileName) throws FileNotFoundException, |
| IOException { |
| if (jpeg == null || exifOutFileName == null) { |
| throw new IllegalArgumentException(NULL_ARGUMENT_STRING); |
| } |
| OutputStream s = null; |
| s = getExifWriterStream(exifOutFileName); |
| s.write(jpeg, 0, jpeg.length); |
| s.flush(); |
| s.close(); |
| } |
| |
| /** |
| * Writes the tags from this ExifInterface object into a jpeg compressed |
| * bitmap, removing prior exif tags. |
| * |
| * @param bmap a bitmap to compress and write exif into. |
| * @param exifOutFileName a String containing the filepath to which the jpeg |
| * image with added exif tags will be written. |
| * @throws FileNotFoundException |
| * @throws IOException |
| */ |
| public void writeExif(Bitmap bmap, String exifOutFileName) throws FileNotFoundException, |
| IOException { |
| if (bmap == null || exifOutFileName == null) { |
| throw new IllegalArgumentException(NULL_ARGUMENT_STRING); |
| } |
| OutputStream s = null; |
| |
| s = getExifWriterStream(exifOutFileName); |
| bmap.compress(Bitmap.CompressFormat.JPEG, 90, s); |
| s.flush(); |
| s.close(); |
| } |
| |
| /** |
| * Writes the tags from this ExifInterface object into a jpeg stream, |
| * removing prior exif tags. |
| * |
| * @param jpegStream an InputStream containing a jpeg compressed image. |
| * @param exifOutFileName a String containing the filepath to which the jpeg |
| * image with added exif tags will be written. |
| * @throws FileNotFoundException |
| * @throws IOException |
| */ |
| public void writeExif(InputStream jpegStream, String exifOutFileName) |
| throws FileNotFoundException, IOException { |
| if (jpegStream == null || exifOutFileName == null) { |
| throw new IllegalArgumentException(NULL_ARGUMENT_STRING); |
| } |
| OutputStream s = null; |
| |
| s = getExifWriterStream(exifOutFileName); |
| doExifStreamIO(jpegStream, s); |
| s.flush(); |
| |
| s.close(); |
| } |
| |
| /** |
| * Writes the tags from this ExifInterface object into a jpeg file, removing |
| * prior exif tags. |
| * |
| * @param jpegFileName a String containing the filepath for a jpeg file. |
| * @param exifOutFileName a String containing the filepath to which the jpeg |
| * image with added exif tags will be written. |
| * @throws FileNotFoundException |
| * @throws IOException |
| */ |
| public void writeExif(String jpegFileName, String exifOutFileName) |
| throws FileNotFoundException, IOException { |
| if (jpegFileName == null || exifOutFileName == null) { |
| throw new IllegalArgumentException(NULL_ARGUMENT_STRING); |
| } |
| InputStream is = null; |
| |
| is = new FileInputStream(jpegFileName); |
| writeExif(is, exifOutFileName); |
| |
| is.close(); |
| } |
| |
| /** |
| * Wraps an OutputStream object with an ExifOutputStream. Exif tags in this |
| * ExifInterface object will be added to a jpeg image written to this |
| * stream, removing prior exif tags. Other methods of this ExifInterface |
| * object should not be called until the returned OutputStream has been |
| * closed. |
| * |
| * @param outStream an OutputStream to wrap. |
| * @return an OutputStream that wraps the outStream parameter, and adds exif |
| * metadata. A jpeg image should be written to this stream. |
| */ |
| public OutputStream getExifWriterStream(OutputStream outStream) { |
| if (outStream == null) { |
| throw new IllegalArgumentException(NULL_ARGUMENT_STRING); |
| } |
| ExifOutputStream eos = new ExifOutputStream(outStream, this); |
| eos.setExifData(mData); |
| return eos; |
| } |
| |
| /** |
| * Returns an OutputStream object that writes to a file. Exif tags in this |
| * ExifInterface object will be added to a jpeg image written to this |
| * stream, removing prior exif tags. Other methods of this ExifInterface |
| * object should not be called until the returned OutputStream has been |
| * closed. |
| * |
| * @param exifOutFileName an String containing a filepath for a jpeg file. |
| * @return an OutputStream that writes to the exifOutFileName file, and adds |
| * exif metadata. A jpeg image should be written to this stream. |
| * @throws FileNotFoundException |
| */ |
| public OutputStream getExifWriterStream(String exifOutFileName) throws FileNotFoundException { |
| if (exifOutFileName == null) { |
| throw new IllegalArgumentException(NULL_ARGUMENT_STRING); |
| } |
| OutputStream out = null; |
| try { |
| out = new FileOutputStream(exifOutFileName); |
| } catch (FileNotFoundException e) { |
| closeSilently(out); |
| throw e; |
| } |
| return getExifWriterStream(out); |
| } |
| |
| /** |
| * Attempts to do an in-place rewrite the exif metadata in a file for the |
| * given tags. If tags do not exist or do not have the same size as the |
| * existing exif tags, this method will fail. |
| * |
| * @param filename a String containing a filepath for a jpeg file with exif |
| * tags to rewrite. |
| * @param tags tags that will be written into the jpeg file over existing |
| * tags if possible. |
| * @return true if success, false if could not overwrite. If false, no |
| * changes are made to the file. |
| * @throws FileNotFoundException |
| * @throws IOException |
| */ |
| public boolean rewriteExif(String filename, Collection<ExifTag> tags) |
| throws FileNotFoundException, IOException { |
| RandomAccessFile file = null; |
| InputStream is = null; |
| boolean ret; |
| try { |
| File temp = new File(filename); |
| is = new BufferedInputStream(new FileInputStream(temp)); |
| |
| // Parse beginning of APP1 in exif to find size of exif header. |
| ExifParser parser = null; |
| try { |
| parser = ExifParser.parse(is, this); |
| } catch (ExifInvalidFormatException e) { |
| throw new IOException("Invalid exif format : ", e); |
| } |
| long exifSize = parser.getOffsetToExifEndFromSOF(); |
| |
| // Free up resources |
| is.close(); |
| is = null; |
| |
| // Open file for memory mapping. |
| file = new RandomAccessFile(temp, "rw"); |
| long fileLength = file.length(); |
| if (fileLength < exifSize) { |
| throw new IOException("Filesize changed during operation"); |
| } |
| |
| // Map only exif header into memory. |
| ByteBuffer buf = file.getChannel().map(MapMode.READ_WRITE, 0, exifSize); |
| |
| // Attempt to overwrite tag values without changing lengths (avoids |
| // file copy). |
| ret = rewriteExif(buf, tags); |
| } finally { |
| closeSilently(is); |
| } |
| file.close(); |
| return ret; |
| } |
| |
| /** |
| * Attempts to do an in-place rewrite the exif metadata in a ByteBuffer for |
| * the given tags. If tags do not exist or do not have the same size as the |
| * existing exif tags, this method will fail. |
| * |
| * @param buf a ByteBuffer containing a jpeg file with existing exif tags to |
| * rewrite. |
| * @param tags tags that will be written into the jpeg ByteBuffer over |
| * existing tags if possible. |
| * @return true if success, false if could not overwrite. If false, no |
| * changes are made to the ByteBuffer. |
| * @throws IOException |
| */ |
| public boolean rewriteExif(ByteBuffer buf, Collection<ExifTag> tags) throws IOException { |
| ExifModifier mod = null; |
| try { |
| mod = new ExifModifier(buf, this); |
| for (ExifTag t : tags) { |
| mod.modifyTag(t); |
| } |
| return mod.commit(); |
| } catch (ExifInvalidFormatException e) { |
| throw new IOException("Invalid exif format : " + e); |
| } |
| } |
| |
| /** |
| * Attempts to do an in-place rewrite of the exif metadata. If this fails, |
| * fall back to overwriting file. This preserves tags that are not being |
| * rewritten. |
| * |
| * @param filename a String containing a filepath for a jpeg file. |
| * @param tags tags that will be written into the jpeg file over existing |
| * tags if possible. |
| * @throws FileNotFoundException |
| * @throws IOException |
| * @see #rewriteExif |
| */ |
| public void forceRewriteExif(String filename, Collection<ExifTag> tags) |
| throws FileNotFoundException, |
| IOException { |
| // Attempt in-place write |
| if (!rewriteExif(filename, tags)) { |
| // Fall back to doing a copy |
| ExifData tempData = mData; |
| mData = new ExifData(DEFAULT_BYTE_ORDER); |
| FileInputStream is = null; |
| ByteArrayOutputStream bytes = null; |
| try { |
| is = new FileInputStream(filename); |
| bytes = new ByteArrayOutputStream(); |
| doExifStreamIO(is, bytes); |
| byte[] imageBytes = bytes.toByteArray(); |
| readExif(imageBytes); |
| setTags(tags); |
| writeExif(imageBytes, filename); |
| } finally { |
| is.close(); |
| // Prevent clobbering of mData |
| mData = tempData; |
| } |
| } |
| } |
| |
| /** |
| * Attempts to do an in-place rewrite of the exif metadata using the tags in |
| * this ExifInterface object. If this fails, fall back to overwriting file. |
| * This preserves tags that are not being rewritten. |
| * |
| * @param filename a String containing a filepath for a jpeg file. |
| * @throws FileNotFoundException |
| * @throws IOException |
| * @see #rewriteExif |
| */ |
| public void forceRewriteExif(String filename) throws FileNotFoundException, IOException { |
| forceRewriteExif(filename, getAllTags()); |
| } |
| |
| /** |
| * Get the exif tags in this ExifInterface object or null if none exist. |
| * |
| * @return a List of {@link ExifTag}s. |
| */ |
| public List<ExifTag> getAllTags() { |
| return mData.getAllTags(); |
| } |
| |
| /** |
| * Returns a list of ExifTags that share a TID (which can be obtained by |
| * calling {@link #getTrueTagKey} on a defined tag constant) or null if none |
| * exist. |
| * |
| * @param tagId a TID as defined in the exif standard (or with |
| * {@link #defineTag}). |
| * @return a List of {@link ExifTag}s. |
| */ |
| public List<ExifTag> getTagsForTagId(short tagId) { |
| return mData.getAllTagsForTagId(tagId); |
| } |
| |
| /** |
| * Returns a list of ExifTags that share an IFD (which can be obtained by |
| * calling {@link #getTrueIFD} on a defined tag constant) or null if none |
| * exist. |
| * |
| * @param ifdId an IFD as defined in the exif standard (or with |
| * {@link #defineTag}). |
| * @return a List of {@link ExifTag}s. |
| */ |
| public List<ExifTag> getTagsForIfdId(int ifdId) { |
| return mData.getAllTagsForIfd(ifdId); |
| } |
| |
| /** |
| * Gets an ExifTag for an IFD other than the tag's default. |
| * |
| * @see #getTag |
| */ |
| public ExifTag getTag(int tagId, int ifdId) { |
| if (!ExifTag.isValidIfd(ifdId)) { |
| return null; |
| } |
| return mData.getTag(getTrueTagKey(tagId), ifdId); |
| } |
| |
| /** |
| * Returns the ExifTag in that tag's default IFD for a defined tag constant |
| * or null if none exists. |
| * |
| * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| * @return an {@link ExifTag} or null if none exists. |
| */ |
| public ExifTag getTag(int tagId) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| return getTag(tagId, ifdId); |
| } |
| |
| /** |
| * Gets a tag value for an IFD other than the tag's default. |
| * |
| * @see #getTagValue |
| */ |
| public Object getTagValue(int tagId, int ifdId) { |
| ExifTag t = getTag(tagId, ifdId); |
| return (t == null) ? null : t.getValue(); |
| } |
| |
| /** |
| * Returns the value of the ExifTag in that tag's default IFD for a defined |
| * tag constant or null if none exists or the value could not be cast into |
| * the return type. |
| * |
| * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| * @return the value of the ExifTag or null if none exists. |
| */ |
| public Object getTagValue(int tagId) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| return getTagValue(tagId, ifdId); |
| } |
| |
| /* |
| * Getter methods that are similar to getTagValue. Null is returned if the |
| * tag value cannot be cast into the return type. |
| */ |
| |
| /** |
| * @see #getTagValue |
| */ |
| public String getTagStringValue(int tagId, int ifdId) { |
| ExifTag t = getTag(tagId, ifdId); |
| if (t == null) { |
| return null; |
| } |
| return t.getValueAsString(); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public String getTagStringValue(int tagId) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| return getTagStringValue(tagId, ifdId); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public Long getTagLongValue(int tagId, int ifdId) { |
| long[] l = getTagLongValues(tagId, ifdId); |
| if (l == null || l.length <= 0) { |
| return null; |
| } |
| return new Long(l[0]); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public Long getTagLongValue(int tagId) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| return getTagLongValue(tagId, ifdId); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public Integer getTagIntValue(int tagId, int ifdId) { |
| int[] l = getTagIntValues(tagId, ifdId); |
| if (l == null || l.length <= 0) { |
| return null; |
| } |
| return new Integer(l[0]); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public Integer getTagIntValue(int tagId) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| return getTagIntValue(tagId, ifdId); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public Byte getTagByteValue(int tagId, int ifdId) { |
| byte[] l = getTagByteValues(tagId, ifdId); |
| if (l == null || l.length <= 0) { |
| return null; |
| } |
| return new Byte(l[0]); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public Byte getTagByteValue(int tagId) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| return getTagByteValue(tagId, ifdId); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public Rational getTagRationalValue(int tagId, int ifdId) { |
| Rational[] l = getTagRationalValues(tagId, ifdId); |
| if (l == null || l.length == 0) { |
| return null; |
| } |
| return new Rational(l[0]); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public Rational getTagRationalValue(int tagId) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| return getTagRationalValue(tagId, ifdId); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public long[] getTagLongValues(int tagId, int ifdId) { |
| ExifTag t = getTag(tagId, ifdId); |
| if (t == null) { |
| return null; |
| } |
| return t.getValueAsLongs(); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public long[] getTagLongValues(int tagId) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| return getTagLongValues(tagId, ifdId); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public int[] getTagIntValues(int tagId, int ifdId) { |
| ExifTag t = getTag(tagId, ifdId); |
| if (t == null) { |
| return null; |
| } |
| return t.getValueAsInts(); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public int[] getTagIntValues(int tagId) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| return getTagIntValues(tagId, ifdId); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public byte[] getTagByteValues(int tagId, int ifdId) { |
| ExifTag t = getTag(tagId, ifdId); |
| if (t == null) { |
| return null; |
| } |
| return t.getValueAsBytes(); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public byte[] getTagByteValues(int tagId) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| return getTagByteValues(tagId, ifdId); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public Rational[] getTagRationalValues(int tagId, int ifdId) { |
| ExifTag t = getTag(tagId, ifdId); |
| if (t == null) { |
| return null; |
| } |
| return t.getValueAsRationals(); |
| } |
| |
| /** |
| * @see #getTagValue |
| */ |
| public Rational[] getTagRationalValues(int tagId) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| return getTagRationalValues(tagId, ifdId); |
| } |
| |
| /** |
| * Checks whether a tag has a defined number of elements. |
| * |
| * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| * @return true if the tag has a defined number of elements. |
| */ |
| public boolean isTagCountDefined(int tagId) { |
| int info = getTagInfo().get(tagId); |
| // No value in info can be zero, as all tags have a non-zero type |
| if (info == 0) { |
| return false; |
| } |
| return getComponentCountFromInfo(info) != ExifTag.SIZE_UNDEFINED; |
| } |
| |
| /** |
| * Gets the defined number of elements for a tag. |
| * |
| * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| * @return the number of elements or {@link ExifTag#SIZE_UNDEFINED} if the |
| * tag or the number of elements is not defined. |
| */ |
| public int getDefinedTagCount(int tagId) { |
| int info = getTagInfo().get(tagId); |
| if (info == 0) { |
| return ExifTag.SIZE_UNDEFINED; |
| } |
| return getComponentCountFromInfo(info); |
| } |
| |
| /** |
| * Gets the number of elements for an ExifTag in a given IFD. |
| * |
| * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| * @param ifdId the IFD containing the ExifTag to check. |
| * @return the number of elements in the ExifTag, if the tag's size is |
| * undefined this will return the actual number of elements that is |
| * in the ExifTag's value. |
| */ |
| public int getActualTagCount(int tagId, int ifdId) { |
| ExifTag t = getTag(tagId, ifdId); |
| if (t == null) { |
| return 0; |
| } |
| return t.getComponentCount(); |
| } |
| |
| /** |
| * Gets the default IFD for a tag. |
| * |
| * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| * @return the default IFD for a tag definition or {@link #IFD_NULL} if no |
| * definition exists. |
| */ |
| public int getDefinedTagDefaultIfd(int tagId) { |
| int info = getTagInfo().get(tagId); |
| if (info == DEFINITION_NULL) { |
| return IFD_NULL; |
| } |
| return getTrueIfd(tagId); |
| } |
| |
| /** |
| * Gets the defined type for a tag. |
| * |
| * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| * @return the type. |
| * @see ExifTag#getDataType() |
| */ |
| public short getDefinedTagType(int tagId) { |
| int info = getTagInfo().get(tagId); |
| if (info == 0) { |
| return -1; |
| } |
| return getTypeFromInfo(info); |
| } |
| |
| /** |
| * Returns true if tag TID 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} |
| * <p> |
| * Note: defining tags with these TID's is disallowed. |
| * |
| * @param tag a tag's TID (can be obtained from a defined tag constant with |
| * {@link #getTrueTagKey}). |
| * @return true if the TID is that of an offset tag. |
| */ |
| protected static boolean isOffsetTag(short tag) { |
| return sOffsetTags.contains(tag); |
| } |
| |
| /** |
| * Creates a tag for a defined tag constant in a given IFD if that IFD is |
| * allowed for the tag. This method will fail anytime the appropriate |
| * {@link ExifTag#setValue} for this tag's datatype would fail. |
| * |
| * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| * @param ifdId the IFD that the tag should be in. |
| * @param val the value of the tag to set. |
| * @return an ExifTag object or null if one could not be constructed. |
| * @see #buildTag |
| */ |
| public ExifTag buildTag(int tagId, int ifdId, Object val) { |
| int info = getTagInfo().get(tagId); |
| if (info == 0 || val == null) { |
| return null; |
| } |
| short type = getTypeFromInfo(info); |
| int definedCount = getComponentCountFromInfo(info); |
| boolean hasDefinedCount = (definedCount != ExifTag.SIZE_UNDEFINED); |
| if (!ExifInterface.isIfdAllowed(info, ifdId)) { |
| return null; |
| } |
| ExifTag t = new ExifTag(getTrueTagKey(tagId), type, definedCount, ifdId, hasDefinedCount); |
| if (!t.setValue(val)) { |
| return null; |
| } |
| return t; |
| } |
| |
| /** |
| * Creates a tag for a defined tag constant in the tag's default IFD. |
| * |
| * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| * @param val the tag's value. |
| * @return an ExifTag object. |
| */ |
| public ExifTag buildTag(int tagId, Object val) { |
| int ifdId = getTrueIfd(tagId); |
| return buildTag(tagId, ifdId, val); |
| } |
| |
| protected ExifTag buildUninitializedTag(int tagId) { |
| int info = getTagInfo().get(tagId); |
| if (info == 0) { |
| return null; |
| } |
| short type = getTypeFromInfo(info); |
| int definedCount = getComponentCountFromInfo(info); |
| boolean hasDefinedCount = (definedCount != ExifTag.SIZE_UNDEFINED); |
| int ifdId = getTrueIfd(tagId); |
| ExifTag t = new ExifTag(getTrueTagKey(tagId), type, definedCount, ifdId, hasDefinedCount); |
| return t; |
| } |
| |
| /** |
| * Sets the value of an ExifTag if it exists in the given IFD. The value |
| * must be the correct type and length for that ExifTag. |
| * |
| * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| * @param ifdId the IFD that the ExifTag is in. |
| * @param val the value to set. |
| * @return true if success, false if the ExifTag doesn't exist or the value |
| * is the wrong type/length. |
| * @see #setTagValue |
| */ |
| public boolean setTagValue(int tagId, int ifdId, Object val) { |
| ExifTag t = getTag(tagId, ifdId); |
| if (t == null) { |
| return false; |
| } |
| return t.setValue(val); |
| } |
| |
| /** |
| * Sets the value of an ExifTag if it exists it's default IFD. The value |
| * must be the correct type and length for that ExifTag. |
| * |
| * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| * @param val the value to set. |
| * @return true if success, false if the ExifTag doesn't exist or the value |
| * is the wrong type/length. |
| */ |
| public boolean setTagValue(int tagId, Object val) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| return setTagValue(tagId, ifdId, val); |
| } |
| |
| /** |
| * Puts an ExifTag into this ExifInterface object's tags, removing a |
| * previous ExifTag with the same TID and IFD. The IFD it is put into will |
| * be the one the tag was created with in {@link #buildTag}. |
| * |
| * @param tag an ExifTag to put into this ExifInterface's tags. |
| * @return the previous ExifTag with the same TID and IFD or null if none |
| * exists. |
| */ |
| public ExifTag setTag(ExifTag tag) { |
| return mData.addTag(tag); |
| } |
| |
| /** |
| * Puts a collection of ExifTags into this ExifInterface objects's tags. Any |
| * previous ExifTags with the same TID and IFDs will be removed. |
| * |
| * @param tags a Collection of ExifTags. |
| * @see #setTag |
| */ |
| public void setTags(Collection<ExifTag> tags) { |
| for (ExifTag t : tags) { |
| setTag(t); |
| } |
| } |
| |
| /** |
| * Removes the ExifTag for a tag constant from the given IFD. |
| * |
| * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| * @param ifdId the IFD of the ExifTag to remove. |
| */ |
| public void deleteTag(int tagId, int ifdId) { |
| mData.removeTag(getTrueTagKey(tagId), ifdId); |
| } |
| |
| /** |
| * Removes the ExifTag for a tag constant from that tag's default IFD. |
| * |
| * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| */ |
| public void deleteTag(int tagId) { |
| int ifdId = getDefinedTagDefaultIfd(tagId); |
| deleteTag(tagId, ifdId); |
| } |
| |
| /** |
| * Creates a new tag definition in this ExifInterface object for a given TID |
| * and default IFD. Creating a definition with the same TID and default IFD |
| * as a previous definition will override it. |
| * |
| * @param tagId the TID for the tag. |
| * @param defaultIfd the default IFD for the tag. |
| * @param tagType the type of the tag (see {@link ExifTag#getDataType()}). |
| * @param defaultComponentCount the number of elements of this tag's type in |
| * the tags value. |
| * @param allowedIfds the IFD's this tag is allowed to be put in. |
| * @return the defined tag constant (e.g. {@link #TAG_IMAGE_WIDTH}) or |
| * {@link #TAG_NULL} if the definition could not be made. |
| */ |
| public int setTagDefinition(short tagId, int defaultIfd, short tagType, |
| short defaultComponentCount, int[] allowedIfds) { |
| if (sBannedDefines.contains(tagId)) { |
| return TAG_NULL; |
| } |
| if (ExifTag.isValidType(tagType) && ExifTag.isValidIfd(defaultIfd)) { |
| int tagDef = defineTag(defaultIfd, tagId); |
| if (tagDef == TAG_NULL) { |
| return TAG_NULL; |
| } |
| int[] otherDefs = getTagDefinitionsForTagId(tagId); |
| SparseIntArray infos = getTagInfo(); |
| // Make sure defaultIfd is in allowedIfds |
| boolean defaultCheck = false; |
| for (int i : allowedIfds) { |
| if (defaultIfd == i) { |
| defaultCheck = true; |
| } |
| if (!ExifTag.isValidIfd(i)) { |
| return TAG_NULL; |
| } |
| } |
| if (!defaultCheck) { |
| return TAG_NULL; |
| } |
| |
| int ifdFlags = getFlagsFromAllowedIfds(allowedIfds); |
| // Make sure no identical tags can exist in allowedIfds |
| if (otherDefs != null) { |
| for (int def : otherDefs) { |
| int tagInfo = infos.get(def); |
| int allowedFlags = getAllowedIfdFlagsFromInfo(tagInfo); |
| if ((ifdFlags & allowedFlags) != 0) { |
| return TAG_NULL; |
| } |
| } |
| } |
| getTagInfo().put(tagDef, ifdFlags << 24 | (tagType << 16) | defaultComponentCount); |
| return tagDef; |
| } |
| return TAG_NULL; |
| } |
| |
| protected int getTagDefinition(short tagId, int defaultIfd) { |
| return getTagInfo().get(defineTag(defaultIfd, tagId)); |
| } |
| |
| protected int[] getTagDefinitionsForTagId(short tagId) { |
| int[] ifds = IfdData.getIfds(); |
| int[] defs = new int[ifds.length]; |
| int counter = 0; |
| SparseIntArray infos = getTagInfo(); |
| for (int i : ifds) { |
| int def = defineTag(i, tagId); |
| if (infos.get(def) != DEFINITION_NULL) { |
| defs[counter++] = def; |
| } |
| } |
| if (counter == 0) { |
| return null; |
| } |
| |
| return Arrays.copyOfRange(defs, 0, counter); |
| } |
| |
| protected int getTagDefinitionForTag(ExifTag tag) { |
| short type = tag.getDataType(); |
| int count = tag.getComponentCount(); |
| int ifd = tag.getIfd(); |
| return getTagDefinitionForTag(tag.getTagId(), type, count, ifd); |
| } |
| |
| protected int getTagDefinitionForTag(short tagId, short type, int count, int ifd) { |
| int[] defs = getTagDefinitionsForTagId(tagId); |
| if (defs == null) { |
| return TAG_NULL; |
| } |
| SparseIntArray infos = getTagInfo(); |
| int ret = TAG_NULL; |
| for (int i : defs) { |
| int info = infos.get(i); |
| short def_type = getTypeFromInfo(info); |
| int def_count = getComponentCountFromInfo(info); |
| int[] def_ifds = getAllowedIfdsFromInfo(info); |
| boolean valid_ifd = false; |
| for (int j : def_ifds) { |
| if (j == ifd) { |
| valid_ifd = true; |
| break; |
| } |
| } |
| if (valid_ifd && type == def_type |
| && (count == def_count || def_count == ExifTag.SIZE_UNDEFINED)) { |
| ret = i; |
| break; |
| } |
| } |
| return ret; |
| } |
| |
| /** |
| * Removes a tag definition for given defined tag constant. |
| * |
| * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. |
| */ |
| public void removeTagDefinition(int tagId) { |
| getTagInfo().delete(tagId); |
| } |
| |
| /** |
| * Resets tag definitions to the default ones. |
| */ |
| public void resetTagDefinitions() { |
| mTagInfo = null; |
| } |
| |
| /** |
| * Returns the thumbnail from IFD1 as a bitmap, or null if none exists. |
| * |
| * @return the thumbnail as a bitmap. |
| */ |
| public Bitmap getThumbnailBitmap() { |
| if (mData.hasCompressedThumbnail()) { |
| byte[] thumb = mData.getCompressedThumbnail(); |
| return BitmapFactory.decodeByteArray(thumb, 0, thumb.length); |
| } else if (mData.hasUncompressedStrip()) { |
| // TODO: implement uncompressed |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the thumbnail from IFD1 as a byte array, or null if none exists. |
| * The bytes may either be an uncompressed strip as specified in the exif |
| * standard or a jpeg compressed image. |
| * |
| * @return the thumbnail as a byte array. |
| */ |
| public byte[] getThumbnailBytes() { |
| if (mData.hasCompressedThumbnail()) { |
| return mData.getCompressedThumbnail(); |
| } else if (mData.hasUncompressedStrip()) { |
| // TODO: implement this |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the thumbnail if it is jpeg compressed, or null if none exists. |
| * |
| * @return the thumbnail as a byte array. |
| */ |
| public byte[] getThumbnail() { |
| return mData.getCompressedThumbnail(); |
| } |
| |
| /** |
| * Check if thumbnail is compressed. |
| * |
| * @return true if the thumbnail is compressed. |
| */ |
| public boolean isThumbnailCompressed() { |
| return mData.hasCompressedThumbnail(); |
| } |
| |
| /** |
| * Check if thumbnail exists. |
| * |
| * @return true if a compressed thumbnail exists. |
| */ |
| public boolean hasThumbnail() { |
| // TODO: add back in uncompressed strip |
| return mData.hasCompressedThumbnail(); |
| } |
| |
| // TODO: uncompressed thumbnail setters |
| |
| /** |
| * Sets the thumbnail to be a jpeg compressed image. Clears any prior |
| * thumbnail. |
| * |
| * @param thumb a byte array containing a jpeg compressed image. |
| * @return true if the thumbnail was set. |
| */ |
| public boolean setCompressedThumbnail(byte[] thumb) { |
| mData.clearThumbnailAndStrips(); |
| mData.setCompressedThumbnail(thumb); |
| return true; |
| } |
| |
| /** |
| * Sets the thumbnail to be a jpeg compressed bitmap. Clears any prior |
| * thumbnail. |
| * |
| * @param thumb a bitmap to compress to a jpeg thumbnail. |
| * @return true if the thumbnail was set. |
| */ |
| public boolean setCompressedThumbnail(Bitmap thumb) { |
| ByteArrayOutputStream thumbnail = new ByteArrayOutputStream(); |
| if (!thumb.compress(Bitmap.CompressFormat.JPEG, 90, thumbnail)) { |
| return false; |
| } |
| return setCompressedThumbnail(thumbnail.toByteArray()); |
| } |
| |
| /** |
| * Clears the compressed thumbnail if it exists. |
| */ |
| public void removeCompressedThumbnail() { |
| mData.setCompressedThumbnail(null); |
| } |
| |
| // Convenience methods: |
| |
| /** |
| * Decodes the user comment tag into string as specified in the EXIF |
| * standard. Returns null if decoding failed. |
| */ |
| public String getUserComment() { |
| return mData.getUserComment(); |
| } |
| |
| /** |
| * Returns the Orientation ExifTag value for a given number of degrees. |
| * |
| * @param degrees the amount an image is rotated in degrees. |
| */ |
| public static short getOrientationValueForRotation(int degrees) { |
| degrees %= 360; |
| if (degrees < 0) { |
| degrees += 360; |
| } |
| if (degrees < 90) { |
| return Orientation.TOP_LEFT; // 0 degrees |
| } else if (degrees < 180) { |
| return Orientation.RIGHT_TOP; // 90 degrees cw |
| } else if (degrees < 270) { |
| return Orientation.BOTTOM_LEFT; // 180 degrees |
| } else { |
| return Orientation.RIGHT_BOTTOM; // 270 degrees cw |
| } |
| } |
| |
| /** |
| * Returns the rotation degrees corresponding to an ExifTag Orientation |
| * value. |
| * |
| * @param orientation the ExifTag Orientation value. |
| */ |
| public static int getRotationForOrientationValue(short orientation) { |
| switch (orientation) { |
| case Orientation.TOP_LEFT: |
| return 0; |
| case Orientation.RIGHT_TOP: |
| return 90; |
| case Orientation.BOTTOM_LEFT: |
| return 180; |
| case Orientation.RIGHT_BOTTOM: |
| return 270; |
| default: |
| return 0; |
| } |
| } |
| |
| /** |
| * Gets the double representation of the GPS latitude or longitude |
| * coordinate. |
| * |
| * @param coordinate an array of 3 Rationals representing the degrees, |
| * minutes, and seconds of the GPS location as defined in the |
| * exif specification. |
| * @param reference a GPS reference reperesented by a String containing "N", |
| * "S", "E", or "W". |
| * @return the GPS coordinate represented as degrees + minutes/60 + |
| * seconds/3600 |
| */ |
| public static double convertLatOrLongToDouble(Rational[] coordinate, String reference) { |
| try { |
| double degrees = coordinate[0].toDouble(); |
| double minutes = coordinate[1].toDouble(); |
| double seconds = coordinate[2].toDouble(); |
| double result = degrees + minutes / 60.0 + seconds / 3600.0; |
| if ((reference.equals("S") || reference.equals("W"))) { |
| return -result; |
| } |
| return result; |
| } catch (ArrayIndexOutOfBoundsException e) { |
| throw new IllegalArgumentException(); |
| } |
| } |
| |
| /** |
| * Gets the GPS latitude and longitude as a pair of doubles from this |
| * ExifInterface object's tags, or null if the necessary tags do not exist. |
| * |
| * @return an array of 2 doubles containing the latitude, and longitude |
| * respectively. |
| * @see #convertLatOrLongToDouble |
| */ |
| public double[] getLatLongAsDoubles() { |
| Rational[] latitude = getTagRationalValues(TAG_GPS_LATITUDE); |
| String latitudeRef = getTagStringValue(TAG_GPS_LATITUDE_REF); |
| Rational[] longitude = getTagRationalValues(TAG_GPS_LONGITUDE); |
| String longitudeRef = getTagStringValue(TAG_GPS_LONGITUDE_REF); |
| if (latitude == null || longitude == null || latitudeRef == null || longitudeRef == null |
| || latitude.length < 3 || longitude.length < 3) { |
| return null; |
| } |
| double[] latLon = new double[2]; |
| latLon[0] = convertLatOrLongToDouble(latitude, latitudeRef); |
| latLon[1] = convertLatOrLongToDouble(longitude, longitudeRef); |
| return latLon; |
| } |
| |
| private static final String GPS_DATE_FORMAT_STR = "yyyy:MM:dd"; |
| private static final String DATETIME_FORMAT_STR = "yyyy:MM:dd kk:mm:ss"; |
| private final DateFormat mDateTimeStampFormat = new SimpleDateFormat(DATETIME_FORMAT_STR); |
| private final DateFormat mGPSDateStampFormat = new SimpleDateFormat(GPS_DATE_FORMAT_STR); |
| private final Calendar mGPSTimeStampCalendar = Calendar |
| .getInstance(TimeZone.getTimeZone("UTC")); |
| |
| /** |
| * Creates, formats, and sets the DateTimeStamp tag for one of: |
| * {@link #TAG_DATE_TIME}, {@link #TAG_DATE_TIME_DIGITIZED}, |
| * {@link #TAG_DATE_TIME_ORIGINAL}. |
| * |
| * @param tagId one of the DateTimeStamp tags. |
| * @param timestamp a timestamp to format, in ms. |
| * @param timezone a TimeZone object. |
| * @return true if success, false if the tag could not be set. |
| */ |
| public boolean addDateTimeStampTag(int tagId, long timestamp, TimeZone timezone) { |
| if (tagId == TAG_DATE_TIME || tagId == TAG_DATE_TIME_DIGITIZED |
| || tagId == TAG_DATE_TIME_ORIGINAL) { |
| mDateTimeStampFormat.setTimeZone(timezone); |
| ExifTag t = buildTag(tagId, mDateTimeStampFormat.format(timestamp)); |
| if (t == null) { |
| return false; |
| } |
| setTag(t); |
| } else { |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Creates and sets all to the GPS tags for a give latitude and longitude. |
| * |
| * @param latitude a GPS latitude coordinate. |
| * @param longitude a GPS longitude coordinate. |
| * @return true if success, false if they could not be created or set. |
| */ |
| public boolean addGpsTags(double latitude, double longitude) { |
| ExifTag latTag = buildTag(TAG_GPS_LATITUDE, toExifLatLong(latitude)); |
| ExifTag longTag = buildTag(TAG_GPS_LONGITUDE, toExifLatLong(longitude)); |
| ExifTag latRefTag = buildTag(TAG_GPS_LATITUDE_REF, |
| latitude >= 0 ? ExifInterface.GpsLatitudeRef.NORTH |
| : ExifInterface.GpsLatitudeRef.SOUTH); |
| ExifTag longRefTag = buildTag(TAG_GPS_LONGITUDE_REF, |
| longitude >= 0 ? ExifInterface.GpsLongitudeRef.EAST |
| : ExifInterface.GpsLongitudeRef.WEST); |
| if (latTag == null || longTag == null || latRefTag == null || longRefTag == null) { |
| return false; |
| } |
| setTag(latTag); |
| setTag(longTag); |
| setTag(latRefTag); |
| setTag(longRefTag); |
| return true; |
| } |
| |
| /** |
| * Creates and sets the GPS timestamp tag. |
| * |
| * @param timestamp a GPS timestamp. |
| * @return true if success, false if could not be created or set. |
| */ |
| public boolean addGpsDateTimeStampTag(long timestamp) { |
| ExifTag t = buildTag(TAG_GPS_DATE_STAMP, mGPSDateStampFormat.format(timestamp)); |
| if (t == null) { |
| return false; |
| } |
| setTag(t); |
| mGPSTimeStampCalendar.setTimeInMillis(timestamp); |
| t = buildTag(TAG_GPS_TIME_STAMP, new Rational[] { |
| new Rational(mGPSTimeStampCalendar.get(Calendar.HOUR_OF_DAY), 1), |
| new Rational(mGPSTimeStampCalendar.get(Calendar.MINUTE), 1), |
| new Rational(mGPSTimeStampCalendar.get(Calendar.SECOND), 1) |
| }); |
| if (t == null) { |
| return false; |
| } |
| setTag(t); |
| return true; |
| } |
| |
| private static Rational[] toExifLatLong(double value) { |
| // convert to the format dd/1 mm/1 ssss/100 |
| value = Math.abs(value); |
| int degrees = (int) value; |
| value = (value - degrees) * 60; |
| int minutes = (int) value; |
| value = (value - minutes) * 6000; |
| int seconds = (int) value; |
| return new Rational[] { |
| new Rational(degrees, 1), new Rational(minutes, 1), new Rational(seconds, 100) |
| }; |
| } |
| |
| private void doExifStreamIO(InputStream is, OutputStream os) throws IOException { |
| byte[] buf = new byte[1024]; |
| int ret = is.read(buf, 0, 1024); |
| while (ret != -1) { |
| os.write(buf, 0, ret); |
| ret = is.read(buf, 0, 1024); |
| } |
| } |
| |
| protected static void closeSilently(Closeable c) { |
| if (c != null) { |
| try { |
| c.close(); |
| } catch (Throwable e) { |
| // ignored |
| } |
| } |
| } |
| |
| private SparseIntArray mTagInfo = null; |
| |
| protected SparseIntArray getTagInfo() { |
| if (mTagInfo == null) { |
| mTagInfo = new SparseIntArray(); |
| initTagInfo(); |
| } |
| return mTagInfo; |
| } |
| |
| private void initTagInfo() { |
| /** |
| * We put tag information in a 4-bytes integer. The first byte a bitmask |
| * representing the allowed IFDs of the tag, the second byte is the data |
| * type, and the last two byte are a short value indicating the default |
| * component count of this tag. |
| */ |
| // IFD0 tags |
| int[] ifdAllowedIfds = { |
| IfdId.TYPE_IFD_0, IfdId.TYPE_IFD_1 |
| }; |
| int ifdFlags = getFlagsFromAllowedIfds(ifdAllowedIfds) << 24; |
| mTagInfo.put(ExifInterface.TAG_MAKE, |
| ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_IMAGE_WIDTH, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_IMAGE_LENGTH, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_BITS_PER_SAMPLE, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 3); |
| mTagInfo.put(ExifInterface.TAG_COMPRESSION, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_PHOTOMETRIC_INTERPRETATION, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_ORIENTATION, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 |
| | 1); |
| mTagInfo.put(ExifInterface.TAG_SAMPLES_PER_PIXEL, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_PLANAR_CONFIGURATION, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_Y_CB_CR_SUB_SAMPLING, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_Y_CB_CR_POSITIONING, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_X_RESOLUTION, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_Y_RESOLUTION, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_RESOLUTION_UNIT, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_STRIP_OFFSETS, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_ROWS_PER_STRIP, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_STRIP_BYTE_COUNTS, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_TRANSFER_FUNCTION, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 3 * 256); |
| mTagInfo.put(ExifInterface.TAG_WHITE_POINT, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_PRIMARY_CHROMATICITIES, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 6); |
| mTagInfo.put(ExifInterface.TAG_Y_CB_CR_COEFFICIENTS, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 3); |
| mTagInfo.put(ExifInterface.TAG_REFERENCE_BLACK_WHITE, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 6); |
| mTagInfo.put(ExifInterface.TAG_DATE_TIME, |
| ifdFlags | ExifTag.TYPE_ASCII << 16 | 20); |
| mTagInfo.put(ExifInterface.TAG_IMAGE_DESCRIPTION, |
| ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_MAKE, |
| ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_MODEL, |
| ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_SOFTWARE, |
| ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_ARTIST, |
| ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_COPYRIGHT, |
| ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_EXIF_IFD, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_GPS_IFD, |
| ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1); |
| // IFD1 tags |
| int[] ifd1AllowedIfds = { |
| IfdId.TYPE_IFD_1 |
| }; |
| int ifdFlags1 = getFlagsFromAllowedIfds(ifd1AllowedIfds) << 24; |
| mTagInfo.put(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT, |
| ifdFlags1 | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, |
| ifdFlags1 | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1); |
| // Exif tags |
| int[] exifAllowedIfds = { |
| IfdId.TYPE_IFD_EXIF |
| }; |
| int exifFlags = getFlagsFromAllowedIfds(exifAllowedIfds) << 24; |
| mTagInfo.put(ExifInterface.TAG_EXIF_VERSION, |
| exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4); |
| mTagInfo.put(ExifInterface.TAG_FLASHPIX_VERSION, |
| exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4); |
| mTagInfo.put(ExifInterface.TAG_COLOR_SPACE, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_COMPONENTS_CONFIGURATION, |
| exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4); |
| mTagInfo.put(ExifInterface.TAG_COMPRESSED_BITS_PER_PIXEL, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_PIXEL_X_DIMENSION, |
| exifFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_PIXEL_Y_DIMENSION, |
| exifFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_MAKER_NOTE, |
| exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_USER_COMMENT, |
| exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_RELATED_SOUND_FILE, |
| exifFlags | ExifTag.TYPE_ASCII << 16 | 13); |
| mTagInfo.put(ExifInterface.TAG_DATE_TIME_ORIGINAL, |
| exifFlags | ExifTag.TYPE_ASCII << 16 | 20); |
| mTagInfo.put(ExifInterface.TAG_DATE_TIME_DIGITIZED, |
| exifFlags | ExifTag.TYPE_ASCII << 16 | 20); |
| mTagInfo.put(ExifInterface.TAG_SUB_SEC_TIME, |
| exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_SUB_SEC_TIME_ORIGINAL, |
| exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_SUB_SEC_TIME_DIGITIZED, |
| exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_IMAGE_UNIQUE_ID, |
| exifFlags | ExifTag.TYPE_ASCII << 16 | 33); |
| mTagInfo.put(ExifInterface.TAG_EXPOSURE_TIME, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_F_NUMBER, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_EXPOSURE_PROGRAM, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_SPECTRAL_SENSITIVITY, |
| exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_ISO_SPEED_RATINGS, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_OECF, |
| exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_SHUTTER_SPEED_VALUE, |
| exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_APERTURE_VALUE, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_BRIGHTNESS_VALUE, |
| exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_EXPOSURE_BIAS_VALUE, |
| exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_MAX_APERTURE_VALUE, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_SUBJECT_DISTANCE, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_METERING_MODE, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_LIGHT_SOURCE, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_FLASH, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_FOCAL_LENGTH, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_SUBJECT_AREA, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_FLASH_ENERGY, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_SPATIAL_FREQUENCY_RESPONSE, |
| exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_FOCAL_PLANE_X_RESOLUTION, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_FOCAL_PLANE_Y_RESOLUTION, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_FOCAL_PLANE_RESOLUTION_UNIT, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_SUBJECT_LOCATION, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_EXPOSURE_INDEX, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_SENSING_METHOD, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_FILE_SOURCE, |
| exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_SCENE_TYPE, |
| exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_CFA_PATTERN, |
| exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_CUSTOM_RENDERED, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_EXPOSURE_MODE, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_WHITE_BALANCE, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_DIGITAL_ZOOM_RATIO, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_FOCAL_LENGTH_IN_35_MM_FILE, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_SCENE_CAPTURE_TYPE, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_GAIN_CONTROL, |
| exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_CONTRAST, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_SATURATION, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_SHARPNESS, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_DEVICE_SETTING_DESCRIPTION, |
| exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_SUBJECT_DISTANCE_RANGE, |
| exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_INTEROPERABILITY_IFD, exifFlags |
| | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1); |
| // GPS tag |
| int[] gpsAllowedIfds = { |
| IfdId.TYPE_IFD_GPS |
| }; |
| int gpsFlags = getFlagsFromAllowedIfds(gpsAllowedIfds) << 24; |
| mTagInfo.put(ExifInterface.TAG_GPS_VERSION_ID, |
| gpsFlags | ExifTag.TYPE_UNSIGNED_BYTE << 16 | 4); |
| mTagInfo.put(ExifInterface.TAG_GPS_LATITUDE_REF, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_GPS_LONGITUDE_REF, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_GPS_LATITUDE, |
| gpsFlags | ExifTag.TYPE_RATIONAL << 16 | 3); |
| mTagInfo.put(ExifInterface.TAG_GPS_LONGITUDE, |
| gpsFlags | ExifTag.TYPE_RATIONAL << 16 | 3); |
| mTagInfo.put(ExifInterface.TAG_GPS_ALTITUDE_REF, |
| gpsFlags | ExifTag.TYPE_UNSIGNED_BYTE << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_GPS_ALTITUDE, |
| gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_GPS_TIME_STAMP, |
| gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 3); |
| mTagInfo.put(ExifInterface.TAG_GPS_SATTELLITES, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_GPS_STATUS, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_GPS_MEASURE_MODE, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_GPS_DOP, |
| gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_GPS_SPEED_REF, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_GPS_SPEED, |
| gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_GPS_TRACK_REF, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_GPS_TRACK, |
| gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_GPS_IMG_DIRECTION_REF, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_GPS_IMG_DIRECTION, |
| gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_GPS_MAP_DATUM, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_GPS_DEST_LATITUDE_REF, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_GPS_DEST_LATITUDE, |
| gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_GPS_DEST_BEARING_REF, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_GPS_DEST_BEARING, |
| gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_GPS_DEST_DISTANCE_REF, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | 2); |
| mTagInfo.put(ExifInterface.TAG_GPS_DEST_DISTANCE, |
| gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1); |
| mTagInfo.put(ExifInterface.TAG_GPS_PROCESSING_METHOD, |
| gpsFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_GPS_AREA_INFORMATION, |
| gpsFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED); |
| mTagInfo.put(ExifInterface.TAG_GPS_DATE_STAMP, |
| gpsFlags | ExifTag.TYPE_ASCII << 16 | 11); |
| mTagInfo.put(ExifInterface.TAG_GPS_DIFFERENTIAL, |
| gpsFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 11); |
| // Interoperability tag |
| int[] interopAllowedIfds = { |
| IfdId.TYPE_IFD_INTEROPERABILITY |
| }; |
| int interopFlags = getFlagsFromAllowedIfds(interopAllowedIfds) << 24; |
| mTagInfo.put(TAG_INTEROPERABILITY_INDEX, interopFlags | ExifTag.TYPE_ASCII << 16 |
| | ExifTag.SIZE_UNDEFINED); |
| } |
| |
| protected static int getAllowedIfdFlagsFromInfo(int info) { |
| return info >>> 24; |
| } |
| |
| protected static int[] getAllowedIfdsFromInfo(int info) { |
| int ifdFlags = getAllowedIfdFlagsFromInfo(info); |
| int[] ifds = IfdData.getIfds(); |
| ArrayList<Integer> l = new ArrayList<Integer>(); |
| for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) { |
| int flag = (ifdFlags >> i) & 1; |
| if (flag == 1) { |
| l.add(ifds[i]); |
| } |
| } |
| if (l.size() <= 0) { |
| return null; |
| } |
| int[] ret = new int[l.size()]; |
| int j = 0; |
| for (int i : l) { |
| ret[j++] = i; |
| } |
| return ret; |
| } |
| |
| protected static boolean isIfdAllowed(int info, int ifd) { |
| int[] ifds = IfdData.getIfds(); |
| int ifdFlags = getAllowedIfdFlagsFromInfo(info); |
| for (int i = 0; i < ifds.length; i++) { |
| if (ifd == ifds[i] && ((ifdFlags >> i) & 1) == 1) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| protected static int getFlagsFromAllowedIfds(int[] allowedIfds) { |
| if (allowedIfds == null || allowedIfds.length == 0) { |
| return 0; |
| } |
| int flags = 0; |
| int[] ifds = IfdData.getIfds(); |
| for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) { |
| for (int j : allowedIfds) { |
| if (ifds[i] == j) { |
| flags |= 1 << i; |
| break; |
| } |
| } |
| } |
| return flags; |
| } |
| |
| protected static short getTypeFromInfo(int info) { |
| return (short) ((info >> 16) & 0x0ff); |
| } |
| |
| protected static int getComponentCountFromInfo(int info) { |
| return info & 0x0ffff; |
| } |
| |
| } |