Bug 1689243 [wpt PR 27362] - Reland "Element reflection implementation updates."...
[gecko.git] / image / decoders / EXIF.cpp
blob34e589d966468644b57720c2a8c275775a2a4e41
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "EXIF.h"
8 #include "mozilla/EndianUtils.h"
10 namespace mozilla {
11 namespace image {
13 // Section references in this file refer to the EXIF v2.3 standard, also known
14 // as CIPA DC-008-Translation-2010.
16 // See Section 4.6.4, Table 4.
17 // Typesafe enums are intentionally not used here since we're comparing to raw
18 // integers produced by parsing.
19 enum EXIFTag {
20 OrientationTag = 0x112,
23 // See Section 4.6.2.
24 enum EXIFType {
25 ByteType = 1,
26 ASCIIType = 2,
27 ShortType = 3,
28 LongType = 4,
29 RationalType = 5,
30 UndefinedType = 7,
31 SignedLongType = 9,
32 SignedRational = 10,
35 static const char* EXIFHeader = "Exif\0\0";
36 static const uint32_t EXIFHeaderLength = 6;
38 /////////////////////////////////////////////////////////////
39 // Parse EXIF data, typically found in a JPEG's APP1 segment.
40 /////////////////////////////////////////////////////////////
41 EXIFData EXIFParser::ParseEXIF(const uint8_t* aData, const uint32_t aLength) {
42 if (!Initialize(aData, aLength)) {
43 return EXIFData();
46 if (!ParseEXIFHeader()) {
47 return EXIFData();
50 uint32_t offsetIFD;
51 if (!ParseTIFFHeader(offsetIFD)) {
52 return EXIFData();
55 JumpTo(offsetIFD);
57 Orientation orientation;
58 if (!ParseIFD0(orientation)) {
59 return EXIFData();
62 // We only care about orientation at this point, so we don't bother with the
63 // other IFDs. If we got this far we're done.
64 return EXIFData(orientation);
67 /////////////////////////////////////////////////////////
68 // Parse the EXIF header. (Section 4.7.2, Figure 30)
69 /////////////////////////////////////////////////////////
70 bool EXIFParser::ParseEXIFHeader() {
71 return MatchString(EXIFHeader, EXIFHeaderLength);
74 /////////////////////////////////////////////////////////
75 // Parse the TIFF header. (Section 4.5.2, Table 1)
76 /////////////////////////////////////////////////////////
77 bool EXIFParser::ParseTIFFHeader(uint32_t& aIFD0OffsetOut) {
78 // Determine byte order.
79 if (MatchString("MM\0*", 4)) {
80 mByteOrder = ByteOrder::BigEndian;
81 } else if (MatchString("II*\0", 4)) {
82 mByteOrder = ByteOrder::LittleEndian;
83 } else {
84 return false;
87 // Determine offset of the 0th IFD. (It shouldn't be greater than 64k, which
88 // is the maximum size of the entry APP1 segment.)
89 uint32_t ifd0Offset;
90 if (!ReadUInt32(ifd0Offset) || ifd0Offset > 64 * 1024) {
91 return false;
94 // The IFD offset is relative to the beginning of the TIFF header, which
95 // begins after the EXIF header, so we need to increase the offset
96 // appropriately.
97 aIFD0OffsetOut = ifd0Offset + EXIFHeaderLength;
98 return true;
101 /////////////////////////////////////////////////////////
102 // Parse the entries in IFD0. (Section 4.6.2)
103 /////////////////////////////////////////////////////////
104 bool EXIFParser::ParseIFD0(Orientation& aOrientationOut) {
105 uint16_t entryCount;
106 if (!ReadUInt16(entryCount)) {
107 return false;
110 for (uint16_t entry = 0; entry < entryCount; ++entry) {
111 // Read the fields of the entry.
112 uint16_t tag;
113 if (!ReadUInt16(tag)) {
114 return false;
117 // Right now, we only care about orientation, so we immediately skip to the
118 // next entry if we find anything else.
119 if (tag != OrientationTag) {
120 Advance(10);
121 continue;
124 uint16_t type;
125 if (!ReadUInt16(type)) {
126 return false;
129 uint32_t count;
130 if (!ReadUInt32(count)) {
131 return false;
134 // We should have an orientation value here; go ahead and parse it.
135 if (!ParseOrientation(type, count, aOrientationOut)) {
136 return false;
139 // Since the orientation is all we care about, we're done.
140 return true;
143 // We didn't find an orientation field in the IFD. That's OK; we assume the
144 // default orientation in that case.
145 aOrientationOut = Orientation();
146 return true;
149 bool EXIFParser::ParseOrientation(uint16_t aType, uint32_t aCount,
150 Orientation& aOut) {
151 // Sanity check the type and count.
152 if (aType != ShortType || aCount != 1) {
153 return false;
156 uint16_t value;
157 if (!ReadUInt16(value)) {
158 return false;
161 switch (value) {
162 case 1:
163 aOut = Orientation(Angle::D0, Flip::Unflipped);
164 break;
165 case 2:
166 aOut = Orientation(Angle::D0, Flip::Horizontal);
167 break;
168 case 3:
169 aOut = Orientation(Angle::D180, Flip::Unflipped);
170 break;
171 case 4:
172 aOut = Orientation(Angle::D180, Flip::Horizontal);
173 break;
174 case 5:
175 aOut = Orientation(Angle::D90, Flip::Horizontal);
176 break;
177 case 6:
178 aOut = Orientation(Angle::D90, Flip::Unflipped);
179 break;
180 case 7:
181 aOut = Orientation(Angle::D270, Flip::Horizontal);
182 break;
183 case 8:
184 aOut = Orientation(Angle::D270, Flip::Unflipped);
185 break;
186 default:
187 return false;
190 // This is a 32-bit field, but the orientation value only occupies the first
191 // 16 bits. We need to advance another 16 bits to consume the entire field.
192 Advance(2);
193 return true;
196 bool EXIFParser::Initialize(const uint8_t* aData, const uint32_t aLength) {
197 if (aData == nullptr) {
198 return false;
201 // An APP1 segment larger than 64k violates the JPEG standard.
202 if (aLength > 64 * 1024) {
203 return false;
206 mStart = mCurrent = aData;
207 mLength = mRemainingLength = aLength;
208 mByteOrder = ByteOrder::Unknown;
209 return true;
212 void EXIFParser::Advance(const uint32_t aDistance) {
213 if (mRemainingLength >= aDistance) {
214 mCurrent += aDistance;
215 mRemainingLength -= aDistance;
216 } else {
217 mCurrent = mStart;
218 mRemainingLength = 0;
222 void EXIFParser::JumpTo(const uint32_t aOffset) {
223 if (mLength >= aOffset) {
224 mCurrent = mStart + aOffset;
225 mRemainingLength = mLength - aOffset;
226 } else {
227 mCurrent = mStart;
228 mRemainingLength = 0;
232 bool EXIFParser::MatchString(const char* aString, const uint32_t aLength) {
233 if (mRemainingLength < aLength) {
234 return false;
237 for (uint32_t i = 0; i < aLength; ++i) {
238 if (mCurrent[i] != aString[i]) {
239 return false;
243 Advance(aLength);
244 return true;
247 bool EXIFParser::MatchUInt16(const uint16_t aValue) {
248 if (mRemainingLength < 2) {
249 return false;
252 bool matched;
253 switch (mByteOrder) {
254 case ByteOrder::LittleEndian:
255 matched = LittleEndian::readUint16(mCurrent) == aValue;
256 break;
257 case ByteOrder::BigEndian:
258 matched = BigEndian::readUint16(mCurrent) == aValue;
259 break;
260 default:
261 MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
262 matched = false;
265 if (matched) {
266 Advance(2);
269 return matched;
272 bool EXIFParser::ReadUInt16(uint16_t& aValue) {
273 if (mRemainingLength < 2) {
274 return false;
277 bool matched = true;
278 switch (mByteOrder) {
279 case ByteOrder::LittleEndian:
280 aValue = LittleEndian::readUint16(mCurrent);
281 break;
282 case ByteOrder::BigEndian:
283 aValue = BigEndian::readUint16(mCurrent);
284 break;
285 default:
286 MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
287 matched = false;
290 if (matched) {
291 Advance(2);
294 return matched;
297 bool EXIFParser::ReadUInt32(uint32_t& aValue) {
298 if (mRemainingLength < 4) {
299 return false;
302 bool matched = true;
303 switch (mByteOrder) {
304 case ByteOrder::LittleEndian:
305 aValue = LittleEndian::readUint32(mCurrent);
306 break;
307 case ByteOrder::BigEndian:
308 aValue = BigEndian::readUint32(mCurrent);
309 break;
310 default:
311 MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
312 matched = false;
315 if (matched) {
316 Advance(4);
319 return matched;
322 } // namespace image
323 } // namespace mozilla