1bcf4a0dae04a4ad14287eeb34069a97c96fe9bb1Sam Juddpackage com.bumptech.glide.load.resource.bitmap; 2d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 3f7a6d65cf7c1a41908dd48e0dab68ee5b881387eSam Juddimport static com.bumptech.glide.load.resource.bitmap.ImageHeaderParser.ImageType.GIF; 4f7a6d65cf7c1a41908dd48e0dab68ee5b881387eSam Juddimport static com.bumptech.glide.load.resource.bitmap.ImageHeaderParser.ImageType.JPEG; 5f7a6d65cf7c1a41908dd48e0dab68ee5b881387eSam Juddimport static com.bumptech.glide.load.resource.bitmap.ImageHeaderParser.ImageType.PNG; 6f7a6d65cf7c1a41908dd48e0dab68ee5b881387eSam Juddimport static com.bumptech.glide.load.resource.bitmap.ImageHeaderParser.ImageType.PNG_A; 7f7a6d65cf7c1a41908dd48e0dab68ee5b881387eSam Juddimport static com.bumptech.glide.load.resource.bitmap.ImageHeaderParser.ImageType.UNKNOWN; 8f7a6d65cf7c1a41908dd48e0dab68ee5b881387eSam Judd 9e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Juddimport android.util.Log; 10d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 11d7efd9c46ebd8872640bb4b4efb564968905507dSam Juddimport java.io.IOException; 12d7efd9c46ebd8872640bb4b4efb564968905507dSam Juddimport java.io.InputStream; 1391289b40e72aee1f4cfce66e536a141e91b39c50Sam Juddimport java.io.UnsupportedEncodingException; 14d7efd9c46ebd8872640bb4b4efb564968905507dSam Juddimport java.nio.ByteBuffer; 15d7efd9c46ebd8872640bb4b4efb564968905507dSam Juddimport java.nio.ByteOrder; 16d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 17d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd/** 185f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * A class for parsing the exif orientation and other data from an image header. 19d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd */ 20f243e2db2fc16ab84ec68995520544f6a469b90dSam Juddpublic class ImageHeaderParser { 21e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd private static final String TAG = "ImageHeaderParser"; 222310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis 23b5419dc08eb0a0f82821d774435720e5a31bc936Sam Judd /** 24b5419dc08eb0a0f82821d774435720e5a31bc936Sam Judd * The format of the image data including whether or not the image may include transparent pixels. 25b5419dc08eb0a0f82821d774435720e5a31bc936Sam Judd */ 262310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis public static enum ImageType { 27fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** GIF type. */ 28c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis GIF(true), 29fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** JPG type. */ 30c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis JPEG(false), 31fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** PNG type with alpha. */ 32c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis PNG_A(true), 33fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** PNG type without alpha. */ 34c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis PNG(false), 35fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** Unrecognized type. */ 36c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis UNKNOWN(false); 372310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis private final boolean hasAlpha; 382310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis 392310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis ImageType(boolean hasAlpha) { 402310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis this.hasAlpha = hasAlpha; 412310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis } 422310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis 432310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis public boolean hasAlpha() { 442310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis return hasAlpha; 452310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis } 462310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis } 472310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis 48525d50359e27ca73eeeba96a994155c684d05292Sam Judd private static final int GIF_HEADER = 0x474946; 49525d50359e27ca73eeeba96a994155c684d05292Sam Judd private static final int PNG_HEADER = 0x89504E47; 50d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd private static final int EXIF_MAGIC_NUMBER = 0xFFD8; 51fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // "MM". 52fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd private static final int MOTOROLA_TIFF_MAGIC_NUMBER = 0x4D4D; 53fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // "II". 54fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd private static final int INTEL_TIFF_MAGIC_NUMBER = 0x4949; 55d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd private static final String JPEG_EXIF_SEGMENT_PREAMBLE = "Exif\0\0"; 5691289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd private static final byte[] JPEG_EXIF_SEGMENT_PREAMBLE_BYTES; 575ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd private static final int SEGMENT_SOS = 0xDA; 585ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd private static final int MARKER_EOI = 0xD9; 595ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd private static final int SEGMENT_START_ID = 0xFF; 605ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd private static final int EXIF_SEGMENT_TYPE = 0xE1; 615ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd private static final int ORIENTATION_TAG_TYPE = 0x0112; 625ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd private static final int[] BYTES_PER_FORMAT = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8 }; 635ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd 645ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd private final StreamReader streamReader; 6591289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd 6691289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd static { 6791289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd byte[] bytes = new byte[0]; 6891289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd try { 6991289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd bytes = JPEG_EXIF_SEGMENT_PREAMBLE.getBytes("UTF-8"); 7091289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd } catch (UnsupportedEncodingException e) { 7191289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd // Ignore. 7291289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd } 7391289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd JPEG_EXIF_SEGMENT_PREAMBLE_BYTES = bytes; 7491289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd } 75d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 76f243e2db2fc16ab84ec68995520544f6a469b90dSam Judd public ImageHeaderParser(InputStream is) { 77d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd streamReader = new StreamReader(is); 78d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 79d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 80525d50359e27ca73eeeba96a994155c684d05292Sam Judd // 0xD0A3C68 -> <htm 81525d50359e27ca73eeeba96a994155c684d05292Sam Judd // 0xCAFEBABE -> <!DOCTYPE... 82525d50359e27ca73eeeba96a994155c684d05292Sam Judd public boolean hasAlpha() throws IOException { 832310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis return getType().hasAlpha(); 842310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis } 852310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis 862310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis public ImageType getType() throws IOException { 87525d50359e27ca73eeeba96a994155c684d05292Sam Judd int firstByte = streamReader.getUInt8(); 88525d50359e27ca73eeeba96a994155c684d05292Sam Judd 89fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // JPEG. 90fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd if (firstByte == EXIF_MAGIC_NUMBER >> 8) { 91c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis return JPEG; 92525d50359e27ca73eeeba96a994155c684d05292Sam Judd } 93525d50359e27ca73eeeba96a994155c684d05292Sam Judd 94525d50359e27ca73eeeba96a994155c684d05292Sam Judd final int firstTwoBytes = firstByte << 8 & 0xFF00 | streamReader.getUInt8() & 0xFF; 95525d50359e27ca73eeeba96a994155c684d05292Sam Judd final int firstFourBytes = firstTwoBytes << 16 & 0xFFFF0000 | streamReader.getUInt16() & 0xFFFF; 96fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // PNG. 97fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd if (firstFourBytes == PNG_HEADER) { 98fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // See: http://stackoverflow.com/questions/2057923/how-to-check-a-png-for-grayscale-alpha-color-type 99525d50359e27ca73eeeba96a994155c684d05292Sam Judd streamReader.skip(25 - 4); 100525d50359e27ca73eeeba96a994155c684d05292Sam Judd int alpha = streamReader.getByte(); 10140c6d302321db0784619297ddb41a48a119ba899Carlos Sobrinho // A RGB indexed PNG can also have transparency. Better safe than sorry! 102c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis return alpha >= 3 ? PNG_A : PNG; 103525d50359e27ca73eeeba96a994155c684d05292Sam Judd } 104525d50359e27ca73eeeba96a994155c684d05292Sam Judd 105fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // GIF from first 3 bytes. 106fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd if (firstFourBytes >> 8 == GIF_HEADER) { 107c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis return GIF; 108525d50359e27ca73eeeba96a994155c684d05292Sam Judd } 109525d50359e27ca73eeeba96a994155c684d05292Sam Judd 110c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis return UNKNOWN; 111525d50359e27ca73eeeba96a994155c684d05292Sam Judd } 112525d50359e27ca73eeeba96a994155c684d05292Sam Judd 113d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd /** 114d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd * Parse the orientation from the image header. If it doesn't handle this image type (or this is not an image) 115d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd * it will return a default value rather than throwing an exception. 116d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd * 117d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd * @return The exif orientation if present or -1 if the header couldn't be parsed or doesn't contain an orientation 118d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd * @throws IOException 119d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd */ 120d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd public int getOrientation() throws IOException { 121d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd final int magicNumber = streamReader.getUInt16(); 122d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 123d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd if (!handles(magicNumber)) { 124d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return -1; 125d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } else { 126d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd byte[] exifData = getExifSegment(); 12791289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd boolean hasJpegExifPreamble = exifData != null 12891289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd && exifData.length >= JPEG_EXIF_SEGMENT_PREAMBLE_BYTES.length; 12991289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd 13091289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd if (hasJpegExifPreamble) { 13191289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd for (int i = 0; i < JPEG_EXIF_SEGMENT_PREAMBLE_BYTES.length; i++) { 13291289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd if (exifData[i] != JPEG_EXIF_SEGMENT_PREAMBLE_BYTES[i]) { 13391289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd hasJpegExifPreamble = false; 13491289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd break; 13591289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd } 13691289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd } 13791289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd } 13891289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd 13991289b40e72aee1f4cfce66e536a141e91b39c50Sam Judd if (hasJpegExifPreamble) { 140d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return parseExifSegment(new RandomAccessReader(exifData)); 141d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } else { 142d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return -1; 143d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 144d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 145d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 146d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 147d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd private byte[] getExifSegment() throws IOException { 148d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd short segmentId, segmentType; 149d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd int segmentLength; 150d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd while (true) { 151d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd segmentId = streamReader.getUInt8(); 152d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 153d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd if (segmentId != SEGMENT_START_ID) { 154e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 155e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd Log.d(TAG, "Unknown segmentId=" + segmentId); 156e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd } 157d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return null; 158d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 159d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 160d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd segmentType = streamReader.getUInt8(); 161d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 162d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd if (segmentType == SEGMENT_SOS) { 163d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return null; 164d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } else if (segmentType == MARKER_EOI) { 165e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 166e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd Log.d(TAG, "Found MARKER_EOI in exif segment"); 167e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd } 168d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return null; 169d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 170d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 171fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Segment length includes bytes for segment length. 172fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd segmentLength = streamReader.getUInt16() - 2; 173d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 174d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd if (segmentType != EXIF_SEGMENT_TYPE) { 175d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd if (segmentLength != streamReader.skip(segmentLength)) { 176e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 177e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd Log.d(TAG, "Unable to skip enough data for type=" + segmentType); 178e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd } 179d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return null; 180d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 181d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } else { 182d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd byte[] segmentData = new byte[segmentLength]; 183d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 184d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd if (segmentLength != streamReader.read(segmentData)) { 185e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 186e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd Log.d(TAG, "Unable to read segment data for type=" + segmentType + " length=" + segmentLength); 187e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd } 188d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return null; 189d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } else { 190d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return segmentData; 191d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 192d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 193d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 194d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 195d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 196ead25618fdd9d1befa76a2ea1dbd66f9741c9af6Robert Papp private static int parseExifSegment(RandomAccessReader segmentData) { 197d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd final int headerOffsetSize = JPEG_EXIF_SEGMENT_PREAMBLE.length(); 198d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 199d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd short byteOrderIdentifier = segmentData.getInt16(headerOffsetSize); 200d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd final ByteOrder byteOrder; 201fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd if (byteOrderIdentifier == MOTOROLA_TIFF_MAGIC_NUMBER) { 202d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd byteOrder = ByteOrder.BIG_ENDIAN; 203d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } else if (byteOrderIdentifier == INTEL_TIFF_MAGIC_NUMBER) { 204d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd byteOrder = ByteOrder.LITTLE_ENDIAN; 205d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } else { 206e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 207e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd Log.d(TAG, "Unknown endianness = " + byteOrderIdentifier); 208e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd } 209d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd byteOrder = ByteOrder.BIG_ENDIAN; 210d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 211d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 212d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd segmentData.order(byteOrder); 213d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 214d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd int firstIfdOffset = segmentData.getInt32(headerOffsetSize + 4) + headerOffsetSize; 215d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd int tagCount = segmentData.getInt16(firstIfdOffset); 216d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 217d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd int tagOffset, tagType, formatCode, componentCount; 218d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd for (int i = 0; i < tagCount; i++) { 219d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd tagOffset = calcTagOffset(firstIfdOffset, i); 220d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 221d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd tagType = segmentData.getInt16(tagOffset); 222d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 223fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // We only want orientation. 224fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd if (tagType != ORIENTATION_TAG_TYPE) { 225d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd continue; 226d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 227d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 228d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd formatCode = segmentData.getInt16(tagOffset + 2); 229d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 230fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // 12 is max format code. 231fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd if (formatCode < 1 || formatCode > 12) { 232e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 233e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd Log.d(TAG, "Got invalid format code = " + formatCode); 234e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd } 235d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd continue; 236d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 237d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 238d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd componentCount = segmentData.getInt32(tagOffset + 4); 239d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 240d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd if (componentCount < 0) { 241e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 242e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd Log.d(TAG, "Negative tiff component count"); 243e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd } 244d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd continue; 245d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 246d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 247e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 248e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd Log.d(TAG, "Got tagIndex=" + i + " tagType=" + tagType + " formatCode =" + formatCode 249e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd + " componentCount=" + componentCount); 250e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd } 251d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 252d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd final int byteCount = componentCount + BYTES_PER_FORMAT[formatCode]; 253d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 254d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd if (byteCount > 4) { 255e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 256e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd Log.d(TAG, "Got byte count > 4, not orientation, continuing, formatCode=" + formatCode); 257e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd } 258d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd continue; 259d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 260d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 261d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd final int tagValueOffset = tagOffset + 8; 262d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 263d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd if (tagValueOffset < 0 || tagValueOffset > segmentData.length()) { 264e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 265e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd Log.d(TAG, "Illegal tagValueOffset=" + tagValueOffset + " tagType=" + tagType); 266e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd } 267d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd continue; 268d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 269d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 270d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd if (byteCount < 0 || tagValueOffset + byteCount > segmentData.length()) { 271e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd if (Log.isLoggable(TAG, Log.DEBUG)) { 272e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd Log.d(TAG, "Illegal number of bytes for TI tag data tagType=" + tagType); 273e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd } 274d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd continue; 275d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 276d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 277d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd //assume componentCount == 1 && fmtCode == 3 278d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return segmentData.getInt16(tagValueOffset); 279d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 280d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 281d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return -1; 282d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 283d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 284d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd private static int calcTagOffset(int ifdOffset, int tagIndex) { 2855ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd return ifdOffset + 2 + 12 * tagIndex; 286d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 287d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 288ead25618fdd9d1befa76a2ea1dbd66f9741c9af6Robert Papp private static boolean handles(int imageMagicNumber) { 289fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd return (imageMagicNumber & EXIF_MAGIC_NUMBER) == EXIF_MAGIC_NUMBER 290fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd || imageMagicNumber == MOTOROLA_TIFF_MAGIC_NUMBER 291fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd || imageMagicNumber == INTEL_TIFF_MAGIC_NUMBER; 292d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 293d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 294d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd private static class RandomAccessReader { 295d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd private final ByteBuffer data; 296d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 297d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd public RandomAccessReader(byte[] data) { 298d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd this.data = ByteBuffer.wrap(data); 299d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd this.data.order(ByteOrder.BIG_ENDIAN); 300d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 301d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 302d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd public void order(ByteOrder byteOrder) { 303d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd this.data.order(byteOrder); 304d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 305d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 306d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd public int length() { 307d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return data.array().length; 308d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 309d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 310d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd public int getInt32(int offset) { 311d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return data.getInt(offset); 312d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 313d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 314d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd public short getInt16(int offset) { 315d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return data.getShort(offset); 316d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 317d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 318d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 319d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd private static class StreamReader { 320d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd private final InputStream is; 321d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd //motorola / big endian byte order 322d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 323d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd public StreamReader(InputStream is) { 324d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd this.is = is; 325d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 326d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 327d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd public int getUInt16() throws IOException { 328d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return (is.read() << 8 & 0xFF00) | (is.read() & 0xFF); 329d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 330d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 331d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd public short getUInt8() throws IOException { 332d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return (short) (is.read() & 0xFF); 333d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 334d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 335d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd public long skip(long total) throws IOException { 336d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return is.skip(total); 337d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 338d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 339d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd public int read(byte[] buffer) throws IOException { 340d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd return is.read(buffer); 341d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 342525d50359e27ca73eeeba96a994155c684d05292Sam Judd 343525d50359e27ca73eeeba96a994155c684d05292Sam Judd public int getByte() throws IOException { 344525d50359e27ca73eeeba96a994155c684d05292Sam Judd return is.read(); 345525d50359e27ca73eeeba96a994155c684d05292Sam Judd } 346d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd } 347d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd} 348d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd 349