Exif.java revision c631b5a4b1f19f84a70b772bc879fae7c92fd4a8
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.ex.photo.util; 18 19import android.util.Log; 20 21import java.io.ByteArrayInputStream; 22import java.io.InputStream; 23 24public class Exif { 25 private static final String TAG = "CameraExif"; 26 27 /** 28 * Returns the degrees in clockwise. Values are 0, 90, 180, or 270. 29 * @param inputStream The input stream will not be closed for you. 30 * @param byteSize Recommended parameter declaring the length of the input stream. If you 31 * pass in -1, we will have to read more from the input stream. 32 * @return 0, 90, 180, or 270. 33 */ 34 public static int getOrientation(final InputStream inputStream, final long byteSize) { 35 if (inputStream == null) { 36 return 0; 37 } 38 39 /* 40 Looking at this algorithm, we never look ahead more than 8 bytes. As long as we call 41 advanceTo() at the end of every loop, we should never have to reallocate a larger buffer. 42 43 Also, the most we ever read backwards is 4 bytes. pack() reads backwards if the encoding 44 is in little endian format. These following two lines potentially reads 4 bytes backwards: 45 46 int tag = pack(jpeg, offset, 4, false); 47 count = pack(jpeg, offset - 2, 2, littleEndian); 48 49 To be safe, we will always advance to some index-4, so we'll need 4 more for the +8 50 look ahead, which makes it a +12 look ahead total. Use 16 just in case my analysis is off. 51 52 This means we only need to allocate a single 16 byte buffer. 53 54 Note: If you do not pass in byteSize parameter, a single large allocation will occur. 55 For a 1MB image, I see one 30KB allocation. This is due to the line containing: 56 57 has(jpeg, byteSize, offset + length - 1) 58 59 where length is a variable int (around 30KB above) read from the EXIF headers. 60 61 This is still much better than allocating a 1MB byte[] which we were doing before. 62 */ 63 64 final int lookAhead = 16; 65 final int readBackwards = 4; 66 final InputStreamBuffer jpeg = new InputStreamBuffer(inputStream, lookAhead, false); 67 68 int offset = 0; 69 int length = 0; 70 71 // ISO/IEC 10918-1:1993(E) 72 while (has(jpeg, byteSize, offset + 3) && (jpeg.get(offset++) & 0xFF) == 0xFF) { 73 final int marker = jpeg.get(offset) & 0xFF; 74 75 // Check if the marker is a padding. 76 if (marker == 0xFF) { 77 continue; 78 } 79 offset++; 80 81 // Check if the marker is SOI or TEM. 82 if (marker == 0xD8 || marker == 0x01) { 83 continue; 84 } 85 // Check if the marker is EOI or SOS. 86 if (marker == 0xD9 || marker == 0xDA) { 87 // Loop ends. 88 jpeg.advanceTo(offset - readBackwards); 89 break; 90 } 91 92 // Get the length and check if it is reasonable. 93 length = pack(jpeg, offset, 2, false); 94 if (length < 2 || !has(jpeg, byteSize, offset + length - 1)) { 95 Log.e(TAG, "Invalid length"); 96 return 0; 97 } 98 99 // Break if the marker is EXIF in APP1. 100 if (marker == 0xE1 && length >= 8 && 101 pack(jpeg, offset + 2, 4, false) == 0x45786966 && 102 pack(jpeg, offset + 6, 2, false) == 0) { 103 offset += 8; 104 length -= 8; 105 // Loop ends. 106 jpeg.advanceTo(offset - readBackwards); 107 break; 108 } 109 110 // Skip other markers. 111 offset += length; 112 length = 0; 113 114 // Loop ends. 115 jpeg.advanceTo(offset - readBackwards); 116 } 117 118 // JEITA CP-3451 Exif Version 2.2 119 if (length > 8) { 120 // Identify the byte order. 121 int tag = pack(jpeg, offset, 4, false); 122 if (tag != 0x49492A00 && tag != 0x4D4D002A) { 123 Log.e(TAG, "Invalid byte order"); 124 return 0; 125 } 126 final boolean littleEndian = (tag == 0x49492A00); 127 128 // Get the offset and check if it is reasonable. 129 int count = pack(jpeg, offset + 4, 4, littleEndian) + 2; 130 if (count < 10 || count > length) { 131 Log.e(TAG, "Invalid offset"); 132 return 0; 133 } 134 offset += count; 135 length -= count; 136 137 // Offset has changed significantly. 138 jpeg.advanceTo(offset - readBackwards); 139 140 // Get the count and go through all the elements. 141 count = pack(jpeg, offset - 2, 2, littleEndian); 142 143 while (count-- > 0 && length >= 12) { 144 // Get the tag and check if it is orientation. 145 tag = pack(jpeg, offset, 2, littleEndian); 146 if (tag == 0x0112) { 147 // We do not really care about type and count, do we? 148 final int orientation = pack(jpeg, offset + 8, 2, littleEndian); 149 switch (orientation) { 150 case 1: 151 return 0; 152 case 3: 153 return 180; 154 case 6: 155 return 90; 156 case 8: 157 return 270; 158 } 159 Log.i(TAG, "Unsupported orientation"); 160 return 0; 161 } 162 offset += 12; 163 length -= 12; 164 165 // Loop ends. 166 jpeg.advanceTo(offset - readBackwards); 167 } 168 } 169 170 Log.i(TAG, "Orientation not found"); 171 return 0; 172 } 173 174 private static int pack(final InputStreamBuffer bytes, int offset, int length, 175 final boolean littleEndian) { 176 int step = 1; 177 if (littleEndian) { 178 offset += length - 1; 179 step = -1; 180 } 181 182 int value = 0; 183 while (length-- > 0) { 184 value = (value << 8) | (bytes.get(offset) & 0xFF); 185 offset += step; 186 } 187 return value; 188 } 189 190 private static boolean has(final InputStreamBuffer jpeg, final long byteSize, final int index) { 191 if (byteSize >= 0) { 192 return index < byteSize; 193 } else { 194 // For large values of index, this will cause the internal buffer to resize. 195 return jpeg.has(index); 196 } 197 } 198 199 @Deprecated 200 public static int getOrientation(final byte[] jpeg) { 201 return getOrientation(new ByteArrayInputStream(jpeg), jpeg.length); 202 } 203} 204