1e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka/*
2e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Copyright (C) 2012 The Android Open Source Project
3e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka *
4e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * you may not use this file except in compliance with the License.
6e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * You may obtain a copy of the License at
7e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka *
8e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka *
10e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Unless required by applicable law or agreed to in writing, software
11e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * See the License for the specific language governing permissions and
14e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * limitations under the License.
15e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */
16e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
17e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapackage com.android.gallery3d.exif;
18e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
19e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.Log;
20e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
21e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.IOException;
22e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.InputStream;
23e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.nio.ByteBuffer;
24e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.nio.ByteOrder;
25e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.util.ArrayList;
26e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.util.List;
27e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
28e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaclass ExifModifier {
29e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    public static final String TAG = "ExifModifier";
30e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    public static final boolean DEBUG = false;
31e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private final ByteBuffer mByteBuffer;
32e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private final ExifData mTagToModified;
33e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>();
34e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private final ExifInterface mInterface;
35e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private int mOffsetBase;
36e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
37e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private static class TagOffset {
38e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final int mOffset;
39e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final ExifTag mTag;
40e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
41e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        TagOffset(ExifTag tag, int offset) {
42e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mTag = tag;
43e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mOffset = offset;
44e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
45e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
46e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
47e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected ExifModifier(ByteBuffer byteBuffer, ExifInterface iRef) throws IOException,
48e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            ExifInvalidFormatException {
49e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        mByteBuffer = byteBuffer;
50e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        mOffsetBase = byteBuffer.position();
51e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        mInterface = iRef;
52e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        InputStream is = null;
53e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        try {
54e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            is = new ByteBufferInputStream(byteBuffer);
55e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            // Do not require any IFD;
56e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            ExifParser parser = ExifParser.parse(is, mInterface);
57e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mTagToModified = new ExifData(parser.getByteOrder());
58e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mOffsetBase += parser.getTiffStartPosition();
59e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mByteBuffer.position(0);
60e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        } finally {
61e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            ExifInterface.closeSilently(is);
62e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
63e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
64e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
65e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected ByteOrder getByteOrder() {
66e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return mTagToModified.getByteOrder();
67e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
68e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
69e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected boolean commit() throws IOException, ExifInvalidFormatException {
70e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        InputStream is = null;
71e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        try {
72e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            is = new ByteBufferInputStream(mByteBuffer);
73e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            int flag = 0;
74e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            IfdData[] ifdDatas = new IfdData[] {
75e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_0),
76e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_1),
77e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF),
78e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY),
79e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS)
80e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            };
81e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
82e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_0] != null) {
83e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                flag |= ExifParser.OPTION_IFD_0;
84e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
85e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_1] != null) {
86e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                flag |= ExifParser.OPTION_IFD_1;
87e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
88e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) {
89e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                flag |= ExifParser.OPTION_IFD_EXIF;
90e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
91e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) {
92e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                flag |= ExifParser.OPTION_IFD_GPS;
93e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
94e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) {
95e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                flag |= ExifParser.OPTION_IFD_INTEROPERABILITY;
96e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
97e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
98e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            ExifParser parser = ExifParser.parse(is, flag, mInterface);
99e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            int event = parser.next();
100e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            IfdData currIfd = null;
101e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            while (event != ExifParser.EVENT_END) {
102e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                switch (event) {
103e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    case ExifParser.EVENT_START_OF_IFD:
104e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        currIfd = ifdDatas[parser.getCurrentIfd()];
105e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        if (currIfd == null) {
106e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                            parser.skipRemainingTagsInCurrentIfd();
107e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        }
108e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        break;
109e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    case ExifParser.EVENT_NEW_TAG:
110e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        ExifTag oldTag = parser.getTag();
111e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        ExifTag newTag = currIfd.getTag(oldTag.getTagId());
112e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        if (newTag != null) {
113e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                            if (newTag.getComponentCount() != oldTag.getComponentCount()
114e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                                    || newTag.getDataType() != oldTag.getDataType()) {
115e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                                return false;
116e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                            } else {
117e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                                mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset()));
118e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                                currIfd.removeTag(oldTag.getTagId());
119e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                                if (currIfd.getTagCount() == 0) {
120e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                                    parser.skipRemainingTagsInCurrentIfd();
121e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                                }
122e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                            }
123e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        }
124e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        break;
125e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
126e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                event = parser.next();
127e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
128e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            for (IfdData ifd : ifdDatas) {
129e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (ifd != null && ifd.getTagCount() > 0) {
130e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    return false;
131e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
132e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
133e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            modify();
134e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        } finally {
135e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            ExifInterface.closeSilently(is);
136e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
137e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return true;
138e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
139e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
140e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private void modify() {
141e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        mByteBuffer.order(getByteOrder());
142e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        for (TagOffset tagOffset : mTagOffsets) {
143e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            writeTagValue(tagOffset.mTag, tagOffset.mOffset);
144e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
145e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
146e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
147e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private void writeTagValue(ExifTag tag, int offset) {
148e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        if (DEBUG) {
149e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            Log.v(TAG, "modifying tag to: \n" + tag.toString());
150e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            Log.v(TAG, "at offset: " + offset);
151e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
152e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        mByteBuffer.position(offset + mOffsetBase);
153e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        switch (tag.getDataType()) {
154e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            case ExifTag.TYPE_ASCII:
155e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                byte buf[] = tag.getStringByte();
156e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (buf.length == tag.getComponentCount()) {
157e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    buf[buf.length - 1] = 0;
158e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mByteBuffer.put(buf);
159e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                } else {
160e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mByteBuffer.put(buf);
161e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mByteBuffer.put((byte) 0);
162e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
163e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                break;
164e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            case ExifTag.TYPE_LONG:
165e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            case ExifTag.TYPE_UNSIGNED_LONG:
166e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
167e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mByteBuffer.putInt((int) tag.getValueAt(i));
168e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
169e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                break;
170e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            case ExifTag.TYPE_RATIONAL:
171e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            case ExifTag.TYPE_UNSIGNED_RATIONAL:
172e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
173e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Rational v = tag.getRational(i);
174e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mByteBuffer.putInt((int) v.getNumerator());
175e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mByteBuffer.putInt((int) v.getDenominator());
176e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
177e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                break;
178e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            case ExifTag.TYPE_UNDEFINED:
179e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            case ExifTag.TYPE_UNSIGNED_BYTE:
180e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                buf = new byte[tag.getComponentCount()];
181e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                tag.getBytes(buf);
182e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                mByteBuffer.put(buf);
183e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                break;
184e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            case ExifTag.TYPE_UNSIGNED_SHORT:
185e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
186e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mByteBuffer.putShort((short) tag.getValueAt(i));
187e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
188e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                break;
189e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
190e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
191e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
192e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    public void modifyTag(ExifTag tag) {
193e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        mTagToModified.addTag(tag);
194e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
195e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka}
196