1/*
2 * Copyright (C) 2010 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
21public class Exif {
22    private static final String TAG = "CameraExif";
23
24    // Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
25    public static int getOrientation(byte[] jpeg) {
26        if (jpeg == null) {
27            return 0;
28        }
29
30        int offset = 0;
31        int length = 0;
32
33        // ISO/IEC 10918-1:1993(E)
34        while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
35            int marker = jpeg[offset] & 0xFF;
36
37            // Check if the marker is a padding.
38            if (marker == 0xFF) {
39                continue;
40            }
41            offset++;
42
43            // Check if the marker is SOI or TEM.
44            if (marker == 0xD8 || marker == 0x01) {
45                continue;
46            }
47            // Check if the marker is EOI or SOS.
48            if (marker == 0xD9 || marker == 0xDA) {
49                break;
50            }
51
52            // Get the length and check if it is reasonable.
53            length = pack(jpeg, offset, 2, false);
54            if (length < 2 || offset + length > jpeg.length) {
55                Log.e(TAG, "Invalid length");
56                return 0;
57            }
58
59            // Break if the marker is EXIF in APP1.
60            if (marker == 0xE1 && length >= 8 &&
61                    pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
62                    pack(jpeg, offset + 6, 2, false) == 0) {
63                offset += 8;
64                length -= 8;
65                break;
66            }
67
68            // Skip other markers.
69            offset += length;
70            length = 0;
71        }
72
73        // JEITA CP-3451 Exif Version 2.2
74        if (length > 8) {
75            // Identify the byte order.
76            int tag = pack(jpeg, offset, 4, false);
77            if (tag != 0x49492A00 && tag != 0x4D4D002A) {
78                Log.e(TAG, "Invalid byte order");
79                return 0;
80            }
81            boolean littleEndian = (tag == 0x49492A00);
82
83            // Get the offset and check if it is reasonable.
84            int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
85            if (count < 10 || count > length) {
86                Log.e(TAG, "Invalid offset");
87                return 0;
88            }
89            offset += count;
90            length -= count;
91
92            // Get the count and go through all the elements.
93            count = pack(jpeg, offset - 2, 2, littleEndian);
94            while (count-- > 0 && length >= 12) {
95                // Get the tag and check if it is orientation.
96                tag = pack(jpeg, offset, 2, littleEndian);
97                if (tag == 0x0112) {
98                    // We do not really care about type and count, do we?
99                    int orientation = pack(jpeg, offset + 8, 2, littleEndian);
100                    switch (orientation) {
101                        case 1:
102                            return 0;
103                        case 3:
104                            return 180;
105                        case 6:
106                            return 90;
107                        case 8:
108                            return 270;
109                    }
110                    Log.i(TAG, "Unsupported orientation");
111                    return 0;
112                }
113                offset += 12;
114                length -= 12;
115            }
116        }
117
118        Log.i(TAG, "Orientation not found");
119        return 0;
120    }
121
122    private static int pack(byte[] bytes, int offset, int length,
123            boolean littleEndian) {
124        int step = 1;
125        if (littleEndian) {
126            offset += length - 1;
127            step = -1;
128        }
129
130        int value = 0;
131        while (length-- > 0) {
132            value = (value << 8) | (bytes[offset] & 0xFF);
133            offset += step;
134        }
135        return value;
136    }
137}
138