• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.gallery3d.exif;
18 
19 import java.nio.ByteOrder;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 
23 /**
24  *  This class stores the EXIF header in IFDs according to the JPEG specification.
25  *  It is the result produced by {@link ExifReader}.
26  *  @see ExifReader
27  *  @see IfdData
28  */
29 public class ExifData {
30     private final IfdData[] mIfdDatas = new IfdData[IfdId.TYPE_IFD_COUNT];
31     private byte[] mThumbnail;
32     private ArrayList<byte[]> mStripBytes = new ArrayList<byte[]>();
33     private final ByteOrder mByteOrder;
34 
ExifData(ByteOrder order)35     public ExifData(ByteOrder order) {
36         mByteOrder = order;
37     }
38 
getIfdData(int ifdId)39     IfdData getIfdData(int ifdId) {
40         return mIfdDatas[ifdId];
41     }
42 
43     /**
44      * Adds IFD data. If IFD data of the same type already exists,
45      * it will be replaced by the new data.
46      */
addIfdData(IfdData data)47     void addIfdData(IfdData data) {
48         mIfdDatas[data.getId()] = data;
49     }
50 
51     /**
52      * Gets the compressed thumbnail. Returns null if there is no compressed thumbnail.
53      *
54      * @see #hasCompressedThumbnail()
55      */
getCompressedThumbnail()56     public byte[] getCompressedThumbnail() {
57         return mThumbnail;
58     }
59 
60     /**
61      * Sets the compressed thumbnail.
62      */
setCompressedThumbnail(byte[] thumbnail)63     public void setCompressedThumbnail(byte[] thumbnail) {
64         mThumbnail = thumbnail;
65     }
66 
67     /**
68      * Returns true it this header contains a compressed thumbnail.
69      */
hasCompressedThumbnail()70     public boolean hasCompressedThumbnail() {
71         return mThumbnail != null;
72     }
73 
74     /**
75      * Adds an uncompressed strip.
76      */
setStripBytes(int index, byte[] strip)77     public void setStripBytes(int index, byte[] strip) {
78         if (index < mStripBytes.size()) {
79             mStripBytes.set(index, strip);
80         } else {
81             for (int i = mStripBytes.size(); i < index; i++) {
82                 mStripBytes.add(null);
83             }
84             mStripBytes.add(strip);
85         }
86     }
87 
88     /**
89      * Gets the strip count.
90      */
getStripCount()91     public int getStripCount() {
92         return mStripBytes.size();
93     }
94 
95     /**
96      * Gets the strip at the specified index.
97      * @exceptions #IndexOutOfBoundException
98      */
getStrip(int index)99     public byte[] getStrip(int index) {
100         return mStripBytes.get(index);
101     }
102 
103     /**
104      * Gets the byte order.
105      */
getByteOrder()106     public ByteOrder getByteOrder() {
107         return mByteOrder;
108     }
109 
110     /**
111      * Returns true if this header contains uncompressed strip of thumbnail.
112      */
hasUncompressedStrip()113     public boolean hasUncompressedStrip() {
114         return mStripBytes.size() != 0;
115     }
116 
117     @Override
equals(Object obj)118     public boolean equals(Object obj) {
119         if (obj instanceof ExifData) {
120             ExifData data = (ExifData) obj;
121             if (data.mByteOrder != mByteOrder
122                     || !Arrays.equals(data.mThumbnail, mThumbnail)
123                     || data.mStripBytes.size() != mStripBytes.size()) return false;
124 
125             for (int i = 0; i < mStripBytes.size(); i++) {
126                 if (!Arrays.equals(data.mStripBytes.get(i), mStripBytes.get(i))) return false;
127             }
128 
129             for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
130                 if (!Util.equals(data.getIfdData(i), getIfdData(i))) return false;
131             }
132             return true;
133         }
134         return false;
135     }
136 
137     /**
138      * Adds {@link ExifTag#TAG_GPS_LATITUDE}, {@link ExifTag#TAG_GPS_LONGITUDE},
139      * {@link ExifTag#TAG_GPS_LATITUDE_REF} and {@link ExifTag#TAG_GPS_LONGITUDE_REF} with the
140      * given latitude and longitude.
141      */
addGpsTags(double latitude, double longitude)142     public void addGpsTags(double latitude, double longitude) {
143         IfdData gpsIfd = getIfdData(IfdId.TYPE_IFD_GPS);
144         if (gpsIfd == null) {
145             gpsIfd = new IfdData(IfdId.TYPE_IFD_GPS);
146             addIfdData(gpsIfd);
147         }
148         ExifTag latTag = new ExifTag(ExifTag.TAG_GPS_LATITUDE, ExifTag.TYPE_RATIONAL,
149                 3, IfdId.TYPE_IFD_GPS);
150         ExifTag longTag = new ExifTag(ExifTag.TAG_GPS_LONGITUDE, ExifTag.TYPE_RATIONAL,
151                 3, IfdId.TYPE_IFD_GPS);
152         ExifTag latRefTag = new ExifTag(ExifTag.TAG_GPS_LATITUDE_REF,
153                 ExifTag.TYPE_ASCII, 2, IfdId.TYPE_IFD_GPS);
154         ExifTag longRefTag = new ExifTag(ExifTag.TAG_GPS_LONGITUDE_REF,
155                 ExifTag.TYPE_ASCII, 2, IfdId.TYPE_IFD_GPS);
156         latTag.setValue(toExifLatLong(latitude));
157         longTag.setValue(toExifLatLong(longitude));
158         latRefTag.setValue(latitude >= 0
159                 ? ExifTag.GpsLatitudeRef.NORTH
160                 : ExifTag.GpsLatitudeRef.SOUTH);
161         longRefTag.setValue(longitude >= 0
162                 ? ExifTag.GpsLongitudeRef.EAST
163                 : ExifTag.GpsLongitudeRef.WEST);
164         gpsIfd.setTag(latTag);
165         gpsIfd.setTag(longTag);
166         gpsIfd.setTag(latRefTag);
167         gpsIfd.setTag(longRefTag);
168     }
169 
toExifLatLong(double value)170     private static Rational[] toExifLatLong(double value) {
171         // convert to the format dd/1 mm/1 ssss/100
172         value = Math.abs(value);
173         int degrees = (int) value;
174         value = (value - degrees) * 60;
175         int minutes = (int) value;
176         value = (value - minutes) * 6000;
177         int seconds = (int) value;
178         return new Rational[] {
179                 new Rational(degrees, 1), new Rational(minutes, 1), new Rational(seconds, 100)};
180     }
181 
getOrCreateIfdData(int ifdId)182     private IfdData getOrCreateIfdData(int ifdId) {
183         IfdData ifdData = mIfdDatas[ifdId];
184         if (ifdData == null) {
185             ifdData = new IfdData(ifdId);
186             mIfdDatas[ifdId] = ifdData;
187         }
188         return ifdData;
189     }
190 
191     /**
192      * Gets the tag with the given tag ID. Returns null if the tag does not exist. For tags
193      * related to interoperability or thumbnail, call {@link #getInteroperabilityTag(short)} and
194      * {@link #getThumbnailTag(short)} respectively.
195      */
getTag(short tagId)196     public ExifTag getTag(short tagId) {
197         int ifdId = ExifTag.getIfdIdFromTagId(tagId);
198         IfdData ifdData = mIfdDatas[ifdId];
199         return (ifdData == null) ? null : ifdData.getTag(tagId);
200     }
201 
202     /**
203      * Gets the thumbnail-related tag with the given tag ID.
204      */
getThumbnailTag(short tagId)205     public ExifTag getThumbnailTag(short tagId) {
206         IfdData ifdData = mIfdDatas[IfdId.TYPE_IFD_1];
207         return (ifdData == null) ? null : ifdData.getTag(tagId);
208     }
209 
210     /**
211      * Gets the interoperability-related tag with the given tag ID.
212      */
getInteroperabilityTag(short tagId)213     public ExifTag getInteroperabilityTag(short tagId) {
214         IfdData ifdData = mIfdDatas[IfdId.TYPE_IFD_INTEROPERABILITY];
215         return (ifdData == null) ? null : ifdData.getTag(tagId);
216     }
217 
218     /**
219      * Adds a tag with the given tag ID. The original tag will be replaced by the new tag. For tags
220      * related to interoperability or thumbnail, call {@link #addInteroperabilityTag(short)} or
221      * {@link #addThumbnailTag(short)} respectively.
222      * @exception IllegalArgumentException if the tag ID is invalid.
223      */
addTag(short tagId)224     public ExifTag addTag(short tagId) {
225         int ifdId = ExifTag.getIfdIdFromTagId(tagId);
226         IfdData ifdData = getOrCreateIfdData(ifdId);
227         ExifTag tag = ExifTag.buildTag(tagId);
228         ifdData.setTag(tag);
229         return tag;
230     }
231 
232     /**
233      * Adds a thumbnail-related tag with the given tag ID. The original tag will be replaced
234      * by the new tag.
235      * @exception IllegalArgumentException if the tag ID is invalid.
236      */
addThumbnailTag(short tagId)237     public ExifTag addThumbnailTag(short tagId) {
238         IfdData ifdData = getOrCreateIfdData(IfdId.TYPE_IFD_1);
239         ExifTag tag = ExifTag.buildThumbnailTag(tagId);
240         ifdData.setTag(tag);
241         return tag;
242     }
243 
244     /**
245      * Adds an interoperability-related tag with the given tag ID. The original tag will be
246      * replaced by the new tag.
247      * @exception IllegalArgumentException if the tag ID is invalid.
248      */
addInteroperabilityTag(short tagId)249     public ExifTag addInteroperabilityTag(short tagId) {
250         IfdData ifdData = getOrCreateIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
251         ExifTag tag = ExifTag.buildInteroperabilityTag(tagId);
252         ifdData.setTag(tag);
253         return tag;
254     }
255 
removeThumbnailData()256     public void removeThumbnailData() {
257         mThumbnail = null;
258         mStripBytes.clear();
259         mIfdDatas[IfdId.TYPE_IFD_1] = null;
260     }
261 }