blob: 5a4d01918b7c0558870442db8633ddf671b1b410 [file] [log] [blame]
Hung-ying Tyan2523f432012-10-19 20:50:50 +08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
ztenghuia16e7b52013-08-23 11:47:56 -070017package com.android.camera.exif;
Hung-ying Tyan2523f432012-10-19 20:50:50 +080018
Angus Kong2bca2102014-03-11 16:27:30 -070019import com.android.camera.debug.Log;
Earl Ou428afc02012-12-07 16:56:10 +080020
21import java.io.UnsupportedEncodingException;
Hung-ying Tyan2523f432012-10-19 20:50:50 +080022import java.nio.ByteOrder;
23import java.util.ArrayList;
24import java.util.Arrays;
Ruben Brunkc274ded2013-03-11 19:00:12 -070025import java.util.List;
Hung-ying Tyan2523f432012-10-19 20:50:50 +080026
27/**
Ruben Brunkc274ded2013-03-11 19:00:12 -070028 * This class stores the EXIF header in IFDs according to the JPEG
29 * specification. It is the result produced by {@link ExifReader}.
30 *
31 * @see ExifReader
32 * @see IfdData
Hung-ying Tyan2523f432012-10-19 20:50:50 +080033 */
Ruben Brunkc274ded2013-03-11 19:00:12 -070034class ExifData {
Angus Kong2bca2102014-03-11 16:27:30 -070035 private static final Log.Tag TAG = new Log.Tag("ExifData");
Earl Ou428afc02012-12-07 16:56:10 +080036 private static final byte[] USER_COMMENT_ASCII = {
Ruben Brunkc274ded2013-03-11 19:00:12 -070037 0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00
38 };
Earl Ou428afc02012-12-07 16:56:10 +080039 private static final byte[] USER_COMMENT_JIS = {
Ruben Brunkc274ded2013-03-11 19:00:12 -070040 0x4A, 0x49, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00
41 };
Earl Ou428afc02012-12-07 16:56:10 +080042 private static final byte[] USER_COMMENT_UNICODE = {
Ruben Brunkc274ded2013-03-11 19:00:12 -070043 0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00
44 };
Earl Ou670baca2012-11-07 17:56:11 +080045
Hung-ying Tyan2523f432012-10-19 20:50:50 +080046 private final IfdData[] mIfdDatas = new IfdData[IfdId.TYPE_IFD_COUNT];
47 private byte[] mThumbnail;
48 private ArrayList<byte[]> mStripBytes = new ArrayList<byte[]>();
49 private final ByteOrder mByteOrder;
50
Ruben Brunkc274ded2013-03-11 19:00:12 -070051 ExifData(ByteOrder order) {
Hung-ying Tyan2523f432012-10-19 20:50:50 +080052 mByteOrder = order;
Hung-ying Tyan2523f432012-10-19 20:50:50 +080053 }
54
55 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -070056 * Gets the compressed thumbnail. Returns null if there is no compressed
57 * thumbnail.
Hung-ying Tyan2523f432012-10-19 20:50:50 +080058 *
59 * @see #hasCompressedThumbnail()
60 */
Ruben Brunkc274ded2013-03-11 19:00:12 -070061 protected byte[] getCompressedThumbnail() {
Hung-ying Tyan2523f432012-10-19 20:50:50 +080062 return mThumbnail;
63 }
64
65 /**
66 * Sets the compressed thumbnail.
67 */
Ruben Brunkc274ded2013-03-11 19:00:12 -070068 protected void setCompressedThumbnail(byte[] thumbnail) {
Hung-ying Tyan2523f432012-10-19 20:50:50 +080069 mThumbnail = thumbnail;
70 }
71
72 /**
73 * Returns true it this header contains a compressed thumbnail.
74 */
Ruben Brunkc274ded2013-03-11 19:00:12 -070075 protected boolean hasCompressedThumbnail() {
Hung-ying Tyan2523f432012-10-19 20:50:50 +080076 return mThumbnail != null;
77 }
78
79 /**
80 * Adds an uncompressed strip.
81 */
Ruben Brunkc274ded2013-03-11 19:00:12 -070082 protected void setStripBytes(int index, byte[] strip) {
Hung-ying Tyan2523f432012-10-19 20:50:50 +080083 if (index < mStripBytes.size()) {
84 mStripBytes.set(index, strip);
85 } else {
86 for (int i = mStripBytes.size(); i < index; i++) {
87 mStripBytes.add(null);
88 }
89 mStripBytes.add(strip);
90 }
91 }
92
93 /**
94 * Gets the strip count.
95 */
Ruben Brunkc274ded2013-03-11 19:00:12 -070096 protected int getStripCount() {
Hung-ying Tyan2523f432012-10-19 20:50:50 +080097 return mStripBytes.size();
98 }
99
100 /**
101 * Gets the strip at the specified index.
Ruben Brunkc274ded2013-03-11 19:00:12 -0700102 *
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800103 * @exceptions #IndexOutOfBoundException
104 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700105 protected byte[] getStrip(int index) {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800106 return mStripBytes.get(index);
107 }
108
109 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700110 * Returns true if this header contains uncompressed strip.
111 */
112 protected boolean hasUncompressedStrip() {
113 return mStripBytes.size() != 0;
114 }
115
116 /**
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800117 * Gets the byte order.
118 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700119 protected ByteOrder getByteOrder() {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800120 return mByteOrder;
121 }
122
123 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700124 * Returns the {@link IfdData} object corresponding to a given IFD if it
125 * exists or null.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800126 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700127 protected IfdData getIfdData(int ifdId) {
128 if (ExifTag.isValidIfd(ifdId)) {
129 return mIfdDatas[ifdId];
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800130 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700131 return null;
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800132 }
133
134 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700135 * Adds IFD data. If IFD data of the same type already exists, it will be
136 * replaced by the new data.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800137 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700138 protected void addIfdData(IfdData data) {
139 mIfdDatas[data.getId()] = data;
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800140 }
141
Earl Ou670baca2012-11-07 17:56:11 +0800142 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700143 * Returns the {@link IfdData} object corresponding to a given IFD or
144 * generates one if none exist.
Earl Ou670baca2012-11-07 17:56:11 +0800145 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700146 protected IfdData getOrCreateIfdData(int ifdId) {
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800147 IfdData ifdData = mIfdDatas[ifdId];
148 if (ifdData == null) {
149 ifdData = new IfdData(ifdId);
150 mIfdDatas[ifdId] = ifdData;
151 }
152 return ifdData;
153 }
154
155 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700156 * Returns the tag with a given TID in the given IFD if the tag exists.
157 * Otherwise returns null.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800158 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700159 protected ExifTag getTag(short tag, int ifd) {
160 IfdData ifdData = mIfdDatas[ifd];
161 return (ifdData == null) ? null : ifdData.getTag(tag);
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800162 }
163
164 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700165 * Adds the given ExifTag to its default IFD and returns an existing ExifTag
166 * with the same TID or null if none exist.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800167 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700168 protected ExifTag addTag(ExifTag tag) {
169 if (tag != null) {
170 int ifd = tag.getIfd();
171 return addTag(tag, ifd);
172 }
173 return null;
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800174 }
175
176 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700177 * Adds the given ExifTag to the given IFD and returns an existing ExifTag
178 * with the same TID or null if none exist.
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800179 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700180 protected ExifTag addTag(ExifTag tag, int ifdId) {
181 if (tag != null && ExifTag.isValidIfd(ifdId)) {
182 IfdData ifdData = getOrCreateIfdData(ifdId);
183 return ifdData.setTag(tag);
184 }
185 return null;
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800186 }
187
Ruben Brunkc274ded2013-03-11 19:00:12 -0700188 protected void clearThumbnailAndStrips() {
189 mThumbnail = null;
190 mStripBytes.clear();
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800191 }
192
Earl Oucdeb7882012-12-07 15:58:19 +0800193 /**
194 * Removes the thumbnail and its related tags. IFD1 will be removed.
195 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700196 protected void removeThumbnailData() {
197 clearThumbnailAndStrips();
Hung-ying Tyan2523f432012-10-19 20:50:50 +0800198 mIfdDatas[IfdId.TYPE_IFD_1] = null;
199 }
Earl Ou428afc02012-12-07 16:56:10 +0800200
201 /**
Ruben Brunkc274ded2013-03-11 19:00:12 -0700202 * Removes the tag with a given TID and IFD.
Earl Ou428afc02012-12-07 16:56:10 +0800203 */
Ruben Brunkc274ded2013-03-11 19:00:12 -0700204 protected void removeTag(short tagId, int ifdId) {
205 IfdData ifdData = mIfdDatas[ifdId];
206 if (ifdData == null) {
207 return;
208 }
209 ifdData.removeTag(tagId);
210 }
211
212 /**
213 * Decodes the user comment tag into string as specified in the EXIF
214 * standard. Returns null if decoding failed.
215 */
216 protected String getUserComment() {
Earl Ou428afc02012-12-07 16:56:10 +0800217 IfdData ifdData = mIfdDatas[IfdId.TYPE_IFD_0];
Ruben Brunkc274ded2013-03-11 19:00:12 -0700218 if (ifdData == null) {
219 return null;
220 }
221 ExifTag tag = ifdData.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_USER_COMMENT));
222 if (tag == null) {
223 return null;
224 }
225 if (tag.getComponentCount() < 8) {
226 return null;
227 }
Earl Ou428afc02012-12-07 16:56:10 +0800228
229 byte[] buf = new byte[tag.getComponentCount()];
230 tag.getBytes(buf);
231
232 byte[] code = new byte[8];
233 System.arraycopy(buf, 0, code, 0, 8);
234
235 try {
236 if (Arrays.equals(code, USER_COMMENT_ASCII)) {
237 return new String(buf, 8, buf.length - 8, "US-ASCII");
238 } else if (Arrays.equals(code, USER_COMMENT_JIS)) {
239 return new String(buf, 8, buf.length - 8, "EUC-JP");
240 } else if (Arrays.equals(code, USER_COMMENT_UNICODE)) {
241 return new String(buf, 8, buf.length - 8, "UTF-16");
242 } else {
243 return null;
244 }
245 } catch (UnsupportedEncodingException e) {
246 Log.w(TAG, "Failed to decode the user comment");
247 return null;
248 }
Earl Ou428afc02012-12-07 16:56:10 +0800249 }
Ruben Brunkc274ded2013-03-11 19:00:12 -0700250
251 /**
252 * Returns a list of all {@link ExifTag}s in the ExifData or null if there
253 * are none.
254 */
255 protected List<ExifTag> getAllTags() {
256 ArrayList<ExifTag> ret = new ArrayList<ExifTag>();
257 for (IfdData d : mIfdDatas) {
258 if (d != null) {
259 ExifTag[] tags = d.getAllTags();
260 if (tags != null) {
261 for (ExifTag t : tags) {
262 ret.add(t);
263 }
264 }
265 }
266 }
267 if (ret.size() == 0) {
268 return null;
269 }
270 return ret;
271 }
272
273 /**
274 * Returns a list of all {@link ExifTag}s in a given IFD or null if there
275 * are none.
276 */
277 protected List<ExifTag> getAllTagsForIfd(int ifd) {
278 IfdData d = mIfdDatas[ifd];
279 if (d == null) {
280 return null;
281 }
282 ExifTag[] tags = d.getAllTags();
283 if (tags == null) {
284 return null;
285 }
286 ArrayList<ExifTag> ret = new ArrayList<ExifTag>(tags.length);
287 for (ExifTag t : tags) {
288 ret.add(t);
289 }
290 if (ret.size() == 0) {
291 return null;
292 }
293 return ret;
294 }
295
296 /**
297 * Returns a list of all {@link ExifTag}s with a given TID or null if there
298 * are none.
299 */
300 protected List<ExifTag> getAllTagsForTagId(short tag) {
301 ArrayList<ExifTag> ret = new ArrayList<ExifTag>();
302 for (IfdData d : mIfdDatas) {
303 if (d != null) {
304 ExifTag t = d.getTag(tag);
305 if (t != null) {
306 ret.add(t);
307 }
308 }
309 }
310 if (ret.size() == 0) {
311 return null;
312 }
313 return ret;
314 }
315
316 @Override
317 public boolean equals(Object obj) {
318 if (this == obj) {
319 return true;
320 }
321 if (obj == null) {
322 return false;
323 }
324 if (obj instanceof ExifData) {
325 ExifData data = (ExifData) obj;
326 if (data.mByteOrder != mByteOrder ||
327 data.mStripBytes.size() != mStripBytes.size() ||
328 !Arrays.equals(data.mThumbnail, mThumbnail)) {
329 return false;
330 }
331 for (int i = 0; i < mStripBytes.size(); i++) {
332 if (!Arrays.equals(data.mStripBytes.get(i), mStripBytes.get(i))) {
333 return false;
334 }
335 }
336 for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
337 IfdData ifd1 = data.getIfdData(i);
338 IfdData ifd2 = getIfdData(i);
339 if (ifd1 != ifd2 && ifd1 != null && !ifd1.equals(ifd2)) {
340 return false;
341 }
342 }
343 return true;
344 }
345 return false;
346 }
347
Hung-ying Tyan1c515f12012-10-22 16:23:41 -0700348}