1 /** 2 * Copyright (C) 2015 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 package com.android.gallery3d.common; 17 18 import android.content.Context; 19 import android.media.ExifInterface; 20 import android.util.Log; 21 22 import java.io.DataInputStream; 23 import java.io.DataOutputStream; 24 import java.io.File; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 29 public class ExifOrientation { 30 private static final String TAG = "ExifOrientation"; 31 private static final boolean DEBUG = false; 32 33 private static final short SOI = (short) 0xFFD8; // start of input 34 private static final short APP0 = (short) 0xFFE0; 35 private static final short APPF = (short) 0xFFEF; 36 private static final short APP1 = (short) 0xFFE1; 37 private static final short SOS = (short) 0xFFDA; // start of stream 38 private static final short EOI = (short) 0xFFD9; // end of input 39 40 // The header is available in first 64 bytes, so reading upto 128 bytes 41 // should be more than enough. 42 private static final int MAX_BYTES_TO_READ = 128 * 1024; 43 44 /** 45 * Parses the rotation of the JPEG image from the input stream. 46 */ readRotation(InputStream in, Context context)47 public static final int readRotation(InputStream in, Context context) { 48 // Since the platform implementation only takes file input, create a temporary file 49 // with just the image header. 50 File tempFile = null; 51 DataOutputStream tempOut = null; 52 53 try { 54 DataInputStream din = new DataInputStream(in); 55 int pos = 0; 56 if (din.readShort() == SOI) { 57 pos += 2; 58 59 short marker = din.readShort(); 60 pos += 2; 61 62 while ((marker >= APP0 && marker <= APPF) && pos < MAX_BYTES_TO_READ) { 63 int length = din.readUnsignedShort(); 64 if (length < 2) { 65 throw new IOException("Invalid header size"); 66 } 67 68 // We only want APP1 headers 69 if (length > 2) { 70 if (marker == APP1) { 71 // Copy the header 72 if (tempFile == null) { 73 tempFile = File.createTempFile(TAG, ".jpg", context.getCacheDir()); 74 tempOut = new DataOutputStream(new FileOutputStream(tempFile)); 75 tempOut.writeShort(SOI); 76 } 77 78 tempOut.writeShort(marker); 79 tempOut.writeShort(length); 80 81 byte[] header = new byte[length - 2]; 82 din.read(header); 83 tempOut.write(header); 84 } else { 85 din.skip(length - 2); 86 } 87 } 88 pos += length; 89 90 marker = din.readShort(); 91 pos += 2; 92 } 93 94 if (tempOut != null) { 95 // Write empty image data. 96 tempOut.writeShort(SOS); 97 // Write the frame size as 2. Since this includes the size bytes as well 98 // (short = 2 bytes), it implies there is 0 byte of image data. 99 tempOut.writeShort(2); 100 101 // End of input 102 tempOut.writeShort(EOI); 103 tempOut.close(); 104 105 return readRotation(tempFile.getAbsolutePath()); 106 } 107 } 108 } catch (IOException e) { 109 if (DEBUG) { 110 Log.d(TAG, "Error parsing input stream", e); 111 } 112 } finally { 113 Utils.closeSilently(in); 114 Utils.closeSilently(tempOut); 115 if (tempFile != null) { 116 tempFile.delete(); 117 } 118 } 119 return 0; 120 } 121 122 /** 123 * Parses the rotation of the JPEG image. 124 */ readRotation(String filePath)125 public static final int readRotation(String filePath) { 126 try { 127 ExifInterface exif = new ExifInterface(filePath); 128 switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) { 129 case ExifInterface.ORIENTATION_ROTATE_90: 130 return 90; 131 case ExifInterface.ORIENTATION_ROTATE_270: 132 return 270; 133 case ExifInterface.ORIENTATION_ROTATE_180: 134 return 180; 135 default: 136 return 0; 137 } 138 } catch (IOException e) { 139 if (DEBUG) { 140 Log.d(TAG, "Error reading file", e); 141 } 142 } 143 return 0; 144 } 145 } 146