forked from mirrors/gecko-dev
		
	 265e672179
			
		
	
	
		265e672179
		
	
	
	
	
		
			
			# ignore-this-changeset --HG-- extra : amend_source : 4d301d3b0b8711c4692392aa76088ba7fd7d1022
		
			
				
	
	
		
			323 lines
		
	
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			323 lines
		
	
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "EXIF.h"
 | |
| 
 | |
| #include "mozilla/EndianUtils.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace image {
 | |
| 
 | |
| // Section references in this file refer to the EXIF v2.3 standard, also known
 | |
| // as CIPA DC-008-Translation-2010.
 | |
| 
 | |
| // See Section 4.6.4, Table 4.
 | |
| // Typesafe enums are intentionally not used here since we're comparing to raw
 | |
| // integers produced by parsing.
 | |
| enum EXIFTag {
 | |
|   OrientationTag = 0x112,
 | |
| };
 | |
| 
 | |
| // See Section 4.6.2.
 | |
| enum EXIFType {
 | |
|   ByteType = 1,
 | |
|   ASCIIType = 2,
 | |
|   ShortType = 3,
 | |
|   LongType = 4,
 | |
|   RationalType = 5,
 | |
|   UndefinedType = 7,
 | |
|   SignedLongType = 9,
 | |
|   SignedRational = 10,
 | |
| };
 | |
| 
 | |
| static const char* EXIFHeader = "Exif\0\0";
 | |
| static const uint32_t EXIFHeaderLength = 6;
 | |
| 
 | |
| /////////////////////////////////////////////////////////////
 | |
| // Parse EXIF data, typically found in a JPEG's APP1 segment.
 | |
| /////////////////////////////////////////////////////////////
 | |
| EXIFData EXIFParser::ParseEXIF(const uint8_t* aData, const uint32_t aLength) {
 | |
|   if (!Initialize(aData, aLength)) {
 | |
|     return EXIFData();
 | |
|   }
 | |
| 
 | |
|   if (!ParseEXIFHeader()) {
 | |
|     return EXIFData();
 | |
|   }
 | |
| 
 | |
|   uint32_t offsetIFD;
 | |
|   if (!ParseTIFFHeader(offsetIFD)) {
 | |
|     return EXIFData();
 | |
|   }
 | |
| 
 | |
|   JumpTo(offsetIFD);
 | |
| 
 | |
|   Orientation orientation;
 | |
|   if (!ParseIFD0(orientation)) {
 | |
|     return EXIFData();
 | |
|   }
 | |
| 
 | |
|   // We only care about orientation at this point, so we don't bother with the
 | |
|   // other IFDs. If we got this far we're done.
 | |
|   return EXIFData(orientation);
 | |
| }
 | |
| 
 | |
| /////////////////////////////////////////////////////////
 | |
| // Parse the EXIF header. (Section 4.7.2, Figure 30)
 | |
| /////////////////////////////////////////////////////////
 | |
| bool EXIFParser::ParseEXIFHeader() {
 | |
|   return MatchString(EXIFHeader, EXIFHeaderLength);
 | |
| }
 | |
| 
 | |
| /////////////////////////////////////////////////////////
 | |
| // Parse the TIFF header. (Section 4.5.2, Table 1)
 | |
| /////////////////////////////////////////////////////////
 | |
| bool EXIFParser::ParseTIFFHeader(uint32_t& aIFD0OffsetOut) {
 | |
|   // Determine byte order.
 | |
|   if (MatchString("MM\0*", 4)) {
 | |
|     mByteOrder = ByteOrder::BigEndian;
 | |
|   } else if (MatchString("II*\0", 4)) {
 | |
|     mByteOrder = ByteOrder::LittleEndian;
 | |
|   } else {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // Determine offset of the 0th IFD. (It shouldn't be greater than 64k, which
 | |
|   // is the maximum size of the entry APP1 segment.)
 | |
|   uint32_t ifd0Offset;
 | |
|   if (!ReadUInt32(ifd0Offset) || ifd0Offset > 64 * 1024) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // The IFD offset is relative to the beginning of the TIFF header, which
 | |
|   // begins after the EXIF header, so we need to increase the offset
 | |
|   // appropriately.
 | |
|   aIFD0OffsetOut = ifd0Offset + EXIFHeaderLength;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /////////////////////////////////////////////////////////
 | |
| // Parse the entries in IFD0. (Section 4.6.2)
 | |
| /////////////////////////////////////////////////////////
 | |
| bool EXIFParser::ParseIFD0(Orientation& aOrientationOut) {
 | |
|   uint16_t entryCount;
 | |
|   if (!ReadUInt16(entryCount)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   for (uint16_t entry = 0; entry < entryCount; ++entry) {
 | |
|     // Read the fields of the entry.
 | |
|     uint16_t tag;
 | |
|     if (!ReadUInt16(tag)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     // Right now, we only care about orientation, so we immediately skip to the
 | |
|     // next entry if we find anything else.
 | |
|     if (tag != OrientationTag) {
 | |
|       Advance(10);
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     uint16_t type;
 | |
|     if (!ReadUInt16(type)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     uint32_t count;
 | |
|     if (!ReadUInt32(count)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     // We should have an orientation value here; go ahead and parse it.
 | |
|     if (!ParseOrientation(type, count, aOrientationOut)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     // Since the orientation is all we care about, we're done.
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // We didn't find an orientation field in the IFD. That's OK; we assume the
 | |
|   // default orientation in that case.
 | |
|   aOrientationOut = Orientation();
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool EXIFParser::ParseOrientation(uint16_t aType, uint32_t aCount,
 | |
|                                   Orientation& aOut) {
 | |
|   // Sanity check the type and count.
 | |
|   if (aType != ShortType || aCount != 1) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   uint16_t value;
 | |
|   if (!ReadUInt16(value)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   switch (value) {
 | |
|     case 1:
 | |
|       aOut = Orientation(Angle::D0, Flip::Unflipped);
 | |
|       break;
 | |
|     case 2:
 | |
|       aOut = Orientation(Angle::D0, Flip::Horizontal);
 | |
|       break;
 | |
|     case 3:
 | |
|       aOut = Orientation(Angle::D180, Flip::Unflipped);
 | |
|       break;
 | |
|     case 4:
 | |
|       aOut = Orientation(Angle::D180, Flip::Horizontal);
 | |
|       break;
 | |
|     case 5:
 | |
|       aOut = Orientation(Angle::D90, Flip::Horizontal);
 | |
|       break;
 | |
|     case 6:
 | |
|       aOut = Orientation(Angle::D90, Flip::Unflipped);
 | |
|       break;
 | |
|     case 7:
 | |
|       aOut = Orientation(Angle::D270, Flip::Horizontal);
 | |
|       break;
 | |
|     case 8:
 | |
|       aOut = Orientation(Angle::D270, Flip::Unflipped);
 | |
|       break;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| 
 | |
|   // This is a 32-bit field, but the orientation value only occupies the first
 | |
|   // 16 bits. We need to advance another 16 bits to consume the entire field.
 | |
|   Advance(2);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool EXIFParser::Initialize(const uint8_t* aData, const uint32_t aLength) {
 | |
|   if (aData == nullptr) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // An APP1 segment larger than 64k violates the JPEG standard.
 | |
|   if (aLength > 64 * 1024) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   mStart = mCurrent = aData;
 | |
|   mLength = mRemainingLength = aLength;
 | |
|   mByteOrder = ByteOrder::Unknown;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void EXIFParser::Advance(const uint32_t aDistance) {
 | |
|   if (mRemainingLength >= aDistance) {
 | |
|     mCurrent += aDistance;
 | |
|     mRemainingLength -= aDistance;
 | |
|   } else {
 | |
|     mCurrent = mStart;
 | |
|     mRemainingLength = 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void EXIFParser::JumpTo(const uint32_t aOffset) {
 | |
|   if (mLength >= aOffset) {
 | |
|     mCurrent = mStart + aOffset;
 | |
|     mRemainingLength = mLength - aOffset;
 | |
|   } else {
 | |
|     mCurrent = mStart;
 | |
|     mRemainingLength = 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool EXIFParser::MatchString(const char* aString, const uint32_t aLength) {
 | |
|   if (mRemainingLength < aLength) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   for (uint32_t i = 0; i < aLength; ++i) {
 | |
|     if (mCurrent[i] != aString[i]) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Advance(aLength);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool EXIFParser::MatchUInt16(const uint16_t aValue) {
 | |
|   if (mRemainingLength < 2) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   bool matched;
 | |
|   switch (mByteOrder) {
 | |
|     case ByteOrder::LittleEndian:
 | |
|       matched = LittleEndian::readUint16(mCurrent) == aValue;
 | |
|       break;
 | |
|     case ByteOrder::BigEndian:
 | |
|       matched = BigEndian::readUint16(mCurrent) == aValue;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
 | |
|       matched = false;
 | |
|   }
 | |
| 
 | |
|   if (matched) {
 | |
|     Advance(2);
 | |
|   }
 | |
| 
 | |
|   return matched;
 | |
| }
 | |
| 
 | |
| bool EXIFParser::ReadUInt16(uint16_t& aValue) {
 | |
|   if (mRemainingLength < 2) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   bool matched = true;
 | |
|   switch (mByteOrder) {
 | |
|     case ByteOrder::LittleEndian:
 | |
|       aValue = LittleEndian::readUint16(mCurrent);
 | |
|       break;
 | |
|     case ByteOrder::BigEndian:
 | |
|       aValue = BigEndian::readUint16(mCurrent);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
 | |
|       matched = false;
 | |
|   }
 | |
| 
 | |
|   if (matched) {
 | |
|     Advance(2);
 | |
|   }
 | |
| 
 | |
|   return matched;
 | |
| }
 | |
| 
 | |
| bool EXIFParser::ReadUInt32(uint32_t& aValue) {
 | |
|   if (mRemainingLength < 4) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   bool matched = true;
 | |
|   switch (mByteOrder) {
 | |
|     case ByteOrder::LittleEndian:
 | |
|       aValue = LittleEndian::readUint32(mCurrent);
 | |
|       break;
 | |
|     case ByteOrder::BigEndian:
 | |
|       aValue = BigEndian::readUint32(mCurrent);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
 | |
|       matched = false;
 | |
|   }
 | |
| 
 | |
|   if (matched) {
 | |
|     Advance(4);
 | |
|   }
 | |
| 
 | |
|   return matched;
 | |
| }
 | |
| 
 | |
| }  // namespace image
 | |
| }  // namespace mozilla
 |