1*4d671364SKiyoung Kim // Copyright 2015 Google Inc.
2*4d671364SKiyoung Kim //
3*4d671364SKiyoung Kim // Licensed under the Apache License, Version 2.0 (the "License");
4*4d671364SKiyoung Kim // you may not use this file except in compliance with the License.
5*4d671364SKiyoung Kim // You may obtain a copy of the License at
6*4d671364SKiyoung Kim //
7*4d671364SKiyoung Kim // http://www.apache.org/licenses/LICENSE-2.0
8*4d671364SKiyoung Kim //
9*4d671364SKiyoung Kim // Unless required by applicable law or agreed to in writing, software
10*4d671364SKiyoung Kim // distributed under the License is distributed on an "AS IS" BASIS,
11*4d671364SKiyoung Kim // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*4d671364SKiyoung Kim // See the License for the specific language governing permissions and
13*4d671364SKiyoung Kim // limitations under the License.
14*4d671364SKiyoung Kim //
15*4d671364SKiyoung Kim ////////////////////////////////////////////////////////////////////////////////
16*4d671364SKiyoung Kim
17*4d671364SKiyoung Kim #include "src/tiff_parser.h"
18*4d671364SKiyoung Kim
19*4d671364SKiyoung Kim #include <cstring>
20*4d671364SKiyoung Kim #include <limits>
21*4d671364SKiyoung Kim #include <numeric>
22*4d671364SKiyoung Kim
23*4d671364SKiyoung Kim #include "src/tiff_directory/tiff_directory.h"
24*4d671364SKiyoung Kim
25*4d671364SKiyoung Kim namespace piex {
26*4d671364SKiyoung Kim namespace {
27*4d671364SKiyoung Kim
28*4d671364SKiyoung Kim using tiff_directory::Endian;
29*4d671364SKiyoung Kim using tiff_directory::Rational;
30*4d671364SKiyoung Kim using tiff_directory::SizeOfType;
31*4d671364SKiyoung Kim using tiff_directory::TIFF_TYPE_LONG;
32*4d671364SKiyoung Kim using tiff_directory::TIFF_TYPE_UNDEFINED;
33*4d671364SKiyoung Kim using tiff_directory::TiffDirectory;
34*4d671364SKiyoung Kim using tiff_directory::kBigEndian;
35*4d671364SKiyoung Kim using tiff_directory::kLittleEndian;
36*4d671364SKiyoung Kim
37*4d671364SKiyoung Kim // Specifies all tags that might be of interest to parse JPEG data.
38*4d671364SKiyoung Kim const std::uint32_t kStartOfFrame = 0xFFC0;
39*4d671364SKiyoung Kim const std::uint32_t kStartOfImage = 0xFFD8;
40*4d671364SKiyoung Kim const std::uint32_t kStartOfScan = 0xFFDA;
41*4d671364SKiyoung Kim
GetFullDimension16(const TiffDirectory & tiff_directory,std::uint16_t * width,std::uint16_t * height)42*4d671364SKiyoung Kim bool GetFullDimension16(const TiffDirectory& tiff_directory,
43*4d671364SKiyoung Kim std::uint16_t* width, std::uint16_t* height) {
44*4d671364SKiyoung Kim std::uint32_t tmp_width = 0;
45*4d671364SKiyoung Kim std::uint32_t tmp_height = 0;
46*4d671364SKiyoung Kim if (!GetFullDimension32(tiff_directory, &tmp_width, &tmp_height) ||
47*4d671364SKiyoung Kim tmp_width > std::numeric_limits<std::uint16_t>::max() ||
48*4d671364SKiyoung Kim tmp_height > std::numeric_limits<std::uint16_t>::max()) {
49*4d671364SKiyoung Kim return false;
50*4d671364SKiyoung Kim }
51*4d671364SKiyoung Kim *width = static_cast<std::uint16_t>(tmp_width);
52*4d671364SKiyoung Kim *height = static_cast<std::uint16_t>(tmp_height);
53*4d671364SKiyoung Kim return true;
54*4d671364SKiyoung Kim }
55*4d671364SKiyoung Kim
FillGpsPreviewImageData(const TiffDirectory & gps_directory,PreviewImageData * preview_image_data)56*4d671364SKiyoung Kim void FillGpsPreviewImageData(const TiffDirectory& gps_directory,
57*4d671364SKiyoung Kim PreviewImageData* preview_image_data) {
58*4d671364SKiyoung Kim if (gps_directory.Has(kGpsTagLatitudeRef) &&
59*4d671364SKiyoung Kim gps_directory.Has(kGpsTagLatitude) &&
60*4d671364SKiyoung Kim gps_directory.Has(kGpsTagLongitudeRef) &&
61*4d671364SKiyoung Kim gps_directory.Has(kGpsTagLongitude) &&
62*4d671364SKiyoung Kim gps_directory.Has(kGpsTagTimeStamp) &&
63*4d671364SKiyoung Kim gps_directory.Has(kGpsTagDateStamp)) {
64*4d671364SKiyoung Kim preview_image_data->gps.is_valid = false;
65*4d671364SKiyoung Kim std::string value;
66*4d671364SKiyoung Kim if (!gps_directory.Get(kGpsTagLatitudeRef, &value) || value.empty() ||
67*4d671364SKiyoung Kim (value[0] != 'N' && value[0] != 'S') ||
68*4d671364SKiyoung Kim !GetRational(kGpsTagLatitude, gps_directory, 3 /* data size */,
69*4d671364SKiyoung Kim preview_image_data->gps.latitude)) {
70*4d671364SKiyoung Kim return;
71*4d671364SKiyoung Kim }
72*4d671364SKiyoung Kim preview_image_data->gps.latitude_ref = value[0];
73*4d671364SKiyoung Kim
74*4d671364SKiyoung Kim if (!gps_directory.Get(kGpsTagLongitudeRef, &value) || value.empty() ||
75*4d671364SKiyoung Kim (value[0] != 'E' && value[0] != 'W') ||
76*4d671364SKiyoung Kim !GetRational(kGpsTagLongitude, gps_directory, 3 /* data size */,
77*4d671364SKiyoung Kim preview_image_data->gps.longitude)) {
78*4d671364SKiyoung Kim return;
79*4d671364SKiyoung Kim }
80*4d671364SKiyoung Kim preview_image_data->gps.longitude_ref = value[0];
81*4d671364SKiyoung Kim
82*4d671364SKiyoung Kim if (!GetRational(kGpsTagTimeStamp, gps_directory, 3 /* data size */,
83*4d671364SKiyoung Kim preview_image_data->gps.time_stamp)) {
84*4d671364SKiyoung Kim return;
85*4d671364SKiyoung Kim }
86*4d671364SKiyoung Kim
87*4d671364SKiyoung Kim const size_t kGpsDateStampSize = 11;
88*4d671364SKiyoung Kim if (!gps_directory.Get(kGpsTagDateStamp,
89*4d671364SKiyoung Kim &preview_image_data->gps.date_stamp)) {
90*4d671364SKiyoung Kim return;
91*4d671364SKiyoung Kim }
92*4d671364SKiyoung Kim if (preview_image_data->gps.date_stamp.size() == kGpsDateStampSize) {
93*4d671364SKiyoung Kim // Resize the date_stamp to remove the "NULL" at the end of string.
94*4d671364SKiyoung Kim preview_image_data->gps.date_stamp.resize(kGpsDateStampSize - 1);
95*4d671364SKiyoung Kim } else {
96*4d671364SKiyoung Kim return;
97*4d671364SKiyoung Kim }
98*4d671364SKiyoung Kim
99*4d671364SKiyoung Kim if (gps_directory.Has(kGpsTagAltitudeRef) &&
100*4d671364SKiyoung Kim gps_directory.Has(kGpsTagAltitude)) {
101*4d671364SKiyoung Kim std::vector<std::uint8_t> bytes;
102*4d671364SKiyoung Kim if (!gps_directory.Get(kGpsTagAltitudeRef, &bytes) || bytes.empty() ||
103*4d671364SKiyoung Kim !GetRational(kGpsTagAltitude, gps_directory, 1,
104*4d671364SKiyoung Kim &preview_image_data->gps.altitude)) {
105*4d671364SKiyoung Kim return;
106*4d671364SKiyoung Kim }
107*4d671364SKiyoung Kim preview_image_data->gps.altitude_ref = bytes[0] != 0;
108*4d671364SKiyoung Kim }
109*4d671364SKiyoung Kim preview_image_data->gps.is_valid = true;
110*4d671364SKiyoung Kim }
111*4d671364SKiyoung Kim }
112*4d671364SKiyoung Kim
GetImageSize(const TiffDirectory & tiff_directory,StreamInterface * stream,Image * image)113*4d671364SKiyoung Kim void GetImageSize(const TiffDirectory& tiff_directory, StreamInterface* stream,
114*4d671364SKiyoung Kim Image* image) {
115*4d671364SKiyoung Kim switch (image->format) {
116*4d671364SKiyoung Kim case Image::kUncompressedRgb: {
117*4d671364SKiyoung Kim GetFullDimension16(tiff_directory, &image->width, &image->height);
118*4d671364SKiyoung Kim break;
119*4d671364SKiyoung Kim }
120*4d671364SKiyoung Kim case Image::kJpegCompressed: {
121*4d671364SKiyoung Kim GetJpegDimensions(image->offset, stream, &image->width, &image->height);
122*4d671364SKiyoung Kim break;
123*4d671364SKiyoung Kim }
124*4d671364SKiyoung Kim default: { return; }
125*4d671364SKiyoung Kim }
126*4d671364SKiyoung Kim }
127*4d671364SKiyoung Kim
FillPreviewImageData(const TiffDirectory & tiff_directory,StreamInterface * stream,PreviewImageData * preview_image_data)128*4d671364SKiyoung Kim bool FillPreviewImageData(const TiffDirectory& tiff_directory,
129*4d671364SKiyoung Kim StreamInterface* stream,
130*4d671364SKiyoung Kim PreviewImageData* preview_image_data) {
131*4d671364SKiyoung Kim bool success = true;
132*4d671364SKiyoung Kim // Get preview or thumbnail. The code assumes that only thumbnails can be
133*4d671364SKiyoung Kim // uncompressed. Preview images are always JPEG compressed.
134*4d671364SKiyoung Kim Image image;
135*4d671364SKiyoung Kim if (GetImageData(tiff_directory, stream, &image)) {
136*4d671364SKiyoung Kim if (IsThumbnail(image)) {
137*4d671364SKiyoung Kim preview_image_data->thumbnail = image;
138*4d671364SKiyoung Kim } else if (image.format == Image::kJpegCompressed) {
139*4d671364SKiyoung Kim preview_image_data->preview = image;
140*4d671364SKiyoung Kim }
141*4d671364SKiyoung Kim }
142*4d671364SKiyoung Kim
143*4d671364SKiyoung Kim // Get exif_orientation if it was not set already.
144*4d671364SKiyoung Kim if (tiff_directory.Has(kTiffTagOrientation) &&
145*4d671364SKiyoung Kim preview_image_data->exif_orientation == 1) {
146*4d671364SKiyoung Kim success &= tiff_directory.Get(kTiffTagOrientation,
147*4d671364SKiyoung Kim &preview_image_data->exif_orientation);
148*4d671364SKiyoung Kim }
149*4d671364SKiyoung Kim
150*4d671364SKiyoung Kim // Get color_space
151*4d671364SKiyoung Kim if (tiff_directory.Has(kExifTagColorSpace)) {
152*4d671364SKiyoung Kim std::uint32_t color_space;
153*4d671364SKiyoung Kim if (tiff_directory.Get(kExifTagColorSpace, &color_space)) {
154*4d671364SKiyoung Kim if (color_space == 1) {
155*4d671364SKiyoung Kim preview_image_data->color_space = PreviewImageData::kSrgb;
156*4d671364SKiyoung Kim } else if (color_space == 65535 || color_space == 2) {
157*4d671364SKiyoung Kim preview_image_data->color_space = PreviewImageData::kAdobeRgb;
158*4d671364SKiyoung Kim }
159*4d671364SKiyoung Kim } else {
160*4d671364SKiyoung Kim success = false;
161*4d671364SKiyoung Kim }
162*4d671364SKiyoung Kim }
163*4d671364SKiyoung Kim
164*4d671364SKiyoung Kim success &= GetFullDimension32(tiff_directory, &preview_image_data->full_width,
165*4d671364SKiyoung Kim &preview_image_data->full_height);
166*4d671364SKiyoung Kim
167*4d671364SKiyoung Kim if (tiff_directory.Has(kTiffTagMake)) {
168*4d671364SKiyoung Kim success &= tiff_directory.Get(kTiffTagMake, &preview_image_data->maker);
169*4d671364SKiyoung Kim }
170*4d671364SKiyoung Kim
171*4d671364SKiyoung Kim if (tiff_directory.Has(kTiffTagModel)) {
172*4d671364SKiyoung Kim success &= tiff_directory.Get(kTiffTagModel, &preview_image_data->model);
173*4d671364SKiyoung Kim }
174*4d671364SKiyoung Kim
175*4d671364SKiyoung Kim if (tiff_directory.Has(kTiffTagCfaPatternDim)) {
176*4d671364SKiyoung Kim std::vector<std::uint32_t> cfa_pattern_dim;
177*4d671364SKiyoung Kim if (tiff_directory.Get(kTiffTagCfaPatternDim, &cfa_pattern_dim) &&
178*4d671364SKiyoung Kim cfa_pattern_dim.size() == 2) {
179*4d671364SKiyoung Kim preview_image_data->cfa_pattern_dim[0] = cfa_pattern_dim[0];
180*4d671364SKiyoung Kim preview_image_data->cfa_pattern_dim[1] = cfa_pattern_dim[1];
181*4d671364SKiyoung Kim }
182*4d671364SKiyoung Kim }
183*4d671364SKiyoung Kim
184*4d671364SKiyoung Kim if (tiff_directory.Has(kExifTagDateTimeOriginal)) {
185*4d671364SKiyoung Kim success &= tiff_directory.Get(kExifTagDateTimeOriginal,
186*4d671364SKiyoung Kim &preview_image_data->date_time);
187*4d671364SKiyoung Kim }
188*4d671364SKiyoung Kim
189*4d671364SKiyoung Kim if (tiff_directory.Has(kExifTagIsoSpeed)) {
190*4d671364SKiyoung Kim success &= tiff_directory.Get(kExifTagIsoSpeed, &preview_image_data->iso);
191*4d671364SKiyoung Kim } else if (tiff_directory.Has(kPanaTagIso)) {
192*4d671364SKiyoung Kim success &= tiff_directory.Get(kPanaTagIso, &preview_image_data->iso);
193*4d671364SKiyoung Kim }
194*4d671364SKiyoung Kim
195*4d671364SKiyoung Kim if (tiff_directory.Has(kExifTagExposureTime)) {
196*4d671364SKiyoung Kim success &= GetRational(kExifTagExposureTime, tiff_directory, 1,
197*4d671364SKiyoung Kim &preview_image_data->exposure_time);
198*4d671364SKiyoung Kim }
199*4d671364SKiyoung Kim
200*4d671364SKiyoung Kim if (tiff_directory.Has(kExifTagFnumber)) {
201*4d671364SKiyoung Kim success &= GetRational(kExifTagFnumber, tiff_directory, 1,
202*4d671364SKiyoung Kim &preview_image_data->fnumber);
203*4d671364SKiyoung Kim }
204*4d671364SKiyoung Kim
205*4d671364SKiyoung Kim if (tiff_directory.Has(kExifTagFocalLength)) {
206*4d671364SKiyoung Kim success &= GetRational(kExifTagFocalLength, tiff_directory, 1,
207*4d671364SKiyoung Kim &preview_image_data->focal_length);
208*4d671364SKiyoung Kim }
209*4d671364SKiyoung Kim
210*4d671364SKiyoung Kim return success;
211*4d671364SKiyoung Kim }
212*4d671364SKiyoung Kim
FindFirstTagInIfds(const TiffDirectory::Tag & tag,const IfdVector & tiff_directory)213*4d671364SKiyoung Kim const TiffDirectory* FindFirstTagInIfds(const TiffDirectory::Tag& tag,
214*4d671364SKiyoung Kim const IfdVector& tiff_directory) {
215*4d671364SKiyoung Kim for (std::uint32_t i = 0; i < tiff_directory.size(); ++i) {
216*4d671364SKiyoung Kim if (tiff_directory[i].Has(tag)) {
217*4d671364SKiyoung Kim return &tiff_directory[i];
218*4d671364SKiyoung Kim }
219*4d671364SKiyoung Kim
220*4d671364SKiyoung Kim // Recursively search sub directories.
221*4d671364SKiyoung Kim const TiffDirectory* sub_directory =
222*4d671364SKiyoung Kim FindFirstTagInIfds(tag, tiff_directory[i].GetSubDirectories());
223*4d671364SKiyoung Kim if (sub_directory != NULL) {
224*4d671364SKiyoung Kim return sub_directory;
225*4d671364SKiyoung Kim }
226*4d671364SKiyoung Kim }
227*4d671364SKiyoung Kim return NULL;
228*4d671364SKiyoung Kim }
229*4d671364SKiyoung Kim
230*4d671364SKiyoung Kim // Return true if all data blocks are ordered one after the other without gaps.
OffsetsAreConsecutive(const std::vector<std::uint32_t> & strip_offsets,const std::vector<std::uint32_t> & strip_byte_counts)231*4d671364SKiyoung Kim bool OffsetsAreConsecutive(
232*4d671364SKiyoung Kim const std::vector<std::uint32_t>& strip_offsets,
233*4d671364SKiyoung Kim const std::vector<std::uint32_t>& strip_byte_counts) {
234*4d671364SKiyoung Kim if (strip_offsets.size() != strip_byte_counts.size() ||
235*4d671364SKiyoung Kim strip_offsets.empty()) {
236*4d671364SKiyoung Kim return false;
237*4d671364SKiyoung Kim }
238*4d671364SKiyoung Kim
239*4d671364SKiyoung Kim for (size_t i = 0; i < strip_offsets.size() - 1; ++i) {
240*4d671364SKiyoung Kim if (strip_offsets[i] + strip_byte_counts[i] != strip_offsets[i + 1]) {
241*4d671364SKiyoung Kim return false;
242*4d671364SKiyoung Kim }
243*4d671364SKiyoung Kim }
244*4d671364SKiyoung Kim return true;
245*4d671364SKiyoung Kim }
246*4d671364SKiyoung Kim
247*4d671364SKiyoung Kim // Gets the SubIfd content.
ParseSubIfds(const std::uint32_t tiff_offset,const TagSet & desired_tags,const std::uint32_t max_number_ifds,const Endian endian,StreamInterface * stream,TiffDirectory * tiff_ifd)248*4d671364SKiyoung Kim bool ParseSubIfds(const std::uint32_t tiff_offset, const TagSet& desired_tags,
249*4d671364SKiyoung Kim const std::uint32_t max_number_ifds, const Endian endian,
250*4d671364SKiyoung Kim StreamInterface* stream, TiffDirectory* tiff_ifd) {
251*4d671364SKiyoung Kim if (tiff_ifd->Has(kTiffTagSubIfd)) {
252*4d671364SKiyoung Kim std::uint32_t offset = 0;
253*4d671364SKiyoung Kim std::uint32_t length = 0;
254*4d671364SKiyoung Kim tiff_ifd->GetOffsetAndLength(kTiffTagSubIfd, TIFF_TYPE_LONG, &offset,
255*4d671364SKiyoung Kim &length);
256*4d671364SKiyoung Kim length /= 4; // length in bytes divided by 4 gives number of IFDs.
257*4d671364SKiyoung Kim for (std::uint32_t j = 0; j < length && j < max_number_ifds; ++j) {
258*4d671364SKiyoung Kim std::uint32_t sub_offset;
259*4d671364SKiyoung Kim if (!Get32u(stream, offset + 4 * j, endian, &sub_offset)) {
260*4d671364SKiyoung Kim return false;
261*4d671364SKiyoung Kim }
262*4d671364SKiyoung Kim
263*4d671364SKiyoung Kim std::uint32_t next_ifd_offset;
264*4d671364SKiyoung Kim TiffDirectory sub_ifd(static_cast<Endian>(endian));
265*4d671364SKiyoung Kim if (!ParseDirectory(tiff_offset, sub_offset, endian, desired_tags, stream,
266*4d671364SKiyoung Kim &sub_ifd, &next_ifd_offset)) {
267*4d671364SKiyoung Kim return false;
268*4d671364SKiyoung Kim }
269*4d671364SKiyoung Kim
270*4d671364SKiyoung Kim tiff_ifd->AddSubDirectory(sub_ifd);
271*4d671364SKiyoung Kim }
272*4d671364SKiyoung Kim }
273*4d671364SKiyoung Kim return true;
274*4d671364SKiyoung Kim }
275*4d671364SKiyoung Kim
276*4d671364SKiyoung Kim } // namespace
277*4d671364SKiyoung Kim
Get16u(StreamInterface * stream,const std::uint32_t offset,const Endian & endian,std::uint16_t * value)278*4d671364SKiyoung Kim bool Get16u(StreamInterface* stream, const std::uint32_t offset,
279*4d671364SKiyoung Kim const Endian& endian, std::uint16_t* value) {
280*4d671364SKiyoung Kim std::uint8_t data[2];
281*4d671364SKiyoung Kim if (stream->GetData(offset, 2, data) == kOk) {
282*4d671364SKiyoung Kim if (endian == kBigEndian) {
283*4d671364SKiyoung Kim *value = (data[0] * 0x100) | data[1];
284*4d671364SKiyoung Kim } else {
285*4d671364SKiyoung Kim *value = (data[1] * 0x100) | data[0];
286*4d671364SKiyoung Kim }
287*4d671364SKiyoung Kim return true;
288*4d671364SKiyoung Kim } else {
289*4d671364SKiyoung Kim return false;
290*4d671364SKiyoung Kim }
291*4d671364SKiyoung Kim }
292*4d671364SKiyoung Kim
Get32u(StreamInterface * stream,const std::uint32_t offset,const Endian & endian,std::uint32_t * value)293*4d671364SKiyoung Kim bool Get32u(StreamInterface* stream, const std::uint32_t offset,
294*4d671364SKiyoung Kim const Endian& endian, std::uint32_t* value) {
295*4d671364SKiyoung Kim std::uint8_t data[4];
296*4d671364SKiyoung Kim if (stream->GetData(offset, 4, data) == kOk) {
297*4d671364SKiyoung Kim if (endian == kBigEndian) {
298*4d671364SKiyoung Kim *value = (data[0] * 0x1000000u) | (data[1] * 0x10000u) |
299*4d671364SKiyoung Kim (data[2] * 0x100u) | data[3];
300*4d671364SKiyoung Kim } else {
301*4d671364SKiyoung Kim *value = (data[3] * 0x1000000u) | (data[2] * 0x10000u) |
302*4d671364SKiyoung Kim (data[1] * 0x100u) | data[0];
303*4d671364SKiyoung Kim }
304*4d671364SKiyoung Kim return true;
305*4d671364SKiyoung Kim } else {
306*4d671364SKiyoung Kim return false;
307*4d671364SKiyoung Kim }
308*4d671364SKiyoung Kim }
309*4d671364SKiyoung Kim
GetData(const size_t offset,const size_t length,StreamInterface * stream,Error * error)310*4d671364SKiyoung Kim std::vector<std::uint8_t> GetData(const size_t offset, const size_t length,
311*4d671364SKiyoung Kim StreamInterface* stream, Error* error) {
312*4d671364SKiyoung Kim // Read in chunks with a maximum size of 1 MiB.
313*4d671364SKiyoung Kim const size_t kChunkSize = 1048576;
314*4d671364SKiyoung Kim
315*4d671364SKiyoung Kim std::vector<std::uint8_t> data;
316*4d671364SKiyoung Kim size_t processed_data = 0;
317*4d671364SKiyoung Kim while (*error == kOk && processed_data < length) {
318*4d671364SKiyoung Kim size_t chunk_length = kChunkSize;
319*4d671364SKiyoung Kim if (length - data.size() < kChunkSize) {
320*4d671364SKiyoung Kim chunk_length = length - data.size();
321*4d671364SKiyoung Kim }
322*4d671364SKiyoung Kim
323*4d671364SKiyoung Kim data.resize(processed_data + chunk_length);
324*4d671364SKiyoung Kim *error = stream->GetData(offset + processed_data, chunk_length,
325*4d671364SKiyoung Kim &data[processed_data]);
326*4d671364SKiyoung Kim
327*4d671364SKiyoung Kim processed_data += chunk_length;
328*4d671364SKiyoung Kim }
329*4d671364SKiyoung Kim return data;
330*4d671364SKiyoung Kim }
331*4d671364SKiyoung Kim
GetEndianness(const std::uint32_t tiff_offset,StreamInterface * stream,Endian * endian)332*4d671364SKiyoung Kim bool GetEndianness(const std::uint32_t tiff_offset, StreamInterface* stream,
333*4d671364SKiyoung Kim Endian* endian) {
334*4d671364SKiyoung Kim const std::uint8_t kTiffBigEndianMagic[] = {'M', 'M'};
335*4d671364SKiyoung Kim const std::uint8_t kTiffLittleEndianMagic[] = {'I', 'I'};
336*4d671364SKiyoung Kim std::uint8_t tiff_endian[sizeof(kTiffBigEndianMagic)];
337*4d671364SKiyoung Kim if (stream->GetData(tiff_offset, sizeof(tiff_endian), &tiff_endian[0]) !=
338*4d671364SKiyoung Kim kOk) {
339*4d671364SKiyoung Kim return false;
340*4d671364SKiyoung Kim }
341*4d671364SKiyoung Kim
342*4d671364SKiyoung Kim if (!memcmp(tiff_endian, kTiffLittleEndianMagic, sizeof(tiff_endian))) {
343*4d671364SKiyoung Kim *endian = kLittleEndian;
344*4d671364SKiyoung Kim return true;
345*4d671364SKiyoung Kim } else if (!memcmp(tiff_endian, kTiffBigEndianMagic, sizeof(tiff_endian))) {
346*4d671364SKiyoung Kim *endian = kBigEndian;
347*4d671364SKiyoung Kim return true;
348*4d671364SKiyoung Kim } else {
349*4d671364SKiyoung Kim return false;
350*4d671364SKiyoung Kim }
351*4d671364SKiyoung Kim }
352*4d671364SKiyoung Kim
GetImageData(const TiffDirectory & tiff_directory,StreamInterface * stream,Image * image)353*4d671364SKiyoung Kim bool GetImageData(const TiffDirectory& tiff_directory, StreamInterface* stream,
354*4d671364SKiyoung Kim Image* image) {
355*4d671364SKiyoung Kim std::uint32_t length = 0;
356*4d671364SKiyoung Kim std::uint32_t offset = 0;
357*4d671364SKiyoung Kim
358*4d671364SKiyoung Kim if (tiff_directory.Has(kTiffTagJpegOffset) &&
359*4d671364SKiyoung Kim tiff_directory.Has(kTiffTagJpegByteCount)) {
360*4d671364SKiyoung Kim if (!tiff_directory.Get(kTiffTagJpegOffset, &offset) ||
361*4d671364SKiyoung Kim !tiff_directory.Get(kTiffTagJpegByteCount, &length)) {
362*4d671364SKiyoung Kim return false;
363*4d671364SKiyoung Kim }
364*4d671364SKiyoung Kim image->format = Image::kJpegCompressed;
365*4d671364SKiyoung Kim } else if (tiff_directory.Has(kTiffTagStripOffsets) &&
366*4d671364SKiyoung Kim tiff_directory.Has(kTiffTagStripByteCounts)) {
367*4d671364SKiyoung Kim std::vector<std::uint32_t> strip_offsets;
368*4d671364SKiyoung Kim std::vector<std::uint32_t> strip_byte_counts;
369*4d671364SKiyoung Kim if (!tiff_directory.Get(kTiffTagStripOffsets, &strip_offsets) ||
370*4d671364SKiyoung Kim !tiff_directory.Get(kTiffTagStripByteCounts, &strip_byte_counts)) {
371*4d671364SKiyoung Kim return false;
372*4d671364SKiyoung Kim }
373*4d671364SKiyoung Kim
374*4d671364SKiyoung Kim std::uint32_t compression = 0;
375*4d671364SKiyoung Kim if (!OffsetsAreConsecutive(strip_offsets, strip_byte_counts) ||
376*4d671364SKiyoung Kim !tiff_directory.Get(kTiffTagCompression, &compression)) {
377*4d671364SKiyoung Kim return false;
378*4d671364SKiyoung Kim }
379*4d671364SKiyoung Kim
380*4d671364SKiyoung Kim std::uint32_t photometric_interpretation = 0;
381*4d671364SKiyoung Kim if (tiff_directory.Get(kTiffTagPhotometric, &photometric_interpretation) &&
382*4d671364SKiyoung Kim photometric_interpretation != 2 /* RGB */ &&
383*4d671364SKiyoung Kim photometric_interpretation != 6 /* YCbCr */) {
384*4d671364SKiyoung Kim return false;
385*4d671364SKiyoung Kim }
386*4d671364SKiyoung Kim
387*4d671364SKiyoung Kim switch (compression) {
388*4d671364SKiyoung Kim case 1: /*uncompressed*/
389*4d671364SKiyoung Kim image->format = Image::kUncompressedRgb;
390*4d671364SKiyoung Kim break;
391*4d671364SKiyoung Kim case 6: /* JPEG(old) */
392*4d671364SKiyoung Kim case 7: /* JPEG */
393*4d671364SKiyoung Kim image->format = Image::kJpegCompressed;
394*4d671364SKiyoung Kim break;
395*4d671364SKiyoung Kim default:
396*4d671364SKiyoung Kim return false;
397*4d671364SKiyoung Kim }
398*4d671364SKiyoung Kim length = static_cast<std::uint32_t>(std::accumulate(
399*4d671364SKiyoung Kim strip_byte_counts.begin(), strip_byte_counts.end(), 0U));
400*4d671364SKiyoung Kim offset = strip_offsets[0];
401*4d671364SKiyoung Kim } else if (tiff_directory.Has(kPanaTagJpegImage)) {
402*4d671364SKiyoung Kim if (!tiff_directory.GetOffsetAndLength(
403*4d671364SKiyoung Kim kPanaTagJpegImage, TIFF_TYPE_UNDEFINED, &offset, &length)) {
404*4d671364SKiyoung Kim return false;
405*4d671364SKiyoung Kim }
406*4d671364SKiyoung Kim image->format = Image::kJpegCompressed;
407*4d671364SKiyoung Kim } else {
408*4d671364SKiyoung Kim return false;
409*4d671364SKiyoung Kim }
410*4d671364SKiyoung Kim
411*4d671364SKiyoung Kim image->length = length;
412*4d671364SKiyoung Kim image->offset = offset;
413*4d671364SKiyoung Kim GetImageSize(tiff_directory, stream, image);
414*4d671364SKiyoung Kim return true;
415*4d671364SKiyoung Kim }
416*4d671364SKiyoung Kim
GetJpegDimensions(const std::uint32_t jpeg_offset,StreamInterface * stream,std::uint16_t * width,std::uint16_t * height)417*4d671364SKiyoung Kim bool GetJpegDimensions(const std::uint32_t jpeg_offset, StreamInterface* stream,
418*4d671364SKiyoung Kim std::uint16_t* width, std::uint16_t* height) {
419*4d671364SKiyoung Kim const Endian endian = kBigEndian;
420*4d671364SKiyoung Kim std::uint32_t offset = jpeg_offset;
421*4d671364SKiyoung Kim std::uint16_t segment;
422*4d671364SKiyoung Kim
423*4d671364SKiyoung Kim // Parse the JPEG header until we find Frame0 which contains the image width
424*4d671364SKiyoung Kim // and height or the actual image data starts (StartOfScan)
425*4d671364SKiyoung Kim do {
426*4d671364SKiyoung Kim if (!Get16u(stream, offset, endian, &segment)) {
427*4d671364SKiyoung Kim return false;
428*4d671364SKiyoung Kim }
429*4d671364SKiyoung Kim offset += 2;
430*4d671364SKiyoung Kim
431*4d671364SKiyoung Kim switch (segment) {
432*4d671364SKiyoung Kim case kStartOfImage:
433*4d671364SKiyoung Kim break;
434*4d671364SKiyoung Kim case kStartOfFrame:
435*4d671364SKiyoung Kim return Get16u(stream, offset + 3, endian, height) &&
436*4d671364SKiyoung Kim Get16u(stream, offset + 5, endian, width);
437*4d671364SKiyoung Kim default: {
438*4d671364SKiyoung Kim std::uint16_t length;
439*4d671364SKiyoung Kim if (!Get16u(stream, offset, endian, &length)) {
440*4d671364SKiyoung Kim return false;
441*4d671364SKiyoung Kim }
442*4d671364SKiyoung Kim offset += length;
443*4d671364SKiyoung Kim }
444*4d671364SKiyoung Kim }
445*4d671364SKiyoung Kim } while (segment != kStartOfScan);
446*4d671364SKiyoung Kim
447*4d671364SKiyoung Kim // No width and hight information found.
448*4d671364SKiyoung Kim return false;
449*4d671364SKiyoung Kim }
450*4d671364SKiyoung Kim
GetRational(const TiffDirectory::Tag & tag,const TiffDirectory & directory,const int data_size,PreviewImageData::Rational * data)451*4d671364SKiyoung Kim bool GetRational(const TiffDirectory::Tag& tag, const TiffDirectory& directory,
452*4d671364SKiyoung Kim const int data_size, PreviewImageData::Rational* data) {
453*4d671364SKiyoung Kim std::vector<Rational> value;
454*4d671364SKiyoung Kim if (directory.Get(tag, &value) &&
455*4d671364SKiyoung Kim value.size() == static_cast<size_t>(data_size)) {
456*4d671364SKiyoung Kim for (size_t i = 0; i < value.size(); ++i) {
457*4d671364SKiyoung Kim data[i].numerator = value[i].numerator;
458*4d671364SKiyoung Kim data[i].denominator = value[i].denominator;
459*4d671364SKiyoung Kim }
460*4d671364SKiyoung Kim return true;
461*4d671364SKiyoung Kim }
462*4d671364SKiyoung Kim return false;
463*4d671364SKiyoung Kim }
464*4d671364SKiyoung Kim
IsThumbnail(const Image & image,const int max_dimension)465*4d671364SKiyoung Kim bool IsThumbnail(const Image& image, const int max_dimension) {
466*4d671364SKiyoung Kim return image.width <= max_dimension && image.height <= max_dimension;
467*4d671364SKiyoung Kim }
468*4d671364SKiyoung Kim
ParseDirectory(const std::uint32_t tiff_offset,const std::uint32_t ifd_offset,const Endian endian,const TagSet & desired_tags,StreamInterface * stream,TiffDirectory * tiff_directory,std::uint32_t * next_ifd_offset)469*4d671364SKiyoung Kim bool ParseDirectory(const std::uint32_t tiff_offset,
470*4d671364SKiyoung Kim const std::uint32_t ifd_offset, const Endian endian,
471*4d671364SKiyoung Kim const TagSet& desired_tags, StreamInterface* stream,
472*4d671364SKiyoung Kim TiffDirectory* tiff_directory,
473*4d671364SKiyoung Kim std::uint32_t* next_ifd_offset) {
474*4d671364SKiyoung Kim std::uint16_t number_of_entries;
475*4d671364SKiyoung Kim if (!Get16u(stream, ifd_offset, endian, &number_of_entries)) {
476*4d671364SKiyoung Kim return false;
477*4d671364SKiyoung Kim }
478*4d671364SKiyoung Kim
479*4d671364SKiyoung Kim for (std::uint32_t i = 0;
480*4d671364SKiyoung Kim i < static_cast<std::uint32_t>(number_of_entries) * 12; i += 12) {
481*4d671364SKiyoung Kim std::uint16_t tag;
482*4d671364SKiyoung Kim std::uint16_t type;
483*4d671364SKiyoung Kim std::uint32_t number_of_elements;
484*4d671364SKiyoung Kim if (Get16u(stream, ifd_offset + 2 + i, endian, &tag) &&
485*4d671364SKiyoung Kim Get16u(stream, ifd_offset + 4 + i, endian, &type) &&
486*4d671364SKiyoung Kim Get32u(stream, ifd_offset + 6 + i, endian, &number_of_elements)) {
487*4d671364SKiyoung Kim // Check if the current tag should be handled.
488*4d671364SKiyoung Kim if (desired_tags.count(static_cast<TiffDirectory::Tag>(tag)) != 1) {
489*4d671364SKiyoung Kim continue;
490*4d671364SKiyoung Kim }
491*4d671364SKiyoung Kim } else {
492*4d671364SKiyoung Kim return false;
493*4d671364SKiyoung Kim }
494*4d671364SKiyoung Kim
495*4d671364SKiyoung Kim const size_t type_size = SizeOfType(type, nullptr /* no error */);
496*4d671364SKiyoung Kim
497*4d671364SKiyoung Kim // Check that type_size * number_of_elements does not exceed UINT32_MAX.
498*4d671364SKiyoung Kim if (type_size != 0 && number_of_elements > UINT32_MAX / type_size) {
499*4d671364SKiyoung Kim return false;
500*4d671364SKiyoung Kim }
501*4d671364SKiyoung Kim const size_t byte_count =
502*4d671364SKiyoung Kim type_size * static_cast<size_t>(number_of_elements);
503*4d671364SKiyoung Kim
504*4d671364SKiyoung Kim std::uint32_t value_offset;
505*4d671364SKiyoung Kim if (byte_count > 4 &&
506*4d671364SKiyoung Kim Get32u(stream, ifd_offset + 10 + i, endian, &value_offset)) {
507*4d671364SKiyoung Kim value_offset += tiff_offset;
508*4d671364SKiyoung Kim } else if (byte_count != 0) {
509*4d671364SKiyoung Kim value_offset = ifd_offset + 10 + i;
510*4d671364SKiyoung Kim } else {
511*4d671364SKiyoung Kim // Ignore entries with an invalid byte count.
512*4d671364SKiyoung Kim continue;
513*4d671364SKiyoung Kim }
514*4d671364SKiyoung Kim
515*4d671364SKiyoung Kim Error error = kOk;
516*4d671364SKiyoung Kim const std::vector<std::uint8_t> data =
517*4d671364SKiyoung Kim GetData(value_offset, byte_count, stream, &error);
518*4d671364SKiyoung Kim if (error != kOk) {
519*4d671364SKiyoung Kim return false;
520*4d671364SKiyoung Kim }
521*4d671364SKiyoung Kim tiff_directory->AddEntry(tag, type, number_of_elements, value_offset, data);
522*4d671364SKiyoung Kim }
523*4d671364SKiyoung Kim
524*4d671364SKiyoung Kim return Get32u(stream, ifd_offset + 2u + number_of_entries * 12u, endian,
525*4d671364SKiyoung Kim next_ifd_offset);
526*4d671364SKiyoung Kim }
527*4d671364SKiyoung Kim
GetExifOrientation(StreamInterface * stream,const std::uint32_t offset,std::uint32_t * orientation)528*4d671364SKiyoung Kim bool GetExifOrientation(StreamInterface* stream, const std::uint32_t offset,
529*4d671364SKiyoung Kim std::uint32_t* orientation) {
530*4d671364SKiyoung Kim const TagSet kOrientationTagSet = {kTiffTagOrientation};
531*4d671364SKiyoung Kim const std::uint32_t kNumberOfIfds = 1;
532*4d671364SKiyoung Kim
533*4d671364SKiyoung Kim TiffContent tiff_content;
534*4d671364SKiyoung Kim if (!TiffParser(stream, offset)
535*4d671364SKiyoung Kim .Parse(kOrientationTagSet, kNumberOfIfds, &tiff_content)) {
536*4d671364SKiyoung Kim return false;
537*4d671364SKiyoung Kim }
538*4d671364SKiyoung Kim
539*4d671364SKiyoung Kim for (const auto& tiff_directory : tiff_content.tiff_directory) {
540*4d671364SKiyoung Kim if (tiff_directory.Has(kTiffTagOrientation) &&
541*4d671364SKiyoung Kim tiff_directory.Get(kTiffTagOrientation, orientation)) {
542*4d671364SKiyoung Kim return true;
543*4d671364SKiyoung Kim }
544*4d671364SKiyoung Kim }
545*4d671364SKiyoung Kim
546*4d671364SKiyoung Kim return false;
547*4d671364SKiyoung Kim }
548*4d671364SKiyoung Kim
GetFullDimension32(const TiffDirectory & tiff_directory,std::uint32_t * width,std::uint32_t * height)549*4d671364SKiyoung Kim bool GetFullDimension32(const TiffDirectory& tiff_directory,
550*4d671364SKiyoung Kim std::uint32_t* width, std::uint32_t* height) {
551*4d671364SKiyoung Kim // The sub file type needs to be 0 (main image) to contain a valid full
552*4d671364SKiyoung Kim // dimensions. This is important in particular for DNG.
553*4d671364SKiyoung Kim if (tiff_directory.Has(kTiffTagSubFileType)) {
554*4d671364SKiyoung Kim std::uint32_t sub_file_type;
555*4d671364SKiyoung Kim if (!tiff_directory.Get(kTiffTagSubFileType, &sub_file_type) ||
556*4d671364SKiyoung Kim sub_file_type != 0) {
557*4d671364SKiyoung Kim return false;
558*4d671364SKiyoung Kim }
559*4d671364SKiyoung Kim }
560*4d671364SKiyoung Kim
561*4d671364SKiyoung Kim if (tiff_directory.Has(kExifTagDefaultCropSize)) {
562*4d671364SKiyoung Kim if (!GetFullCropDimension(tiff_directory, width, height)) {
563*4d671364SKiyoung Kim return false;
564*4d671364SKiyoung Kim }
565*4d671364SKiyoung Kim } else if (tiff_directory.Has(kExifTagWidth) &&
566*4d671364SKiyoung Kim tiff_directory.Has(kExifTagHeight)) {
567*4d671364SKiyoung Kim if (!tiff_directory.Get(kExifTagWidth, width) ||
568*4d671364SKiyoung Kim !tiff_directory.Get(kExifTagHeight, height)) {
569*4d671364SKiyoung Kim return false;
570*4d671364SKiyoung Kim }
571*4d671364SKiyoung Kim } else if (tiff_directory.Has(kTiffTagImageWidth) &&
572*4d671364SKiyoung Kim tiff_directory.Has(kTiffTagImageLength)) {
573*4d671364SKiyoung Kim if (!tiff_directory.Get(kTiffTagImageWidth, width) ||
574*4d671364SKiyoung Kim !tiff_directory.Get(kTiffTagImageLength, height)) {
575*4d671364SKiyoung Kim return false;
576*4d671364SKiyoung Kim }
577*4d671364SKiyoung Kim } else if (tiff_directory.Has(kPanaTagTopBorder) &&
578*4d671364SKiyoung Kim tiff_directory.Has(kPanaTagLeftBorder) &&
579*4d671364SKiyoung Kim tiff_directory.Has(kPanaTagBottomBorder) &&
580*4d671364SKiyoung Kim tiff_directory.Has(kPanaTagRightBorder)) {
581*4d671364SKiyoung Kim std::uint32_t left;
582*4d671364SKiyoung Kim std::uint32_t right;
583*4d671364SKiyoung Kim std::uint32_t top;
584*4d671364SKiyoung Kim std::uint32_t bottom;
585*4d671364SKiyoung Kim if (tiff_directory.Get(kPanaTagLeftBorder, &left) &&
586*4d671364SKiyoung Kim tiff_directory.Get(kPanaTagRightBorder, &right) &&
587*4d671364SKiyoung Kim tiff_directory.Get(kPanaTagTopBorder, &top) &&
588*4d671364SKiyoung Kim tiff_directory.Get(kPanaTagBottomBorder, &bottom) && bottom > top &&
589*4d671364SKiyoung Kim right > left) {
590*4d671364SKiyoung Kim *height = bottom - top;
591*4d671364SKiyoung Kim *width = right - left;
592*4d671364SKiyoung Kim } else {
593*4d671364SKiyoung Kim return false;
594*4d671364SKiyoung Kim }
595*4d671364SKiyoung Kim }
596*4d671364SKiyoung Kim return true;
597*4d671364SKiyoung Kim }
598*4d671364SKiyoung Kim
GetFullCropDimension(const tiff_directory::TiffDirectory & tiff_directory,std::uint32_t * width,std::uint32_t * height)599*4d671364SKiyoung Kim bool GetFullCropDimension(const tiff_directory::TiffDirectory& tiff_directory,
600*4d671364SKiyoung Kim std::uint32_t* width, std::uint32_t* height) {
601*4d671364SKiyoung Kim if (!tiff_directory.Has(kExifTagDefaultCropSize)) {
602*4d671364SKiyoung Kim // This doesn't look right to return true here, as we have not written
603*4d671364SKiyoung Kim // anything to *width and *height. However, changing the return value here
604*4d671364SKiyoung Kim // causes a whole bunch of tests to fail.
605*4d671364SKiyoung Kim // TODO(timurrrr): Return false and fix the tests.
606*4d671364SKiyoung Kim // In fact, this whole if() seems to be not needed,
607*4d671364SKiyoung Kim // as tiff_directory(kExifTagDefaultCropSize) will return false below.
608*4d671364SKiyoung Kim return true;
609*4d671364SKiyoung Kim }
610*4d671364SKiyoung Kim
611*4d671364SKiyoung Kim std::vector<std::uint32_t> crop(2);
612*4d671364SKiyoung Kim if (tiff_directory.Get(kExifTagDefaultCropSize, &crop)) {
613*4d671364SKiyoung Kim if (crop.size() == 2 && crop[0] > 0 && crop[1] > 0) {
614*4d671364SKiyoung Kim *width = crop[0];
615*4d671364SKiyoung Kim *height = crop[1];
616*4d671364SKiyoung Kim return true;
617*4d671364SKiyoung Kim } else {
618*4d671364SKiyoung Kim return false;
619*4d671364SKiyoung Kim }
620*4d671364SKiyoung Kim }
621*4d671364SKiyoung Kim
622*4d671364SKiyoung Kim std::vector<Rational> crop_rational(2);
623*4d671364SKiyoung Kim if (tiff_directory.Get(kExifTagDefaultCropSize, &crop_rational)) {
624*4d671364SKiyoung Kim if (crop_rational.size() == 2 && crop_rational[0].numerator > 0 &&
625*4d671364SKiyoung Kim crop_rational[0].denominator > 0 && crop_rational[1].numerator > 0 &&
626*4d671364SKiyoung Kim crop_rational[1].denominator > 0) {
627*4d671364SKiyoung Kim *width = crop_rational[0].numerator / crop_rational[0].denominator;
628*4d671364SKiyoung Kim *height = crop_rational[1].numerator / crop_rational[1].denominator;
629*4d671364SKiyoung Kim return true;
630*4d671364SKiyoung Kim } else {
631*4d671364SKiyoung Kim return false;
632*4d671364SKiyoung Kim }
633*4d671364SKiyoung Kim }
634*4d671364SKiyoung Kim
635*4d671364SKiyoung Kim return false;
636*4d671364SKiyoung Kim }
637*4d671364SKiyoung Kim
TiffParser(StreamInterface * stream)638*4d671364SKiyoung Kim TiffParser::TiffParser(StreamInterface* stream) : stream_(stream) {}
639*4d671364SKiyoung Kim
TiffParser(StreamInterface * stream,const std::uint32_t offset)640*4d671364SKiyoung Kim TiffParser::TiffParser(StreamInterface* stream, const std::uint32_t offset)
641*4d671364SKiyoung Kim : stream_(stream), tiff_offset_(offset) {}
642*4d671364SKiyoung Kim
GetPreviewImageData(const TiffContent & tiff_content,PreviewImageData * preview_image_data)643*4d671364SKiyoung Kim bool TiffParser::GetPreviewImageData(const TiffContent& tiff_content,
644*4d671364SKiyoung Kim PreviewImageData* preview_image_data) {
645*4d671364SKiyoung Kim bool success = true;
646*4d671364SKiyoung Kim for (const auto& tiff_directory : tiff_content.tiff_directory) {
647*4d671364SKiyoung Kim success = FillPreviewImageData(tiff_directory, stream_, preview_image_data);
648*4d671364SKiyoung Kim if (success && tiff_directory.Has(kTiffTagExifIfd) &&
649*4d671364SKiyoung Kim tiff_content.exif_directory) {
650*4d671364SKiyoung Kim success = FillPreviewImageData(*tiff_content.exif_directory, stream_,
651*4d671364SKiyoung Kim preview_image_data);
652*4d671364SKiyoung Kim }
653*4d671364SKiyoung Kim if (success && tiff_directory.Has(kExifTagGps) &&
654*4d671364SKiyoung Kim tiff_content.gps_directory) {
655*4d671364SKiyoung Kim FillGpsPreviewImageData(*tiff_content.gps_directory, preview_image_data);
656*4d671364SKiyoung Kim }
657*4d671364SKiyoung Kim for (const auto& sub_directory : tiff_directory.GetSubDirectories()) {
658*4d671364SKiyoung Kim if (success) {
659*4d671364SKiyoung Kim success =
660*4d671364SKiyoung Kim FillPreviewImageData(sub_directory, stream_, preview_image_data);
661*4d671364SKiyoung Kim }
662*4d671364SKiyoung Kim }
663*4d671364SKiyoung Kim }
664*4d671364SKiyoung Kim return success;
665*4d671364SKiyoung Kim }
666*4d671364SKiyoung Kim
Parse(const TagSet & desired_tags,const std::uint16_t max_number_ifds,TiffContent * tiff_content)667*4d671364SKiyoung Kim bool TiffParser::Parse(const TagSet& desired_tags,
668*4d671364SKiyoung Kim const std::uint16_t max_number_ifds,
669*4d671364SKiyoung Kim TiffContent* tiff_content) {
670*4d671364SKiyoung Kim if (!tiff_content->tiff_directory.empty()) {
671*4d671364SKiyoung Kim return false; // You shall call Parse() only once.
672*4d671364SKiyoung Kim }
673*4d671364SKiyoung Kim
674*4d671364SKiyoung Kim const std::uint32_t kTiffIdentifierSize = 4;
675*4d671364SKiyoung Kim std::uint32_t offset_to_ifd = 0;
676*4d671364SKiyoung Kim if (!GetEndianness(tiff_offset_, stream_, &endian_) ||
677*4d671364SKiyoung Kim !Get32u(stream_, tiff_offset_ + kTiffIdentifierSize, endian_,
678*4d671364SKiyoung Kim &offset_to_ifd)) {
679*4d671364SKiyoung Kim return false;
680*4d671364SKiyoung Kim }
681*4d671364SKiyoung Kim
682*4d671364SKiyoung Kim if (!ParseIfd(tiff_offset_ + offset_to_ifd, desired_tags, max_number_ifds,
683*4d671364SKiyoung Kim &tiff_content->tiff_directory)) {
684*4d671364SKiyoung Kim return false;
685*4d671364SKiyoung Kim }
686*4d671364SKiyoung Kim
687*4d671364SKiyoung Kim // Get the Exif data.
688*4d671364SKiyoung Kim if (FindFirstTagInIfds(kTiffTagExifIfd, tiff_content->tiff_directory) !=
689*4d671364SKiyoung Kim nullptr) {
690*4d671364SKiyoung Kim const TiffDirectory* tiff_ifd =
691*4d671364SKiyoung Kim FindFirstTagInIfds(kTiffTagExifIfd, tiff_content->tiff_directory);
692*4d671364SKiyoung Kim std::uint32_t offset;
693*4d671364SKiyoung Kim if (tiff_ifd->Get(kTiffTagExifIfd, &offset)) {
694*4d671364SKiyoung Kim tiff_content->exif_directory.reset(new TiffDirectory(endian_));
695*4d671364SKiyoung Kim std::uint32_t next_ifd_offset;
696*4d671364SKiyoung Kim if (!ParseDirectory(
697*4d671364SKiyoung Kim tiff_offset_, tiff_offset_ + offset, endian_, desired_tags,
698*4d671364SKiyoung Kim stream_, tiff_content->exif_directory.get(), &next_ifd_offset)) {
699*4d671364SKiyoung Kim return false;
700*4d671364SKiyoung Kim }
701*4d671364SKiyoung Kim
702*4d671364SKiyoung Kim return ParseGpsData(tiff_ifd, tiff_content);
703*4d671364SKiyoung Kim }
704*4d671364SKiyoung Kim }
705*4d671364SKiyoung Kim
706*4d671364SKiyoung Kim // Get the GPS data from the tiff ifd.
707*4d671364SKiyoung Kim if (FindFirstTagInIfds(kExifTagGps, tiff_content->tiff_directory) !=
708*4d671364SKiyoung Kim nullptr) {
709*4d671364SKiyoung Kim const TiffDirectory* tiff_ifd =
710*4d671364SKiyoung Kim FindFirstTagInIfds(kExifTagGps, tiff_content->tiff_directory);
711*4d671364SKiyoung Kim return ParseGpsData(tiff_ifd, tiff_content);
712*4d671364SKiyoung Kim }
713*4d671364SKiyoung Kim
714*4d671364SKiyoung Kim return true;
715*4d671364SKiyoung Kim }
716*4d671364SKiyoung Kim
ParseIfd(const std::uint32_t ifd_offset,const TagSet & desired_tags,const std::uint16_t max_number_ifds,IfdVector * tiff_directory)717*4d671364SKiyoung Kim bool TiffParser::ParseIfd(const std::uint32_t ifd_offset,
718*4d671364SKiyoung Kim const TagSet& desired_tags,
719*4d671364SKiyoung Kim const std::uint16_t max_number_ifds,
720*4d671364SKiyoung Kim IfdVector* tiff_directory) {
721*4d671364SKiyoung Kim std::uint32_t next_ifd_offset;
722*4d671364SKiyoung Kim TiffDirectory tiff_ifd(static_cast<Endian>(endian_));
723*4d671364SKiyoung Kim if (!ParseDirectory(tiff_offset_, ifd_offset, endian_, desired_tags, stream_,
724*4d671364SKiyoung Kim &tiff_ifd, &next_ifd_offset) ||
725*4d671364SKiyoung Kim !ParseSubIfds(tiff_offset_, desired_tags, max_number_ifds, endian_,
726*4d671364SKiyoung Kim stream_, &tiff_ifd)) {
727*4d671364SKiyoung Kim return false;
728*4d671364SKiyoung Kim }
729*4d671364SKiyoung Kim
730*4d671364SKiyoung Kim tiff_directory->push_back(tiff_ifd);
731*4d671364SKiyoung Kim if (next_ifd_offset != 0 && tiff_directory->size() < max_number_ifds) {
732*4d671364SKiyoung Kim return ParseIfd(tiff_offset_ + next_ifd_offset, desired_tags,
733*4d671364SKiyoung Kim max_number_ifds, tiff_directory);
734*4d671364SKiyoung Kim }
735*4d671364SKiyoung Kim return true;
736*4d671364SKiyoung Kim }
737*4d671364SKiyoung Kim
ParseGpsData(const TiffDirectory * tiff_ifd,TiffContent * tiff_content)738*4d671364SKiyoung Kim bool TiffParser::ParseGpsData(const TiffDirectory* tiff_ifd,
739*4d671364SKiyoung Kim TiffContent* tiff_content) {
740*4d671364SKiyoung Kim std::uint32_t offset;
741*4d671364SKiyoung Kim if (tiff_ifd->Get(kExifTagGps, &offset)) {
742*4d671364SKiyoung Kim tiff_content->gps_directory.reset(new TiffDirectory(endian_));
743*4d671364SKiyoung Kim const TagSet gps_tags = {kGpsTagLatitudeRef, kGpsTagLatitude,
744*4d671364SKiyoung Kim kGpsTagLongitudeRef, kGpsTagLongitude,
745*4d671364SKiyoung Kim kGpsTagAltitudeRef, kGpsTagAltitude,
746*4d671364SKiyoung Kim kGpsTagTimeStamp, kGpsTagDateStamp};
747*4d671364SKiyoung Kim std::uint32_t next_ifd_offset;
748*4d671364SKiyoung Kim return ParseDirectory(tiff_offset_, tiff_offset_ + offset, endian_,
749*4d671364SKiyoung Kim gps_tags, stream_, tiff_content->gps_directory.get(),
750*4d671364SKiyoung Kim &next_ifd_offset);
751*4d671364SKiyoung Kim }
752*4d671364SKiyoung Kim return true;
753*4d671364SKiyoung Kim }
754*4d671364SKiyoung Kim
755*4d671364SKiyoung Kim } // namespace piex
756