blob: 3e0cd553f925573b4f07fdc57eda1a9a108b9ff5 [file] [log] [blame]
Hung-ying Tyan2523f432012-10-19 20:50:50 +08001/*
2 * Copyright (C) 2012 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
ztenghuia16e7b52013-08-23 11:47:56 -070017package com.android.camera.exif;
Hung-ying Tyan2523f432012-10-19 20:50:50 +080018
Angus Kong2bca2102014-03-11 16:27:30 -070019import com.android.camera.debug.Log;
Earl Ou7e92ec72012-10-18 19:13:37 +080020
Hung-ying Tyan2523f432012-10-19 20:50:50 +080021import java.io.IOException;
22import java.io.InputStream;
23import java.nio.ByteOrder;
24import java.nio.charset.Charset;
25import java.util.Map.Entry;
26import java.util.TreeMap;
27
28/**
Ruben Brunkc274ded2013-03-11 19:00:12 -070029 * This class provides a low-level EXIF parsing API. Given a JPEG format
30 * InputStream, the caller can request which IFD's to read via
31 * {@link #parse(InputStream, int)} with given options.
Hung-ying Tyan2523f432012-10-19 20:50:50 +080032 * <p>
Ruben Brunkc274ded2013-03-11 19:00:12 -070033 * Below is an example of getting EXIF data from IFD 0 and EXIF IFD using the
34 * parser.
35 *
Hung-ying Tyan2523f432012-10-19 20:50:50 +080036 * <pre>
37 * void parse() {
38 * ExifParser parser = ExifParser.parse(mImageInputStream,
39 * ExifParser.OPTION_IFD_0 | ExifParser.OPTIONS_IFD_EXIF);
40 * int event = parser.next();
41 * while (event != ExifParser.EVENT_END) {
42 * switch (event) {
43 * case ExifParser.EVENT_START_OF_IFD:
44 * break;
45 * case ExifParser.EVENT_NEW_TAG:
46 * ExifTag tag = parser.getTag();
47 * if (!tag.hasValue()) {
48 * parser.registerForTagValue(tag);
49 * } else {
50 * processTag(tag);
51 * }
52 * break;
53 * case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
54 * tag = parser.getTag();
55 * if (tag.getDataType() != ExifTag.TYPE_UNDEFINED) {
56 * processTag(tag);
57 * }
58 * break;
59 * }
60 * event = parser.next();
61 * }
62 * }
63 *
64 * void processTag(ExifTag tag) {
65 * // process the tag as you like.
66 * }
67 * </pre>
68 */
Ruben Brunkc274ded2013-03-11 19:00:12 -070069class ExifParser {
70 private static final boolean LOGV = false;
Angus Kong2bca2102014-03-11 16:27:30 -070071 private static final Log.Tag TAG = new Log.Tag("ExifParser");
Hung-ying Tyan2523f432012-10-19 20:50:50 +080072 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -070073 * When the parser reaches a new IFD area. Call {@link #getCurrentIfd()} to
74 * know which IFD we are in.
Hung-ying Tyan2523f432012-10-19 20:50:50 +080075 */
76 public static final int EVENT_START_OF_IFD = 0;
77 /**
78 * When the parser reaches a new tag. Call {@link #getTag()}to get the
79 * corresponding tag.
80 */
81 public static final int EVENT_NEW_TAG = 1;
82 /**
83 * When the parser reaches the value area of tag that is registered by
Ruben Brunkc274ded2013-03-11 19:00:12 -070084 * {@link #registerForTagValue(ExifTag)} previously. Call {@link #getTag()}
85 * to get the corresponding tag.
Hung-ying Tyan2523f432012-10-19 20:50:50 +080086 */
87 public static final int EVENT_VALUE_OF_REGISTERED_TAG = 2;
88
89 /**
90 * When the parser reaches the compressed image area.
91 */
92 public static final int EVENT_COMPRESSED_IMAGE = 3;
93 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -070094 * When the parser reaches the uncompressed image strip. Call
95 * {@link #getStripIndex()} to get the index of the strip.
96 *
Hung-ying Tyan2523f432012-10-19 20:50:50 +080097 * @see #getStripIndex()
98 * @see #getStripCount()
99 */
100 public static final int EVENT_UNCOMPRESSED_STRIP = 4;
101 /**
102 * When there is nothing more to parse.
103 */
104 public static final int EVENT_END = 5;
105
106 /**
107 * Option bit to request to parse IFD0.
108 */
109 public static final int OPTION_IFD_0 = 1 << 0;
110 /**
111 * Option bit to request to parse IFD1.
112 */
113 public static final int OPTION_IFD_1 = 1 << 1;
114 /**
115 * Option bit to request to parse Exif-IFD.
116 */
117 public static final int OPTION_IFD_EXIF = 1 << 2;
118 /**
119 * Option bit to request to parse GPS-IFD.
120 */
121 public static final int OPTION_IFD_GPS = 1 << 3;
122 /**
123 * Option bit to request to parse Interoperability-IFD.
124 */
125 public static final int OPTION_IFD_INTEROPERABILITY = 1 << 4;
126 /**
127 * Option bit to request to parse thumbnail.
128 */
129 public static final int OPTION_THUMBNAIL = 1 << 5;
130
Ruben Brunkc274ded2013-03-11 19:00:12 -0700131 protected static final int EXIF_HEADER = 0x45786966; // EXIF header "Exif"
132 protected static final short EXIF_HEADER_TAIL = (short) 0x0000; // EXIF header in APP1
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800133
134 // TIFF header
Ruben Brunkc274ded2013-03-11 19:00:12 -0700135 protected static final short LITTLE_ENDIAN_TAG = (short) 0x4949; // "II"
136 protected static final short BIG_ENDIAN_TAG = (short) 0x4d4d; // "MM"
137 protected static final short TIFF_HEADER_TAIL = 0x002A;
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800138
Ruben Brunkc274ded2013-03-11 19:00:12 -0700139 protected static final int TAG_SIZE = 12;
140 protected static final int OFFSET_SIZE = 2;
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800141
Earl Ouff0f96c2012-10-19 16:50:34 +0800142 private static final Charset US_ASCII = Charset.forName("US-ASCII");
143
Ruben Brunkc274ded2013-03-11 19:00:12 -0700144 protected static final int DEFAULT_IFD0_OFFSET = 8;
Earl Ou04903342012-10-19 23:27:44 +0800145
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800146 private final CountedDataInputStream mTiffStream;
147 private final int mOptions;
148 private int mIfdStartOffset = 0;
149 private int mNumOfTagInIfd = 0;
150 private int mIfdType;
151 private ExifTag mTag;
152 private ImageEvent mImageEvent;
153 private int mStripCount;
154 private ExifTag mStripSizeTag;
155 private ExifTag mJpegSizeTag;
156 private boolean mNeedToParseOffsetsInCurrentIfd;
157 private boolean mContainExifData = false;
Earl Ou04903342012-10-19 23:27:44 +0800158 private int mApp1End;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700159 private int mOffsetToApp1EndFromSOF = 0;
Earl Ou04903342012-10-19 23:27:44 +0800160 private byte[] mDataAboveIfd0;
161 private int mIfd0Position;
Earl Ouc06c8042012-11-28 13:58:30 +0800162 private int mTiffStartPosition;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700163 private final ExifInterface mInterface;
164
165 private static final short TAG_EXIF_IFD = ExifInterface
166 .getTrueTagKey(ExifInterface.TAG_EXIF_IFD);
167 private static final short TAG_GPS_IFD = ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD);
168 private static final short TAG_INTEROPERABILITY_IFD = ExifInterface
169 .getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD);
170 private static final short TAG_JPEG_INTERCHANGE_FORMAT = ExifInterface
171 .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
172 private static final short TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = ExifInterface
173 .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
174 private static final short TAG_STRIP_OFFSETS = ExifInterface
175 .getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS);
176 private static final short TAG_STRIP_BYTE_COUNTS = ExifInterface
177 .getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800178
179 private final TreeMap<Integer, Object> mCorrespondingEvent = new TreeMap<Integer, Object>();
180
181 private boolean isIfdRequested(int ifdType) {
182 switch (ifdType) {
183 case IfdId.TYPE_IFD_0:
184 return (mOptions & OPTION_IFD_0) != 0;
185 case IfdId.TYPE_IFD_1:
186 return (mOptions & OPTION_IFD_1) != 0;
187 case IfdId.TYPE_IFD_EXIF:
188 return (mOptions & OPTION_IFD_EXIF) != 0;
189 case IfdId.TYPE_IFD_GPS:
190 return (mOptions & OPTION_IFD_GPS) != 0;
191 case IfdId.TYPE_IFD_INTEROPERABILITY:
192 return (mOptions & OPTION_IFD_INTEROPERABILITY) != 0;
193 }
194 return false;
195 }
196
197 private boolean isThumbnailRequested() {
198 return (mOptions & OPTION_THUMBNAIL) != 0;
199 }
200
Ruben Brunkc274ded2013-03-11 19:00:12 -0700201 private ExifParser(InputStream inputStream, int options, ExifInterface iRef)
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800202 throws IOException, ExifInvalidFormatException {
Ruben Brunkec965b62013-01-15 17:23:40 -0800203 if (inputStream == null) {
204 throw new IOException("Null argument inputStream to ExifParser");
205 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700206 if (LOGV) {
207 Log.v(TAG, "Reading exif...");
208 }
209 mInterface = iRef;
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800210 mContainExifData = seekTiffData(inputStream);
211 mTiffStream = new CountedDataInputStream(inputStream);
212 mOptions = options;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700213 if (!mContainExifData) {
214 return;
215 }
Earl Ou04903342012-10-19 23:27:44 +0800216
217 parseTiffHeader();
218 long offset = mTiffStream.readUnsignedInt();
219 if (offset > Integer.MAX_VALUE) {
220 throw new ExifInvalidFormatException("Invalid offset " + offset);
221 }
222 mIfd0Position = (int) offset;
Earl Ouc06c8042012-11-28 13:58:30 +0800223 mIfdType = IfdId.TYPE_IFD_0;
224 if (isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
225 registerIfd(IfdId.TYPE_IFD_0, offset);
226 if (offset != DEFAULT_IFD0_OFFSET) {
227 mDataAboveIfd0 = new byte[(int) offset - DEFAULT_IFD0_OFFSET];
228 read(mDataAboveIfd0);
229 }
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800230 }
231 }
232
233 /**
234 * Parses the the given InputStream with the given options
Ruben Brunkc274ded2013-03-11 19:00:12 -0700235 *
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800236 * @exception IOException
237 * @exception ExifInvalidFormatException
238 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700239 protected static ExifParser parse(InputStream inputStream, int options, ExifInterface iRef)
Earl Ouc06c8042012-11-28 13:58:30 +0800240 throws IOException, ExifInvalidFormatException {
Ruben Brunkc274ded2013-03-11 19:00:12 -0700241 return new ExifParser(inputStream, options, iRef);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800242 }
243
244 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700245 * Parses the the given InputStream with default options; that is, every IFD
246 * and thumbnaill will be parsed.
247 *
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800248 * @exception IOException
249 * @exception ExifInvalidFormatException
250 * @see #parse(InputStream, int)
251 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700252 protected static ExifParser parse(InputStream inputStream, ExifInterface iRef)
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800253 throws IOException, ExifInvalidFormatException {
254 return new ExifParser(inputStream, OPTION_IFD_0 | OPTION_IFD_1
255 | OPTION_IFD_EXIF | OPTION_IFD_GPS | OPTION_IFD_INTEROPERABILITY
Ruben Brunkc274ded2013-03-11 19:00:12 -0700256 | OPTION_THUMBNAIL, iRef);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800257 }
258
259 /**
260 * Moves the parser forward and returns the next parsing event
261 *
262 * @exception IOException
263 * @exception ExifInvalidFormatException
264 * @see #EVENT_START_OF_IFD
265 * @see #EVENT_NEW_TAG
266 * @see #EVENT_VALUE_OF_REGISTERED_TAG
267 * @see #EVENT_COMPRESSED_IMAGE
268 * @see #EVENT_UNCOMPRESSED_STRIP
269 * @see #EVENT_END
270 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700271 protected int next() throws IOException, ExifInvalidFormatException {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800272 if (!mContainExifData) {
273 return EVENT_END;
274 }
275 int offset = mTiffStream.getReadByteCount();
276 int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
277 if (offset < endOfTags) {
278 mTag = readTag();
Earl Ouc57d5a62012-10-19 18:30:01 +0800279 if (mTag == null) {
280 return next();
281 }
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800282 if (mNeedToParseOffsetsInCurrentIfd) {
283 checkOffsetOrImageTag(mTag);
284 }
285 return EVENT_NEW_TAG;
286 } else if (offset == endOfTags) {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800287 // There is a link to ifd1 at the end of ifd0
288 if (mIfdType == IfdId.TYPE_IFD_0) {
Earl Ou7e92ec72012-10-18 19:13:37 +0800289 long ifdOffset = readUnsignedLong();
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800290 if (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested()) {
291 if (ifdOffset != 0) {
292 registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
293 }
294 }
295 } else {
Earl Ou7e92ec72012-10-18 19:13:37 +0800296 int offsetSize = 4;
297 // Some camera models use invalid length of the offset
298 if (mCorrespondingEvent.size() > 0) {
299 offsetSize = mCorrespondingEvent.firstEntry().getKey() -
300 mTiffStream.getReadByteCount();
301 }
302 if (offsetSize < 4) {
303 Log.w(TAG, "Invalid size of link to next IFD: " + offsetSize);
304 } else {
305 long ifdOffset = readUnsignedLong();
306 if (ifdOffset != 0) {
307 Log.w(TAG, "Invalid link to next IFD: " + ifdOffset);
308 }
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800309 }
310 }
311 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700312 while (mCorrespondingEvent.size() != 0) {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800313 Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
314 Object event = entry.getValue();
Earl Ou04903342012-10-19 23:27:44 +0800315 try {
316 skipTo(entry.getKey());
317 } catch (IOException e) {
318 Log.w(TAG, "Failed to skip to data at: " + entry.getKey() +
319 " for " + event.getClass().getName() + ", the file may be broken.");
320 continue;
321 }
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800322 if (event instanceof IfdEvent) {
323 mIfdType = ((IfdEvent) event).ifd;
324 mNumOfTagInIfd = mTiffStream.readUnsignedShort();
325 mIfdStartOffset = entry.getKey();
Earl Ou04903342012-10-19 23:27:44 +0800326
327 if (mNumOfTagInIfd * TAG_SIZE + mIfdStartOffset + OFFSET_SIZE > mApp1End) {
328 Log.w(TAG, "Invalid size of IFD " + mIfdType);
329 return EVENT_END;
330 }
331
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800332 mNeedToParseOffsetsInCurrentIfd = needToParseOffsetsInCurrentIfd();
333 if (((IfdEvent) event).isRequested) {
334 return EVENT_START_OF_IFD;
335 } else {
336 skipRemainingTagsInCurrentIfd();
337 }
338 } else if (event instanceof ImageEvent) {
339 mImageEvent = (ImageEvent) event;
340 return mImageEvent.type;
341 } else {
342 ExifTagEvent tagEvent = (ExifTagEvent) event;
343 mTag = tagEvent.tag;
344 if (mTag.getDataType() != ExifTag.TYPE_UNDEFINED) {
345 readFullTagValue(mTag);
346 checkOffsetOrImageTag(mTag);
347 }
348 if (tagEvent.isRequested) {
349 return EVENT_VALUE_OF_REGISTERED_TAG;
350 }
351 }
352 }
353 return EVENT_END;
354 }
355
356 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700357 * Skips the tags area of current IFD, if the parser is not in the tag area,
358 * nothing will happen.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800359 *
360 * @throws IOException
361 * @throws ExifInvalidFormatException
362 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700363 protected void skipRemainingTagsInCurrentIfd() throws IOException, ExifInvalidFormatException {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800364 int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
365 int offset = mTiffStream.getReadByteCount();
Ruben Brunkc274ded2013-03-11 19:00:12 -0700366 if (offset > endOfTags) {
367 return;
368 }
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800369 if (mNeedToParseOffsetsInCurrentIfd) {
370 while (offset < endOfTags) {
371 mTag = readTag();
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800372 offset += TAG_SIZE;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700373 if (mTag == null) {
374 continue;
375 }
Earl Ouc57d5a62012-10-19 18:30:01 +0800376 checkOffsetOrImageTag(mTag);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800377 }
378 } else {
379 skipTo(endOfTags);
380 }
381 long ifdOffset = readUnsignedLong();
382 // For ifd0, there is a link to ifd1 in the end of all tags
383 if (mIfdType == IfdId.TYPE_IFD_0
384 && (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested())) {
385 if (ifdOffset > 0) {
386 registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
387 }
388 }
389 }
390
391 private boolean needToParseOffsetsInCurrentIfd() {
392 switch (mIfdType) {
393 case IfdId.TYPE_IFD_0:
394 return isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_GPS)
Earl Ouc06c8042012-11-28 13:58:30 +0800395 || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)
396 || isIfdRequested(IfdId.TYPE_IFD_1);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800397 case IfdId.TYPE_IFD_1:
398 return isThumbnailRequested();
399 case IfdId.TYPE_IFD_EXIF:
400 // The offset to interoperability IFD is located in Exif IFD
401 return isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY);
402 default:
403 return false;
404 }
405 }
406
407 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700408 * If {@link #next()} return {@link #EVENT_NEW_TAG} or
409 * {@link #EVENT_VALUE_OF_REGISTERED_TAG}, call this function to get the
410 * corresponding tag.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800411 * <p>
Ruben Brunkc274ded2013-03-11 19:00:12 -0700412 * For {@link #EVENT_NEW_TAG}, the tag may not contain the value if the size
413 * of the value is greater than 4 bytes. One should call
414 * {@link ExifTag#hasValue()} to check if the tag contains value. If there
415 * is no value,call {@link #registerForTagValue(ExifTag)} to have the parser
416 * emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
417 * pointed by the offset.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800418 * <p>
Ruben Brunkc274ded2013-03-11 19:00:12 -0700419 * When {@link #EVENT_VALUE_OF_REGISTERED_TAG} is emitted, the value of the
420 * tag will have already been read except for tags of undefined type. For
421 * tags of undefined type, call one of the read methods to get the value.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800422 *
423 * @see #registerForTagValue(ExifTag)
424 * @see #read(byte[])
425 * @see #read(byte[], int, int)
426 * @see #readLong()
427 * @see #readRational()
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800428 * @see #readString(int)
429 * @see #readString(int, Charset)
430 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700431 protected ExifTag getTag() {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800432 return mTag;
433 }
434
435 /**
436 * Gets number of tags in the current IFD area.
437 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700438 protected int getTagCountInCurrentIfd() {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800439 return mNumOfTagInIfd;
440 }
441
442 /**
443 * Gets the ID of current IFD.
444 *
445 * @see IfdId#TYPE_IFD_0
446 * @see IfdId#TYPE_IFD_1
447 * @see IfdId#TYPE_IFD_GPS
448 * @see IfdId#TYPE_IFD_INTEROPERABILITY
449 * @see IfdId#TYPE_IFD_EXIF
450 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700451 protected int getCurrentIfd() {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800452 return mIfdType;
453 }
454
455 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700456 * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
457 * get the index of this strip.
458 *
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800459 * @see #getStripCount()
460 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700461 protected int getStripIndex() {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800462 return mImageEvent.stripIndex;
463 }
464
465 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700466 * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
467 * get the number of strip data.
468 *
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800469 * @see #getStripIndex()
470 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700471 protected int getStripCount() {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800472 return mStripCount;
473 }
474
475 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700476 * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
477 * get the strip size.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800478 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700479 protected int getStripSize() {
480 if (mStripSizeTag == null)
481 return 0;
Earl Ou994234e2012-11-14 15:30:51 +0800482 return (int) mStripSizeTag.getValueAt(0);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800483 }
484
485 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700486 * When receiving {@link #EVENT_COMPRESSED_IMAGE}, call this function to get
487 * the image data size.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800488 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700489 protected int getCompressedImageSize() {
490 if (mJpegSizeTag == null) {
491 return 0;
492 }
Earl Ou994234e2012-11-14 15:30:51 +0800493 return (int) mJpegSizeTag.getValueAt(0);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800494 }
495
496 private void skipTo(int offset) throws IOException {
497 mTiffStream.skipTo(offset);
498 while (!mCorrespondingEvent.isEmpty() && mCorrespondingEvent.firstKey() < offset) {
499 mCorrespondingEvent.pollFirstEntry();
500 }
501 }
502
503 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700504 * When getting {@link #EVENT_NEW_TAG} in the tag area of IFD, the tag may
505 * not contain the value if the size of the value is greater than 4 bytes.
506 * When the value is not available here, call this method so that the parser
507 * will emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
508 * where the value is located.
509 *
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800510 * @see #EVENT_VALUE_OF_REGISTERED_TAG
511 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700512 protected void registerForTagValue(ExifTag tag) {
Ruben Brunk310ef8d2013-04-25 13:10:53 -0700513 if (tag.getOffset() >= mTiffStream.getReadByteCount()) {
514 mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, true));
515 }
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800516 }
517
518 private void registerIfd(int ifdType, long offset) {
519 // Cast unsigned int to int since the offset is always smaller
520 // than the size of APP1 (65536)
521 mCorrespondingEvent.put((int) offset, new IfdEvent(ifdType, isIfdRequested(ifdType)));
522 }
523
524 private void registerCompressedImage(long offset) {
525 mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_COMPRESSED_IMAGE));
526 }
527
528 private void registerUncompressedStrip(int stripIndex, long offset) {
529 mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_UNCOMPRESSED_STRIP
530 , stripIndex));
531 }
532
533 private ExifTag readTag() throws IOException, ExifInvalidFormatException {
534 short tagId = mTiffStream.readShort();
535 short dataFormat = mTiffStream.readShort();
536 long numOfComp = mTiffStream.readUnsignedInt();
537 if (numOfComp > Integer.MAX_VALUE) {
538 throw new ExifInvalidFormatException(
539 "Number of component is larger then Integer.MAX_VALUE");
540 }
Earl Ouc57d5a62012-10-19 18:30:01 +0800541 // Some invalid image file contains invalid data type. Ignore those tags
542 if (!ExifTag.isValidType(dataFormat)) {
543 Log.w(TAG, String.format("Tag %04x: Invalid data type %d", tagId, dataFormat));
544 mTiffStream.skip(4);
545 return null;
546 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700547 // TODO: handle numOfComp overflow
548 ExifTag tag = new ExifTag(tagId, dataFormat, (int) numOfComp, mIfdType,
549 ((int) numOfComp) != ExifTag.SIZE_UNDEFINED);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800550 int dataSize = tag.getDataSize();
551 if (dataSize > 4) {
552 long offset = mTiffStream.readUnsignedInt();
553 if (offset > Integer.MAX_VALUE) {
554 throw new ExifInvalidFormatException(
555 "offset is larger then Integer.MAX_VALUE");
556 }
Earl Ou04903342012-10-19 23:27:44 +0800557 // Some invalid images put some undefined data before IFD0.
558 // Read the data here.
559 if ((offset < mIfd0Position) && (dataFormat == ExifTag.TYPE_UNDEFINED)) {
560 byte[] buf = new byte[(int) numOfComp];
561 System.arraycopy(mDataAboveIfd0, (int) offset - DEFAULT_IFD0_OFFSET,
562 buf, 0, (int) numOfComp);
563 tag.setValue(buf);
564 } else {
565 tag.setOffset((int) offset);
566 }
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800567 } else {
Ruben Brunk310ef8d2013-04-25 13:10:53 -0700568 boolean defCount = tag.hasDefinedCount();
569 // Set defined count to 0 so we can add \0 to non-terminated strings
570 tag.setHasDefinedCount(false);
571 // Read value
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800572 readFullTagValue(tag);
Ruben Brunk310ef8d2013-04-25 13:10:53 -0700573 tag.setHasDefinedCount(defCount);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800574 mTiffStream.skip(4 - dataSize);
Earl Ouc06c8042012-11-28 13:58:30 +0800575 // Set the offset to the position of value.
576 tag.setOffset(mTiffStream.getReadByteCount() - 4);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800577 }
578 return tag;
579 }
580
581 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700582 * Check the tag, if the tag is one of the offset tag that points to the IFD
583 * or image the caller is interested in, register the IFD or image.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800584 */
585 private void checkOffsetOrImageTag(ExifTag tag) {
Earl Ou04903342012-10-19 23:27:44 +0800586 // Some invalid formattd image contains tag with 0 size.
587 if (tag.getComponentCount() == 0) {
588 return;
589 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700590 short tid = tag.getTagId();
591 int ifd = tag.getIfd();
592 if (tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) {
593 if (isIfdRequested(IfdId.TYPE_IFD_EXIF)
594 || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
595 registerIfd(IfdId.TYPE_IFD_EXIF, tag.getValueAt(0));
596 }
597 } else if (tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) {
598 if (isIfdRequested(IfdId.TYPE_IFD_GPS)) {
599 registerIfd(IfdId.TYPE_IFD_GPS, tag.getValueAt(0));
600 }
601 } else if (tid == TAG_INTEROPERABILITY_IFD
602 && checkAllowed(ifd, ExifInterface.TAG_INTEROPERABILITY_IFD)) {
603 if (isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
604 registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0));
605 }
606 } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT
607 && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) {
608 if (isThumbnailRequested()) {
609 registerCompressedImage(tag.getValueAt(0));
610 }
611 } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
612 && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)) {
613 if (isThumbnailRequested()) {
614 mJpegSizeTag = tag;
615 }
616 } else if (tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) {
617 if (isThumbnailRequested()) {
618 if (tag.hasValue()) {
619 for (int i = 0; i < tag.getComponentCount(); i++) {
620 if (tag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) {
621 registerUncompressedStrip(i, tag.getValueAt(i));
622 } else {
623 registerUncompressedStrip(i, tag.getValueAt(i));
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800624 }
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800625 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700626 } else {
627 mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, false));
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800628 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700629 }
630 } else if (tid == TAG_STRIP_BYTE_COUNTS
631 && checkAllowed(ifd, ExifInterface.TAG_STRIP_BYTE_COUNTS)
632 &&isThumbnailRequested() && tag.hasValue()) {
633 mStripSizeTag = tag;
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800634 }
635 }
636
Ruben Brunkc274ded2013-03-11 19:00:12 -0700637 private boolean checkAllowed(int ifd, int tagId) {
638 int info = mInterface.getTagInfo().get(tagId);
639 if (info == ExifInterface.DEFINITION_NULL) {
640 return false;
641 }
642 return ExifInterface.isIfdAllowed(info, ifd);
643 }
644
645 protected void readFullTagValue(ExifTag tag) throws IOException {
Earl Ou04903342012-10-19 23:27:44 +0800646 // Some invalid images contains tags with wrong size, check it here
647 short type = tag.getDataType();
648 if (type == ExifTag.TYPE_ASCII || type == ExifTag.TYPE_UNDEFINED ||
649 type == ExifTag.TYPE_UNSIGNED_BYTE) {
650 int size = tag.getComponentCount();
651 if (mCorrespondingEvent.size() > 0) {
Ruben Brunkc274ded2013-03-11 19:00:12 -0700652 if (mCorrespondingEvent.firstEntry().getKey() < mTiffStream.getReadByteCount()
653 + size) {
Ruben Brunk310ef8d2013-04-25 13:10:53 -0700654 Object event = mCorrespondingEvent.firstEntry().getValue();
655 if (event instanceof ImageEvent) {
656 // Tag value overlaps thumbnail, ignore thumbnail.
657 Log.w(TAG, "Thumbnail overlaps value for tag: \n" + tag.toString());
Ruben Brunkc274ded2013-03-11 19:00:12 -0700658 Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
Ruben Brunkc274ded2013-03-11 19:00:12 -0700659 Log.w(TAG, "Invalid thumbnail offset: " + entry.getKey());
660 } else {
Ruben Brunk310ef8d2013-04-25 13:10:53 -0700661 // Tag value overlaps another tag, shorten count
662 if (event instanceof IfdEvent) {
663 Log.w(TAG, "Ifd " + ((IfdEvent) event).ifd
664 + " overlaps value for tag: \n" + tag.toString());
665 } else if (event instanceof ExifTagEvent) {
666 Log.w(TAG, "Tag value for tag: \n"
667 + ((ExifTagEvent) event).tag.toString()
668 + " overlaps value for tag: \n" + tag.toString());
669 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700670 size = mCorrespondingEvent.firstEntry().getKey()
671 - mTiffStream.getReadByteCount();
672 Log.w(TAG, "Invalid size of tag: \n" + tag.toString()
673 + " setting count to: " + size);
674 tag.forceSetComponentCount(size);
675 }
Earl Ou04903342012-10-19 23:27:44 +0800676 }
677 }
678 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700679 switch (tag.getDataType()) {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800680 case ExifTag.TYPE_UNSIGNED_BYTE:
Ruben Brunkc274ded2013-03-11 19:00:12 -0700681 case ExifTag.TYPE_UNDEFINED: {
682 byte buf[] = new byte[tag.getComponentCount()];
683 read(buf);
684 tag.setValue(buf);
685 }
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800686 break;
687 case ExifTag.TYPE_ASCII:
688 tag.setValue(readString(tag.getComponentCount()));
689 break;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700690 case ExifTag.TYPE_UNSIGNED_LONG: {
691 long value[] = new long[tag.getComponentCount()];
692 for (int i = 0, n = value.length; i < n; i++) {
693 value[i] = readUnsignedLong();
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800694 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700695 tag.setValue(value);
696 }
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800697 break;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700698 case ExifTag.TYPE_UNSIGNED_RATIONAL: {
699 Rational value[] = new Rational[tag.getComponentCount()];
700 for (int i = 0, n = value.length; i < n; i++) {
701 value[i] = readUnsignedRational();
Earl Ouc06c8042012-11-28 13:58:30 +0800702 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700703 tag.setValue(value);
704 }
Earl Ouc06c8042012-11-28 13:58:30 +0800705 break;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700706 case ExifTag.TYPE_UNSIGNED_SHORT: {
707 int value[] = new int[tag.getComponentCount()];
708 for (int i = 0, n = value.length; i < n; i++) {
709 value[i] = readUnsignedShort();
Earl Ouc06c8042012-11-28 13:58:30 +0800710 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700711 tag.setValue(value);
712 }
Earl Ouc06c8042012-11-28 13:58:30 +0800713 break;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700714 case ExifTag.TYPE_LONG: {
715 int value[] = new int[tag.getComponentCount()];
716 for (int i = 0, n = value.length; i < n; i++) {
717 value[i] = readLong();
Earl Ouc06c8042012-11-28 13:58:30 +0800718 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700719 tag.setValue(value);
720 }
Earl Ouc06c8042012-11-28 13:58:30 +0800721 break;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700722 case ExifTag.TYPE_RATIONAL: {
723 Rational value[] = new Rational[tag.getComponentCount()];
724 for (int i = 0, n = value.length; i < n; i++) {
725 value[i] = readRational();
Earl Ouc06c8042012-11-28 13:58:30 +0800726 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700727 tag.setValue(value);
728 }
Earl Ouc06c8042012-11-28 13:58:30 +0800729 break;
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800730 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700731 if (LOGV) {
732 Log.v(TAG, "\n" + tag.toString());
733 }
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800734 }
735
736 private void parseTiffHeader() throws IOException,
737 ExifInvalidFormatException {
738 short byteOrder = mTiffStream.readShort();
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800739 if (LITTLE_ENDIAN_TAG == byteOrder) {
740 mTiffStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
741 } else if (BIG_ENDIAN_TAG == byteOrder) {
742 mTiffStream.setByteOrder(ByteOrder.BIG_ENDIAN);
743 } else {
744 throw new ExifInvalidFormatException("Invalid TIFF header");
745 }
746
747 if (mTiffStream.readShort() != TIFF_HEADER_TAIL) {
748 throw new ExifInvalidFormatException("Invalid TIFF header");
749 }
750 }
751
752 private boolean seekTiffData(InputStream inputStream) throws IOException,
753 ExifInvalidFormatException {
Earl Ouc06c8042012-11-28 13:58:30 +0800754 CountedDataInputStream dataStream = new CountedDataInputStream(inputStream);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800755 if (dataStream.readShort() != JpegHeader.SOI) {
756 throw new ExifInvalidFormatException("Invalid JPEG format");
757 }
758
759 short marker = dataStream.readShort();
Ruben Brunkc274ded2013-03-11 19:00:12 -0700760 while (marker != JpegHeader.EOI
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800761 && !JpegHeader.isSofMarker(marker)) {
762 int length = dataStream.readUnsignedShort();
Earl Ou3f120e22012-10-19 17:41:16 +0800763 // Some invalid formatted image contains multiple APP1,
764 // try to find the one with Exif data.
765 if (marker == JpegHeader.APP1) {
766 int header = 0;
767 short headerTail = 0;
768 if (length >= 8) {
769 header = dataStream.readInt();
770 headerTail = dataStream.readShort();
771 length -= 6;
772 if (header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
Earl Ouc06c8042012-11-28 13:58:30 +0800773 mTiffStartPosition = dataStream.getReadByteCount();
Earl Ou04903342012-10-19 23:27:44 +0800774 mApp1End = length;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700775 mOffsetToApp1EndFromSOF = mTiffStartPosition + mApp1End;
Earl Ou3f120e22012-10-19 17:41:16 +0800776 return true;
777 }
778 }
779 }
780 if (length < 2 || (length - 2) != dataStream.skip(length - 2)) {
781 Log.w(TAG, "Invalid JPEG format.");
782 return false;
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800783 }
784 marker = dataStream.readShort();
785 }
Earl Ou3f120e22012-10-19 17:41:16 +0800786 return false;
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800787 }
788
Ruben Brunkc274ded2013-03-11 19:00:12 -0700789 protected int getOffsetToExifEndFromSOF() {
790 return mOffsetToApp1EndFromSOF;
791 }
792
793 protected int getTiffStartPosition() {
Earl Ouc06c8042012-11-28 13:58:30 +0800794 return mTiffStartPosition;
795 }
796
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800797 /**
798 * Reads bytes from the InputStream.
799 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700800 protected int read(byte[] buffer, int offset, int length) throws IOException {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800801 return mTiffStream.read(buffer, offset, length);
802 }
803
804 /**
805 * Equivalent to read(buffer, 0, buffer.length).
806 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700807 protected int read(byte[] buffer) throws IOException {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800808 return mTiffStream.read(buffer);
809 }
810
811 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700812 * Reads a String from the InputStream with US-ASCII charset. The parser
813 * will read n bytes and convert it to ascii string. This is used for
814 * reading values of type {@link ExifTag#TYPE_ASCII}.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800815 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700816 protected String readString(int n) throws IOException {
Earl Ouff0f96c2012-10-19 16:50:34 +0800817 return readString(n, US_ASCII);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800818 }
819
820 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700821 * Reads a String from the InputStream with the given charset. The parser
822 * will read n bytes and convert it to string. This is used for reading
823 * values of type {@link ExifTag#TYPE_ASCII}.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800824 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700825 protected String readString(int n, Charset charset) throws IOException {
Earl Ouff0f96c2012-10-19 16:50:34 +0800826 if (n > 0) {
Earl Ouff0f96c2012-10-19 16:50:34 +0800827 return mTiffStream.readString(n, charset);
828 } else {
829 return "";
830 }
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800831 }
832
833 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700834 * Reads value of type {@link ExifTag#TYPE_UNSIGNED_SHORT} from the
835 * InputStream.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800836 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700837 protected int readUnsignedShort() throws IOException {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800838 return mTiffStream.readShort() & 0xffff;
839 }
840
841 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700842 * Reads value of type {@link ExifTag#TYPE_UNSIGNED_LONG} from the
843 * InputStream.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800844 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700845 protected long readUnsignedLong() throws IOException {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800846 return readLong() & 0xffffffffL;
847 }
848
849 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700850 * Reads value of type {@link ExifTag#TYPE_UNSIGNED_RATIONAL} from the
851 * InputStream.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800852 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700853 protected Rational readUnsignedRational() throws IOException {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800854 long nomi = readUnsignedLong();
855 long denomi = readUnsignedLong();
856 return new Rational(nomi, denomi);
857 }
858
859 /**
860 * Reads value of type {@link ExifTag#TYPE_LONG} from the InputStream.
861 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700862 protected int readLong() throws IOException {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800863 return mTiffStream.readInt();
864 }
865
866 /**
867 * Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream.
868 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700869 protected Rational readRational() throws IOException {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800870 int nomi = readLong();
871 int denomi = readLong();
872 return new Rational(nomi, denomi);
873 }
874
875 private static class ImageEvent {
876 int stripIndex;
877 int type;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700878
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800879 ImageEvent(int type) {
880 this.stripIndex = 0;
881 this.type = type;
882 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700883
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800884 ImageEvent(int type, int stripIndex) {
885 this.type = type;
886 this.stripIndex = stripIndex;
887 }
888 }
889
890 private static class IfdEvent {
891 int ifd;
892 boolean isRequested;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700893
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800894 IfdEvent(int ifd, boolean isInterestedIfd) {
895 this.ifd = ifd;
896 this.isRequested = isInterestedIfd;
897 }
898 }
899
900 private static class ExifTagEvent {
901 ExifTag tag;
902 boolean isRequested;
Ruben Brunkc274ded2013-03-11 19:00:12 -0700903
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800904 ExifTagEvent(ExifTag tag, boolean isRequireByUser) {
905 this.tag = tag;
906 this.isRequested = isRequireByUser;
907 }
908 }
909
910 /**
911 * Gets the byte order of the current InputStream.
912 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700913 protected ByteOrder getByteOrder() {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800914 return mTiffStream.getByteOrder();
915 }
Earl Ouff0f96c2012-10-19 16:50:34 +0800916}