193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein/*
293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Copyright (C) 2013 The Android Open Source Project
393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Licensed under the Apache License, Version 2.0 (the "License");
593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * you may not use this file except in compliance with the License.
693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * You may obtain a copy of the License at
793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *      http://www.apache.org/licenses/LICENSE-2.0
993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
1093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Unless required by applicable law or agreed to in writing, software
1193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * distributed under the License is distributed on an "AS IS" BASIS,
1293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * See the License for the specific language governing permissions and
1493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * limitations under the License.
1593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein */
1693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinpackage com.android.bitmap.util;
1893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.util.Log;
2093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
2193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport java.io.ByteArrayInputStream;
2293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport java.io.InputStream;
2393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
2493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein/**
2593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * TODO
2693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Exif and InputStreamBuffer were pulled in from frameworks/ex/photo, and should be part of a
2793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * separate library that is used by both this and chips.
2893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein */
2993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinpublic class Exif {
3093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static final String TAG = Exif.class.getSimpleName();
3193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
3293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
3393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
3493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * @param inputStream The input stream will not be closed for you.
3593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * @param byteSize Recommended parameter declaring the length of the input stream. If you
3693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     *                 pass in -1, we will have to read more from the input stream.
3793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * @return 0, 90, 180, or 270.
3893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
3993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public static int getOrientation(final InputStream inputStream, final long byteSize) {
4093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (inputStream == null) {
4193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return 0;
4293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
4393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
4493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        /*
4593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          Looking at this algorithm, we never look ahead more than 8 bytes. As long as we call
4693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          advanceTo() at the end of every loop, we should never have to reallocate a larger buffer.
4793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
4893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          Also, the most we ever read backwards is 4 bytes. pack() reads backwards if the encoding
4993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          is in little endian format. These following two lines potentially reads 4 bytes backwards:
5093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
5193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          int tag = pack(jpeg, offset, 4, false);
5293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          count = pack(jpeg, offset - 2, 2, littleEndian);
5393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
5493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          To be safe, we will always advance to some index-4, so we'll need 4 more for the +8
5593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          look ahead, which makes it a +12 look ahead total. Use 16 just in case my analysis is off.
5693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
5793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          This means we only need to allocate a single 16 byte buffer.
5893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
5993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          Note: If you do not pass in byteSize parameter, a single large allocation will occur.
6093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          For a 1MB image, I see one 30KB allocation. This is due to the line containing:
6193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
6293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          has(jpeg, byteSize, offset + length - 1)
6393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
6493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          where length is a variable int (around 30KB above) read from the EXIF headers.
6593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
6693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein          This is still much better than allocating a 1MB byte[] which we were doing before.
6793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         */
6893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
6993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final int lookAhead = 16;
7093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final int readBackwards = 4;
7193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final InputStreamBuffer jpeg = new InputStreamBuffer(inputStream, lookAhead, false);
7293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
7393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        int offset = 0;
7493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        int length = 0;
7593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
7693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (has(jpeg, byteSize, 1)) {
7793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // JPEG image files begin with FF D8. Only JPEG images have EXIF data.
7893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final boolean possibleJpegFormat = jpeg.get(0) == (byte) 0xFF
7993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    && jpeg.get(1) == (byte) 0xD8;
8093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (!possibleJpegFormat) {
8193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                return 0;
8293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
8393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
8493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
8593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        // ISO/IEC 10918-1:1993(E)
8693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        while (has(jpeg, byteSize, offset + 3) && (jpeg.get(offset++) & 0xFF) == 0xFF) {
8793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final int marker = jpeg.get(offset) & 0xFF;
8893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
8993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // Check if the marker is a padding.
9093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (marker == 0xFF) {
9193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                continue;
9293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
9393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            offset++;
9493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
9593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // Check if the marker is SOI or TEM.
9693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (marker == 0xD8 || marker == 0x01) {
9793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                continue;
9893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
9993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // Check if the marker is EOI or SOS.
10093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (marker == 0xD9 || marker == 0xDA) {
10193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                // Loop ends.
10293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                jpeg.advanceTo(offset - readBackwards);
10393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                break;
10493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
10593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
10693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // Get the length and check if it is reasonable.
10793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            length = pack(jpeg, offset, 2, false);
10893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (length < 2 || !has(jpeg, byteSize, offset + length - 1)) {
10993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                Log.e(TAG, "Invalid length");
11093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                return 0;
11193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
11293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
11393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // Break if the marker is EXIF in APP1.
11493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (marker == 0xE1 && length >= 8 &&
11593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
11693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    pack(jpeg, offset + 6, 2, false) == 0) {
11793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                offset += 8;
11893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                length -= 8;
11993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                // Loop ends.
12093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                jpeg.advanceTo(offset - readBackwards);
12193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                break;
12293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
12393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
12493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // Skip other markers.
12593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            offset += length;
12693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            length = 0;
12793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
12893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // Loop ends.
12993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            jpeg.advanceTo(offset - readBackwards);
13093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
13193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
13293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        // JEITA CP-3451 Exif Version 2.2
13393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (length > 8) {
13493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // Identify the byte order.
13593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            int tag = pack(jpeg, offset, 4, false);
13693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (tag != 0x49492A00 && tag != 0x4D4D002A) {
13793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                Log.e(TAG, "Invalid byte order");
13893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                return 0;
13993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
14093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final boolean littleEndian = (tag == 0x49492A00);
14193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
14293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // Get the offset and check if it is reasonable.
14393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
14493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (count < 10 || count > length) {
14593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                Log.e(TAG, "Invalid offset");
14693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                return 0;
14793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
14893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            offset += count;
14993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            length -= count;
15093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
15193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // Offset has changed significantly.
15293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            jpeg.advanceTo(offset - readBackwards);
15393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
15493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // Get the count and go through all the elements.
15593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            count = pack(jpeg, offset - 2, 2, littleEndian);
15693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
15793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            while (count-- > 0 && length >= 12) {
15893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                // Get the tag and check if it is orientation.
15993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                tag = pack(jpeg, offset, 2, littleEndian);
16093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (tag == 0x0112) {
16193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    // We do not really care about type and count, do we?
16293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    final int orientation = pack(jpeg, offset + 8, 2, littleEndian);
16393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    switch (orientation) {
16493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        case 1:
16593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                            return 0;
16693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        case 3:
16793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                            return 180;
16893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        case 6:
16993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                            return 90;
17093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        case 8:
17193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                            return 270;
17293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
17393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    Log.i(TAG, "Unsupported orientation");
17493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    return 0;
17593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
17693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                offset += 12;
17793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                length -= 12;
17893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
17993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                // Loop ends.
18093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                jpeg.advanceTo(offset - readBackwards);
18193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
18293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
18393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
18493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        return 0;
18593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
18693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
18793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static int pack(final InputStreamBuffer bytes, int offset, int length,
18893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final boolean littleEndian) {
18993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        int step = 1;
19093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (littleEndian) {
19193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            offset += length - 1;
19293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            step = -1;
19393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
19493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
19593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        int value = 0;
19693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        while (length-- > 0) {
19793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            value = (value << 8) | (bytes.get(offset) & 0xFF);
19893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            offset += step;
19993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
20093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        return value;
20193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
20293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
20393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static boolean has(final InputStreamBuffer jpeg, final long byteSize, final int index) {
20493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (byteSize >= 0) {
20593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return index < byteSize;
20693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        } else {
20793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // For large values of index, this will cause the internal buffer to resize.
20893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return jpeg.has(index);
20993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
21093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
21193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
21293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Deprecated
21393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public static int getOrientation(final byte[] jpeg) {
21493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        return getOrientation(new ByteArrayInputStream(jpeg), jpeg.length);
21593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
21693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein}