12523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan/* 22523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * Copyright (C) 2012 The Android Open Source Project 32523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * 42523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * Licensed under the Apache License, Version 2.0 (the "License"); 52523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * you may not use this file except in compliance with the License. 62523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * You may obtain a copy of the License at 72523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * 82523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * http://www.apache.org/licenses/LICENSE-2.0 92523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * 102523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * Unless required by applicable law or agreed to in writing, software 112523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * distributed under the License is distributed on an "AS IS" BASIS, 122523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * See the License for the specific language governing permissions and 142523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan * limitations under the License. 152523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan */ 162523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 17a16e7b50f3148f581439509279f242092e254309ztenghuipackage com.android.camera.exif; 182523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 192bca210e5fc8a77685775ffb403096167b017dceAngus Kongimport com.android.camera.debug.Log; 20c274ded801f745d6318186958107622e7a4fef33Ruben Brunk 2143b9c0c3fce9cb877ca73cb34917fb2c7556c4d4Ruben Brunkimport java.io.BufferedOutputStream; 222523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyanimport java.io.FilterOutputStream; 232523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyanimport java.io.IOException; 242523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyanimport java.io.OutputStream; 252523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyanimport java.nio.ByteBuffer; 262523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyanimport java.nio.ByteOrder; 27a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunkimport java.util.ArrayList; 282523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 299095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou/** 309095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * This class provides a way to replace the Exif header of a JPEG image. 319095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * <p> 329095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * Below is an example of writing EXIF data into a file 33c274ded801f745d6318186958107622e7a4fef33Ruben Brunk * 349095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * <pre> 359095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * public static void writeExif(byte[] jpeg, ExifData exif, String path) { 369095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * OutputStream os = null; 379095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * try { 389095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * os = new FileOutputStream(path); 399095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * ExifOutputStream eos = new ExifOutputStream(os); 409095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * // Set the exif header 419095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * eos.setExifData(exif); 429095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * // Write the original jpeg out, the header will be add into the file. 439095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * eos.write(jpeg); 449095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * } catch (FileNotFoundException e) { 459095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * e.printStackTrace(); 469095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * } catch (IOException e) { 479095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * e.printStackTrace(); 489095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * } finally { 499095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * if (os != null) { 509095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * try { 519095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * os.close(); 529095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * } catch (IOException e) { 539095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * e.printStackTrace(); 549095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * } 559095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * } 569095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * } 579095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * } 589095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * </pre> 599095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou */ 60c274ded801f745d6318186958107622e7a4fef33Ruben Brunkclass ExifOutputStream extends FilterOutputStream { 612bca210e5fc8a77685775ffb403096167b017dceAngus Kong private static final Log.Tag TAG = new Log.Tag("ExifOutputStream"); 62c274ded801f745d6318186958107622e7a4fef33Ruben Brunk private static final boolean DEBUG = false; 6343b9c0c3fce9cb877ca73cb34917fb2c7556c4d4Ruben Brunk private static final int STREAMBUFFER_SIZE = 0x00010000; // 64Kb 642523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 652523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private static final int STATE_SOI = 0; 662523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private static final int STATE_FRAME_HEADER = 1; 672523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private static final int STATE_JPEG_DATA = 2; 682523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 692523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private static final int EXIF_HEADER = 0x45786966; 702523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private static final short TIFF_HEADER = 0x002A; 712523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private static final short TIFF_BIG_ENDIAN = 0x4d4d; 722523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private static final short TIFF_LITTLE_ENDIAN = 0x4949; 732523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private static final short TAG_SIZE = 12; 742523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private static final short TIFF_HEADER_SIZE = 8; 75c274ded801f745d6318186958107622e7a4fef33Ruben Brunk private static final int MAX_EXIF_SIZE = 65535; 762523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 772523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private ExifData mExifData; 782523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private int mState = STATE_SOI; 792523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private int mByteToSkip; 802523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private int mByteToCopy; 81e3961e715d17179849901f2274e046b925b1a058Ruben Brunk private byte[] mSingleByteArray = new byte[1]; 822523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private ByteBuffer mBuffer = ByteBuffer.allocate(4); 83c274ded801f745d6318186958107622e7a4fef33Ruben Brunk private final ExifInterface mInterface; 842523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 85c274ded801f745d6318186958107622e7a4fef33Ruben Brunk protected ExifOutputStream(OutputStream ou, ExifInterface iRef) { 8643b9c0c3fce9cb877ca73cb34917fb2c7556c4d4Ruben Brunk super(new BufferedOutputStream(ou, STREAMBUFFER_SIZE)); 87c274ded801f745d6318186958107622e7a4fef33Ruben Brunk mInterface = iRef; 882523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 892523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 909095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou /** 91c274ded801f745d6318186958107622e7a4fef33Ruben Brunk * Sets the ExifData to be written into the JPEG file. Should be called 92c274ded801f745d6318186958107622e7a4fef33Ruben Brunk * before writing image data. 939095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou */ 94c274ded801f745d6318186958107622e7a4fef33Ruben Brunk protected void setExifData(ExifData exifData) { 952523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mExifData = exifData; 962523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 972523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 989095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou /** 999095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * Gets the Exif header to be written into the JPEF file. 1009095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou */ 101c274ded801f745d6318186958107622e7a4fef33Ruben Brunk protected ExifData getExifData() { 1022523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan return mExifData; 1032523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 1042523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 1052523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private int requestByteToBuffer(int requestByteCount, byte[] buffer 1062523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan , int offset, int length) { 1072523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan int byteNeeded = requestByteCount - mBuffer.position(); 1082523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan int byteToRead = length > byteNeeded ? byteNeeded : length; 1092523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mBuffer.put(buffer, offset, byteToRead); 1102523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan return byteToRead; 1112523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 1122523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 1139095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou /** 114c274ded801f745d6318186958107622e7a4fef33Ruben Brunk * Writes the image out. The input data should be a valid JPEG format. After 115c274ded801f745d6318186958107622e7a4fef33Ruben Brunk * writing, it's Exif header will be replaced by the given header. 1169095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou */ 1172523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan @Override 1182523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan public void write(byte[] buffer, int offset, int length) throws IOException { 119c274ded801f745d6318186958107622e7a4fef33Ruben Brunk while ((mByteToSkip > 0 || mByteToCopy > 0 || mState != STATE_JPEG_DATA) 1202523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan && length > 0) { 1212523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (mByteToSkip > 0) { 1222523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan int byteToProcess = length > mByteToSkip ? mByteToSkip : length; 1232523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan length -= byteToProcess; 1242523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mByteToSkip -= byteToProcess; 1252523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset += byteToProcess; 1262523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 1272523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (mByteToCopy > 0) { 1282523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan int byteToProcess = length > mByteToCopy ? mByteToCopy : length; 1292523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan out.write(buffer, offset, byteToProcess); 1302523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan length -= byteToProcess; 1312523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mByteToCopy -= byteToProcess; 1322523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset += byteToProcess; 1332523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 134c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (length == 0) { 135c274ded801f745d6318186958107622e7a4fef33Ruben Brunk return; 136c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 1372523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan switch (mState) { 1382523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan case STATE_SOI: 1392523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan int byteRead = requestByteToBuffer(2, buffer, offset, length); 1402523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset += byteRead; 1412523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan length -= byteRead; 142c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (mBuffer.position() < 2) { 143c274ded801f745d6318186958107622e7a4fef33Ruben Brunk return; 144c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 1452523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mBuffer.rewind(); 146c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (mBuffer.getShort() != JpegHeader.SOI) { 147c274ded801f745d6318186958107622e7a4fef33Ruben Brunk throw new IOException("Not a valid jpeg image, cannot write exif"); 148c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 149c274ded801f745d6318186958107622e7a4fef33Ruben Brunk out.write(mBuffer.array(), 0, 2); 1502523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mState = STATE_FRAME_HEADER; 1512523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mBuffer.rewind(); 1522523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan writeExifData(); 1532523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan break; 1542523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan case STATE_FRAME_HEADER: 155c274ded801f745d6318186958107622e7a4fef33Ruben Brunk // We ignore the APP1 segment and copy all other segments 156c274ded801f745d6318186958107622e7a4fef33Ruben Brunk // until SOF tag. 1572523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan byteRead = requestByteToBuffer(4, buffer, offset, length); 1582523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset += byteRead; 1592523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan length -= byteRead; 1602523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan // Check if this image data doesn't contain SOF. 1612523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (mBuffer.position() == 2) { 1622523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan short tag = mBuffer.getShort(); 1632523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (tag == JpegHeader.EOI) { 1642523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan out.write(mBuffer.array(), 0, 2); 1652523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mBuffer.rewind(); 1662523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 1672523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 168c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (mBuffer.position() < 4) { 169c274ded801f745d6318186958107622e7a4fef33Ruben Brunk return; 170c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 1712523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mBuffer.rewind(); 1722523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan short marker = mBuffer.getShort(); 1732523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (marker == JpegHeader.APP1) { 174c274ded801f745d6318186958107622e7a4fef33Ruben Brunk mByteToSkip = (mBuffer.getShort() & 0x0000ffff) - 2; 1752523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mState = STATE_JPEG_DATA; 1762523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } else if (!JpegHeader.isSofMarker(marker)) { 1772523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan out.write(mBuffer.array(), 0, 4); 178c274ded801f745d6318186958107622e7a4fef33Ruben Brunk mByteToCopy = (mBuffer.getShort() & 0x0000ffff) - 2; 1792523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } else { 1802523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan out.write(mBuffer.array(), 0, 4); 1812523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mState = STATE_JPEG_DATA; 1822523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 1832523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mBuffer.rewind(); 1842523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 1852523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 1862523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (length > 0) { 1872523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan out.write(buffer, offset, length); 1882523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 1892523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 1902523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 1919095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou /** 192c274ded801f745d6318186958107622e7a4fef33Ruben Brunk * Writes the one bytes out. The input data should be a valid JPEG format. 193c274ded801f745d6318186958107622e7a4fef33Ruben Brunk * After writing, it's Exif header will be replaced by the given header. 1949095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou */ 1952523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan @Override 1962523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan public void write(int oneByte) throws IOException { 197e3961e715d17179849901f2274e046b925b1a058Ruben Brunk mSingleByteArray[0] = (byte) (0xff & oneByte); 198e3961e715d17179849901f2274e046b925b1a058Ruben Brunk write(mSingleByteArray); 1992523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 2002523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 2019095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou /** 2029095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou * Equivalent to calling write(buffer, 0, buffer.length). 2039095cf465e3dc55d26cc4f4cc45dc60442868f14Earl Ou */ 2042523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan @Override 2052523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan public void write(byte[] buffer) throws IOException { 2062523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan write(buffer, 0, buffer.length); 2072523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 2082523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 2092523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private void writeExifData() throws IOException { 210c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (mExifData == null) { 211c274ded801f745d6318186958107622e7a4fef33Ruben Brunk return; 212c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 213c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (DEBUG) { 214c274ded801f745d6318186958107622e7a4fef33Ruben Brunk Log.v(TAG, "Writing exif data..."); 215c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 216a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk ArrayList<ExifTag> nullTags = stripNullValueTags(mExifData); 2172523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan createRequiredIfdAndTag(); 2182523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan int exifSize = calculateAllOffset(); 219c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (exifSize + 8 > MAX_EXIF_SIZE) { 220c274ded801f745d6318186958107622e7a4fef33Ruben Brunk throw new IOException("Exif header is too large (>64Kb)"); 221c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 2222523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan OrderedDataOutputStream dataOutputStream = new OrderedDataOutputStream(out); 2232523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN); 2242523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeShort(JpegHeader.APP1); 2252523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeShort((short) (exifSize + 8)); 2262523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeInt(EXIF_HEADER); 2272523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeShort((short) 0x0000); 2282523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (mExifData.getByteOrder() == ByteOrder.BIG_ENDIAN) { 2292523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeShort(TIFF_BIG_ENDIAN); 2302523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } else { 2312523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN); 2322523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 2332523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.setByteOrder(mExifData.getByteOrder()); 2342523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeShort(TIFF_HEADER); 2352523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeInt(8); 2362523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan writeAllTags(dataOutputStream); 2372523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan writeThumbnail(dataOutputStream); 238a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk for (ExifTag t : nullTags) { 239a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk mExifData.addTag(t); 240a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk } 241a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk } 242a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk 243a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk private ArrayList<ExifTag> stripNullValueTags(ExifData data) { 244a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk ArrayList<ExifTag> nullTags = new ArrayList<ExifTag>(); 245a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk for(ExifTag t : data.getAllTags()) { 246a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk if (t.getValue() == null && !ExifInterface.isOffsetTag(t.getTagId())) { 247a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk data.removeTag(t.getTagId(), t.getIfd()); 248a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk nullTags.add(t); 249a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk } 250a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk } 251a0c21c94c35865b0464cae27436f21d50ff1eb31Ruben Brunk return nullTags; 2522523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 2532523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 2542523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private void writeThumbnail(OrderedDataOutputStream dataOutputStream) throws IOException { 2552523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (mExifData.hasCompressedThumbnail()) { 2562523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.write(mExifData.getCompressedThumbnail()); 2572523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } else if (mExifData.hasUncompressedStrip()) { 2582523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan for (int i = 0; i < mExifData.getStripCount(); i++) { 2592523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.write(mExifData.getStrip(i)); 2602523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 2612523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 2622523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 2632523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 2642523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private void writeAllTags(OrderedDataOutputStream dataOutputStream) throws IOException { 2652523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_0), dataOutputStream); 2662523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_EXIF), dataOutputStream); 2672523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData interoperabilityIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY); 2682523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (interoperabilityIfd != null) { 2692523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan writeIfd(interoperabilityIfd, dataOutputStream); 2702523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 2712523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS); 2722523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (gpsIfd != null) { 2732523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan writeIfd(gpsIfd, dataOutputStream); 2742523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 2752523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1); 2762523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (ifd1 != null) { 2772523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_1), dataOutputStream); 2782523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 2792523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 2802523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 2812523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private void writeIfd(IfdData ifd, OrderedDataOutputStream dataOutputStream) 2822523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan throws IOException { 2832523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan ExifTag[] tags = ifd.getAllTags(); 2842523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeShort((short) tags.length); 285c274ded801f745d6318186958107622e7a4fef33Ruben Brunk for (ExifTag tag : tags) { 2862523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeShort(tag.getTagId()); 2872523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeShort(tag.getDataType()); 2882523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeInt(tag.getComponentCount()); 289c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (DEBUG) { 290c274ded801f745d6318186958107622e7a4fef33Ruben Brunk Log.v(TAG, "\n" + tag.toString()); 291c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 2922523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (tag.getDataSize() > 4) { 2932523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeInt(tag.getOffset()); 2942523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } else { 295c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ExifOutputStream.writeTagValue(tag, dataOutputStream); 2962523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan for (int i = 0, n = 4 - tag.getDataSize(); i < n; i++) { 2972523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.write(0); 2982523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 2992523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 3002523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 3012523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan dataOutputStream.writeInt(ifd.getOffsetToNextIfd()); 302c274ded801f745d6318186958107622e7a4fef33Ruben Brunk for (ExifTag tag : tags) { 3032523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (tag.getDataSize() > 4) { 304c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ExifOutputStream.writeTagValue(tag, dataOutputStream); 3052523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 3062523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 3072523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 3082523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 3092523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private int calculateOffsetOfIfd(IfdData ifd, int offset) { 3102523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset += 2 + ifd.getTagCount() * TAG_SIZE + 4; 3112523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan ExifTag[] tags = ifd.getAllTags(); 312c274ded801f745d6318186958107622e7a4fef33Ruben Brunk for (ExifTag tag : tags) { 3132523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (tag.getDataSize() > 4) { 3142523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan tag.setOffset(offset); 3152523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset += tag.getDataSize(); 3162523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 3172523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 3182523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan return offset; 3192523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 3202523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 321c274ded801f745d6318186958107622e7a4fef33Ruben Brunk private void createRequiredIfdAndTag() throws IOException { 3222523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan // IFD0 is required for all file 3232523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0); 3242523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (ifd0 == null) { 3252523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan ifd0 = new IfdData(IfdId.TYPE_IFD_0); 3262523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mExifData.addIfdData(ifd0); 3272523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 328c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ExifTag exifOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_EXIF_IFD); 329c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (exifOffsetTag == null) { 330c274ded801f745d6318186958107622e7a4fef33Ruben Brunk throw new IOException("No definition for crucial exif tag: " 331c274ded801f745d6318186958107622e7a4fef33Ruben Brunk + ExifInterface.TAG_EXIF_IFD); 332c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 3332523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan ifd0.setTag(exifOffsetTag); 3342523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 335c274ded801f745d6318186958107622e7a4fef33Ruben Brunk // Exif IFD is required for all files. 3362523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF); 3372523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (exifIfd == null) { 3382523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan exifIfd = new IfdData(IfdId.TYPE_IFD_EXIF); 3392523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mExifData.addIfdData(exifIfd); 3402523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 3412523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 3422523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan // GPS IFD 3432523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS); 3442523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (gpsIfd != null) { 345c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ExifTag gpsOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_GPS_IFD); 346c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (gpsOffsetTag == null) { 347c274ded801f745d6318186958107622e7a4fef33Ruben Brunk throw new IOException("No definition for crucial exif tag: " 348c274ded801f745d6318186958107622e7a4fef33Ruben Brunk + ExifInterface.TAG_GPS_IFD); 349c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 3502523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan ifd0.setTag(gpsOffsetTag); 3512523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 3522523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 3532523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan // Interoperability IFD 3542523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY); 3552523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (interIfd != null) { 356c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ExifTag interOffsetTag = mInterface 357c274ded801f745d6318186958107622e7a4fef33Ruben Brunk .buildUninitializedTag(ExifInterface.TAG_INTEROPERABILITY_IFD); 358c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (interOffsetTag == null) { 359c274ded801f745d6318186958107622e7a4fef33Ruben Brunk throw new IOException("No definition for crucial exif tag: " 360c274ded801f745d6318186958107622e7a4fef33Ruben Brunk + ExifInterface.TAG_INTEROPERABILITY_IFD); 361c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 3622523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan exifIfd.setTag(interOffsetTag); 3632523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 3642523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 3652523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1); 3662523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 3672523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan // thumbnail 3682523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (mExifData.hasCompressedThumbnail()) { 369c274ded801f745d6318186958107622e7a4fef33Ruben Brunk 3702523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (ifd1 == null) { 3712523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan ifd1 = new IfdData(IfdId.TYPE_IFD_1); 3722523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mExifData.addIfdData(ifd1); 3732523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 374c274ded801f745d6318186958107622e7a4fef33Ruben Brunk 375c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ExifTag offsetTag = mInterface 376c274ded801f745d6318186958107622e7a4fef33Ruben Brunk .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT); 377c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (offsetTag == null) { 378c274ded801f745d6318186958107622e7a4fef33Ruben Brunk throw new IOException("No definition for crucial exif tag: " 379c274ded801f745d6318186958107622e7a4fef33Ruben Brunk + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT); 380c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 381c274ded801f745d6318186958107622e7a4fef33Ruben Brunk 3822523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan ifd1.setTag(offsetTag); 383c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ExifTag lengthTag = mInterface 384c274ded801f745d6318186958107622e7a4fef33Ruben Brunk .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 385c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (lengthTag == null) { 386c274ded801f745d6318186958107622e7a4fef33Ruben Brunk throw new IOException("No definition for crucial exif tag: " 387c274ded801f745d6318186958107622e7a4fef33Ruben Brunk + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 388c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 389c274ded801f745d6318186958107622e7a4fef33Ruben Brunk 3902523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan lengthTag.setValue(mExifData.getCompressedThumbnail().length); 3912523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan ifd1.setTag(lengthTag); 392c274ded801f745d6318186958107622e7a4fef33Ruben Brunk 393c274ded801f745d6318186958107622e7a4fef33Ruben Brunk // Get rid of tags for uncompressed if they exist. 394c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)); 395c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS)); 396c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } else if (mExifData.hasUncompressedStrip()) { 3972523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (ifd1 == null) { 3982523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan ifd1 = new IfdData(IfdId.TYPE_IFD_1); 3992523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan mExifData.addIfdData(ifd1); 4002523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 4012523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan int stripCount = mExifData.getStripCount(); 402c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ExifTag offsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_OFFSETS); 403c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (offsetTag == null) { 404c274ded801f745d6318186958107622e7a4fef33Ruben Brunk throw new IOException("No definition for crucial exif tag: " 405c274ded801f745d6318186958107622e7a4fef33Ruben Brunk + ExifInterface.TAG_STRIP_OFFSETS); 406c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 407c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ExifTag lengthTag = mInterface 408c274ded801f745d6318186958107622e7a4fef33Ruben Brunk .buildUninitializedTag(ExifInterface.TAG_STRIP_BYTE_COUNTS); 409c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (lengthTag == null) { 410c274ded801f745d6318186958107622e7a4fef33Ruben Brunk throw new IOException("No definition for crucial exif tag: " 411c274ded801f745d6318186958107622e7a4fef33Ruben Brunk + ExifInterface.TAG_STRIP_BYTE_COUNTS); 412c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 4132523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan long[] lengths = new long[stripCount]; 4142523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan for (int i = 0; i < mExifData.getStripCount(); i++) { 4152523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan lengths[i] = mExifData.getStrip(i).length; 4162523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 4172523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan lengthTag.setValue(lengths); 4182523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan ifd1.setTag(offsetTag); 4192523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan ifd1.setTag(lengthTag); 420c274ded801f745d6318186958107622e7a4fef33Ruben Brunk // Get rid of tags for compressed if they exist. 421c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)); 422c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ifd1.removeTag(ExifInterface 423c274ded801f745d6318186958107622e7a4fef33Ruben Brunk .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)); 4242e47253cfb049943199faa43c04f77d0bab0b5eenicolasroard } else if (ifd1 != null) { 425c274ded801f745d6318186958107622e7a4fef33Ruben Brunk // Get rid of offset and length tags if there is no thumbnail. 426c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)); 427c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS)); 428c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)); 429c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ifd1.removeTag(ExifInterface 430c274ded801f745d6318186958107622e7a4fef33Ruben Brunk .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)); 4312523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 4322523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 4332523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 4342523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan private int calculateAllOffset() { 4352523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan int offset = TIFF_HEADER_SIZE; 4362523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0); 4372523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset = calculateOffsetOfIfd(ifd0, offset); 438c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD)).setValue(offset); 4392523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 4402523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF); 4412523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset = calculateOffsetOfIfd(exifIfd, offset); 4422523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 4432523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY); 4442523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (interIfd != null) { 445c274ded801f745d6318186958107622e7a4fef33Ruben Brunk exifIfd.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD)) 446c274ded801f745d6318186958107622e7a4fef33Ruben Brunk .setValue(offset); 4472523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset = calculateOffsetOfIfd(interIfd, offset); 4482523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 4492523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 4502523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS); 4512523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (gpsIfd != null) { 452c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD)).setValue(offset); 4532523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset = calculateOffsetOfIfd(gpsIfd, offset); 4542523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 4552523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 4562523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1); 4572523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (ifd1 != null) { 4582523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan ifd0.setOffsetToNextIfd(offset); 4592523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset = calculateOffsetOfIfd(ifd1, offset); 4602523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 4612523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan 4622523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan // thumbnail 4632523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan if (mExifData.hasCompressedThumbnail()) { 464c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) 465c274ded801f745d6318186958107622e7a4fef33Ruben Brunk .setValue(offset); 4662523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset += mExifData.getCompressedThumbnail().length; 467c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } else if (mExifData.hasUncompressedStrip()) { 4682523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan int stripCount = mExifData.getStripCount(); 4692523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan long[] offsets = new long[stripCount]; 4702523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan for (int i = 0; i < mExifData.getStripCount(); i++) { 4712523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offsets[i] = offset; 4722523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan offset += mExifData.getStrip(i).length; 4732523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 474c274ded801f745d6318186958107622e7a4fef33Ruben Brunk ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)).setValue( 475c274ded801f745d6318186958107622e7a4fef33Ruben Brunk offsets); 4762523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 4772523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan return offset; 4782523f4344661b1e6a734d1ba20e92308c87a7c54Hung-ying Tyan } 479c274ded801f745d6318186958107622e7a4fef33Ruben Brunk 480c274ded801f745d6318186958107622e7a4fef33Ruben Brunk static void writeTagValue(ExifTag tag, OrderedDataOutputStream dataOutputStream) 481c274ded801f745d6318186958107622e7a4fef33Ruben Brunk throws IOException { 482c274ded801f745d6318186958107622e7a4fef33Ruben Brunk switch (tag.getDataType()) { 483c274ded801f745d6318186958107622e7a4fef33Ruben Brunk case ExifTag.TYPE_ASCII: 484c274ded801f745d6318186958107622e7a4fef33Ruben Brunk byte buf[] = tag.getStringByte(); 485c274ded801f745d6318186958107622e7a4fef33Ruben Brunk if (buf.length == tag.getComponentCount()) { 486c274ded801f745d6318186958107622e7a4fef33Ruben Brunk buf[buf.length - 1] = 0; 487c274ded801f745d6318186958107622e7a4fef33Ruben Brunk dataOutputStream.write(buf); 488c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } else { 489c274ded801f745d6318186958107622e7a4fef33Ruben Brunk dataOutputStream.write(buf); 490c274ded801f745d6318186958107622e7a4fef33Ruben Brunk dataOutputStream.write(0); 491c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 492c274ded801f745d6318186958107622e7a4fef33Ruben Brunk break; 493c274ded801f745d6318186958107622e7a4fef33Ruben Brunk case ExifTag.TYPE_LONG: 494c274ded801f745d6318186958107622e7a4fef33Ruben Brunk case ExifTag.TYPE_UNSIGNED_LONG: 495c274ded801f745d6318186958107622e7a4fef33Ruben Brunk for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 496c274ded801f745d6318186958107622e7a4fef33Ruben Brunk dataOutputStream.writeInt((int) tag.getValueAt(i)); 497c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 498c274ded801f745d6318186958107622e7a4fef33Ruben Brunk break; 499c274ded801f745d6318186958107622e7a4fef33Ruben Brunk case ExifTag.TYPE_RATIONAL: 500c274ded801f745d6318186958107622e7a4fef33Ruben Brunk case ExifTag.TYPE_UNSIGNED_RATIONAL: 501c274ded801f745d6318186958107622e7a4fef33Ruben Brunk for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 502c274ded801f745d6318186958107622e7a4fef33Ruben Brunk dataOutputStream.writeRational(tag.getRational(i)); 503c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 504c274ded801f745d6318186958107622e7a4fef33Ruben Brunk break; 505c274ded801f745d6318186958107622e7a4fef33Ruben Brunk case ExifTag.TYPE_UNDEFINED: 506c274ded801f745d6318186958107622e7a4fef33Ruben Brunk case ExifTag.TYPE_UNSIGNED_BYTE: 507c274ded801f745d6318186958107622e7a4fef33Ruben Brunk buf = new byte[tag.getComponentCount()]; 508c274ded801f745d6318186958107622e7a4fef33Ruben Brunk tag.getBytes(buf); 509c274ded801f745d6318186958107622e7a4fef33Ruben Brunk dataOutputStream.write(buf); 510c274ded801f745d6318186958107622e7a4fef33Ruben Brunk break; 511c274ded801f745d6318186958107622e7a4fef33Ruben Brunk case ExifTag.TYPE_UNSIGNED_SHORT: 512c274ded801f745d6318186958107622e7a4fef33Ruben Brunk for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 513c274ded801f745d6318186958107622e7a4fef33Ruben Brunk dataOutputStream.writeShort((short) tag.getValueAt(i)); 514c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 515c274ded801f745d6318186958107622e7a4fef33Ruben Brunk break; 516c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 517c274ded801f745d6318186958107622e7a4fef33Ruben Brunk } 518c274ded801f745d6318186958107622e7a4fef33Ruben Brunk} 519