137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh/*
237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh * Copyright (C) 2010 The Android Open Source Project
337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh *
437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh * Licensed under the Apache License, Version 2.0 (the "License");
537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh * you may not use this file except in compliance with the License.
637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh * You may obtain a copy of the License at
737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh *
837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh *      http://www.apache.org/licenses/LICENSE-2.0
937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh *
1037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh * Unless required by applicable law or agreed to in writing, software
1137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh * distributed under the License is distributed on an "AS IS" BASIS,
1237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh * See the License for the specific language governing permissions and
1437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh * limitations under the License.
1537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh */
1637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
1737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yehpackage com.android.camera;
1837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
1937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yehimport android.util.Log;
2037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
21a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kongpublic class Exif {
2237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh    private static final String TAG = "CameraExif";
2337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
24a3d2bab37e9c008e3b4c7e4ba93ba1fc40bd4b90Wu-cheng Li    // Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
2537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh    public static int getOrientation(byte[] jpeg) {
2637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        if (jpeg == null) {
2737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            return 0;
2837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        }
2937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
3037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        int offset = 0;
3137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        int length = 0;
3237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
3337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        // ISO/IEC 10918-1:1993(E)
3437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
3537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            int marker = jpeg[offset] & 0xFF;
3637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
3737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            // Check if the marker is a padding.
3837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            if (marker == 0xFF) {
3937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                continue;
4037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            }
4137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            offset++;
4237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
4337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            // Check if the marker is SOI or TEM.
4437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            if (marker == 0xD8 || marker == 0x01) {
4537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                continue;
4637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            }
4737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            // Check if the marker is EOI or SOS.
4837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            if (marker == 0xD9 || marker == 0xDA) {
4937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                break;
5037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            }
5137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
5237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            // Get the length and check if it is reasonable.
5337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            length = pack(jpeg, offset, 2, false);
5437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            if (length < 2 || offset + length > jpeg.length) {
5537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                Log.e(TAG, "Invalid length");
5637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                return 0;
5737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            }
5837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
5937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            // Break if the marker is EXIF in APP1.
6037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            if (marker == 0xE1 && length >= 8 &&
6137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                    pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
6237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                    pack(jpeg, offset + 6, 2, false) == 0) {
6337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                offset += 8;
6437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                length -= 8;
6537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                break;
6637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            }
6737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
6837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            // Skip other markers.
6937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            offset += length;
7037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            length = 0;
7137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        }
7237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
7337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        // JEITA CP-3451 Exif Version 2.2
7437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        if (length > 8) {
7537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            // Identify the byte order.
7637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            int tag = pack(jpeg, offset, 4, false);
7737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            if (tag != 0x49492A00 && tag != 0x4D4D002A) {
7837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                Log.e(TAG, "Invalid byte order");
7937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                return 0;
8037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            }
8137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            boolean littleEndian = (tag == 0x49492A00);
8237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
8337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            // Get the offset and check if it is reasonable.
8437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
8537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            if (count < 10 || count > length) {
8637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                Log.e(TAG, "Invalid offset");
8737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                return 0;
8837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            }
8937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            offset += count;
9037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            length -= count;
9137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
9237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            // Get the count and go through all the elements.
9337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            count = pack(jpeg, offset - 2, 2, littleEndian);
9437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            while (count-- > 0 && length >= 12) {
9537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                // Get the tag and check if it is orientation.
9637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                tag = pack(jpeg, offset, 2, littleEndian);
9737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                if (tag == 0x0112) {
9837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                    // We do not really care about type and count, do we?
9937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                    int orientation = pack(jpeg, offset + 8, 2, littleEndian);
10037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                    switch (orientation) {
10137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                        case 1:
10237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                            return 0;
10337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                        case 3:
10437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                            return 180;
10537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                        case 6:
10637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                            return 90;
10737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                        case 8:
10837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                            return 270;
10937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                    }
11037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                    Log.i(TAG, "Unsupported orientation");
11137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                    return 0;
11237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                }
11337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                offset += 12;
11437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh                length -= 12;
11537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            }
11637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        }
11737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
11837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        Log.i(TAG, "Orientation not found");
11937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        return 0;
12037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh    }
12137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
12237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh    private static int pack(byte[] bytes, int offset, int length,
12337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            boolean littleEndian) {
12437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        int step = 1;
12537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        if (littleEndian) {
12637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            offset += length - 1;
12737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            step = -1;
12837cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        }
12937cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh
13037cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        int value = 0;
13137cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        while (length-- > 0) {
13237cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            value = (value << 8) | (bytes[offset] & 0xFF);
13337cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh            offset += step;
13437cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        }
13537cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh        return value;
13637cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh    }
13737cffb8467d1ccca719a7847582d04da36c5bc6fChia-chi Yeh}
138