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