19323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook/*
2c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei * Copyright (C) 2013 The Android Open Source Project
39323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook *
49323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook * Licensed under the Apache License, Version 2.0 (the "License");
59323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook * you may not use this file except in compliance with the License.
69323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook * You may obtain a copy of the License at
79323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook *
89323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook *      http://www.apache.org/licenses/LICENSE-2.0
99323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook *
109323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook * Unless required by applicable law or agreed to in writing, software
119323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook * distributed under the License is distributed on an "AS IS" BASIS,
129323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook * See the License for the specific language governing permissions and
149323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook * limitations under the License.
159323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook */
169323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
179323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrookpackage com.android.ex.photo.util;
189323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
199323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrookimport android.util.Log;
209323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
21c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Weiimport java.io.ByteArrayInputStream;
22c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Weiimport java.io.InputStream;
23c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
249323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrookpublic class Exif {
259323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook    private static final String TAG = "CameraExif";
269323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
27c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei    /**
28c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei     * Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
29c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei     * @param inputStream The input stream will not be closed for you.
30c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei     * @param byteSize Recommended parameter declaring the length of the input stream. If you
31c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei     *                 pass in -1, we will have to read more from the input stream.
32c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei     * @return 0, 90, 180, or 270.
33c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei     */
34c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei    public static int getOrientation(final InputStream inputStream, final long byteSize) {
35c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei        if (inputStream == null) {
369323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            return 0;
379323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        }
389323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
39c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei        /*
40c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          Looking at this algorithm, we never look ahead more than 8 bytes. As long as we call
41c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          advanceTo() at the end of every loop, we should never have to reallocate a larger buffer.
42c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
43c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          Also, the most we ever read backwards is 4 bytes. pack() reads backwards if the encoding
44c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          is in little endian format. These following two lines potentially reads 4 bytes backwards:
45c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
46c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          int tag = pack(jpeg, offset, 4, false);
47c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          count = pack(jpeg, offset - 2, 2, littleEndian);
48c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
49c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          To be safe, we will always advance to some index-4, so we'll need 4 more for the +8
50c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          look ahead, which makes it a +12 look ahead total. Use 16 just in case my analysis is off.
51c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
52c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          This means we only need to allocate a single 16 byte buffer.
53c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
54c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          Note: If you do not pass in byteSize parameter, a single large allocation will occur.
55c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          For a 1MB image, I see one 30KB allocation. This is due to the line containing:
56c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
57c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          has(jpeg, byteSize, offset + length - 1)
58c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
59c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          where length is a variable int (around 30KB above) read from the EXIF headers.
60c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
61c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei          This is still much better than allocating a 1MB byte[] which we were doing before.
62c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei         */
63c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
64c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei        final int lookAhead = 16;
65c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei        final int readBackwards = 4;
66c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei        final InputStreamBuffer jpeg = new InputStreamBuffer(inputStream, lookAhead, false);
67c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
689323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        int offset = 0;
699323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        int length = 0;
709323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
71b0cade17e70016ce562fb2033ea9e4a044137100Mark Wei        if (has(jpeg, byteSize, 1)) {
72b0cade17e70016ce562fb2033ea9e4a044137100Mark Wei            // JPEG image files begin with FF D8. Only JPEG images have EXIF data.
73b0cade17e70016ce562fb2033ea9e4a044137100Mark Wei            final boolean possibleJpegFormat = jpeg.get(0) == (byte) 0xFF
74b0cade17e70016ce562fb2033ea9e4a044137100Mark Wei                    && jpeg.get(1) == (byte) 0xD8;
75b0cade17e70016ce562fb2033ea9e4a044137100Mark Wei            if (!possibleJpegFormat) {
76b0cade17e70016ce562fb2033ea9e4a044137100Mark Wei                return 0;
77b0cade17e70016ce562fb2033ea9e4a044137100Mark Wei            }
78b0cade17e70016ce562fb2033ea9e4a044137100Mark Wei        }
79b0cade17e70016ce562fb2033ea9e4a044137100Mark Wei
809323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        // ISO/IEC 10918-1:1993(E)
81c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei        while (has(jpeg, byteSize, offset + 3) && (jpeg.get(offset++) & 0xFF) == 0xFF) {
82c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei            final int marker = jpeg.get(offset) & 0xFF;
839323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
849323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            // Check if the marker is a padding.
859323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            if (marker == 0xFF) {
869323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                continue;
879323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            }
889323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            offset++;
899323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
909323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            // Check if the marker is SOI or TEM.
919323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            if (marker == 0xD8 || marker == 0x01) {
929323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                continue;
939323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            }
949323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            // Check if the marker is EOI or SOS.
959323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            if (marker == 0xD9 || marker == 0xDA) {
96c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei                // Loop ends.
97c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei                jpeg.advanceTo(offset - readBackwards);
989323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                break;
999323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            }
1009323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
1019323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            // Get the length and check if it is reasonable.
1029323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            length = pack(jpeg, offset, 2, false);
103c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei            if (length < 2 || !has(jpeg, byteSize, offset + length - 1)) {
1049323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                Log.e(TAG, "Invalid length");
1059323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                return 0;
1069323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            }
1079323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
1089323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            // Break if the marker is EXIF in APP1.
1099323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            if (marker == 0xE1 && length >= 8 &&
1109323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                    pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
1119323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                    pack(jpeg, offset + 6, 2, false) == 0) {
1129323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                offset += 8;
1139323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                length -= 8;
114c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei                // Loop ends.
115c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei                jpeg.advanceTo(offset - readBackwards);
1169323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                break;
1179323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            }
1189323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
1199323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            // Skip other markers.
1209323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            offset += length;
1219323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            length = 0;
122c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
123c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei            // Loop ends.
124c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei            jpeg.advanceTo(offset - readBackwards);
1259323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        }
1269323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
1279323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        // JEITA CP-3451 Exif Version 2.2
1289323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        if (length > 8) {
1299323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            // Identify the byte order.
1309323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            int tag = pack(jpeg, offset, 4, false);
1319323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            if (tag != 0x49492A00 && tag != 0x4D4D002A) {
1329323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                Log.e(TAG, "Invalid byte order");
1339323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                return 0;
1349323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            }
135c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei            final boolean littleEndian = (tag == 0x49492A00);
1369323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
1379323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            // Get the offset and check if it is reasonable.
1389323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
1399323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            if (count < 10 || count > length) {
1409323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                Log.e(TAG, "Invalid offset");
1419323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                return 0;
1429323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            }
1439323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            offset += count;
1449323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            length -= count;
1459323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
146c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei            // Offset has changed significantly.
147c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei            jpeg.advanceTo(offset - readBackwards);
148c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
1499323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            // Get the count and go through all the elements.
1509323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            count = pack(jpeg, offset - 2, 2, littleEndian);
151c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
1529323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            while (count-- > 0 && length >= 12) {
1539323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                // Get the tag and check if it is orientation.
1549323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                tag = pack(jpeg, offset, 2, littleEndian);
1559323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                if (tag == 0x0112) {
1569323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                    // We do not really care about type and count, do we?
157c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei                    final int orientation = pack(jpeg, offset + 8, 2, littleEndian);
1589323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                    switch (orientation) {
1599323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                        case 1:
1609323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                            return 0;
1619323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                        case 3:
1629323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                            return 180;
1639323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                        case 6:
1649323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                            return 90;
1659323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                        case 8:
1669323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                            return 270;
1679323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                    }
1689323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                    Log.i(TAG, "Unsupported orientation");
1699323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                    return 0;
1709323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                }
1719323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                offset += 12;
1729323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook                length -= 12;
173c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
174c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei                // Loop ends.
175c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei                jpeg.advanceTo(offset - readBackwards);
1769323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            }
1779323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        }
1789323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
1799323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        return 0;
1809323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook    }
1819323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
182c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei    private static int pack(final InputStreamBuffer bytes, int offset, int length,
183c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei            final boolean littleEndian) {
1849323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        int step = 1;
1859323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        if (littleEndian) {
1869323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            offset += length - 1;
1879323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            step = -1;
1889323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        }
1899323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook
1909323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        int value = 0;
1919323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        while (length-- > 0) {
192c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei            value = (value << 8) | (bytes.get(offset) & 0xFF);
1939323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook            offset += step;
1949323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        }
1959323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook        return value;
1969323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook    }
197c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
198c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei    private static boolean has(final InputStreamBuffer jpeg, final long byteSize, final int index) {
199c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei        if (byteSize >= 0) {
200c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei            return index < byteSize;
201c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei        } else {
202c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei            // For large values of index, this will cause the internal buffer to resize.
203c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei            return jpeg.has(index);
204c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei        }
205c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei    }
206c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei
207c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei    @Deprecated
208c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei    public static int getOrientation(final byte[] jpeg) {
209c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei        return getOrientation(new ByteArrayInputStream(jpeg), jpeg.length);
210c631b5a4b1f19f84a70b772bc879fae7c92fd4a8Mark Wei    }
2119323b13fc9bc79ce38ce7c851a2aa894ab988ed0Paul Westbrook}
212