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