blob: cad5bc4c099b186fc85faa973de1f18fadda7f25 [file] [log] [blame]
Jaesung Chung29345532016-01-14 02:28:47 +09001// Copyright 2015 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15////////////////////////////////////////////////////////////////////////////////
16
17#include "src/tiff_parser.h"
18
19#include <cstring>
20
21#include "src/tiff_directory/tiff_directory.h"
22
23namespace piex {
24namespace {
25
26using tiff_directory::Endian;
27using tiff_directory::Rational;
28using tiff_directory::SRational;
29using tiff_directory::SizeOfType;
30using tiff_directory::TIFF_TYPE_LONG;
31using tiff_directory::TIFF_TYPE_UNDEFINED;
32using tiff_directory::TiffDirectory;
33using tiff_directory::kBigEndian;
34using tiff_directory::kLittleEndian;
35
36// Specifies all tags that might be of interest to parse JPEG data.
37const std::uint32_t kStartOfFrame = 0xFFC0;
38const std::uint32_t kStartOfImage = 0xFFD8;
39const std::uint32_t kStartOfScan = 0xFFDA;
40
41// Reads the width and height of the full resolution image. The tag groups are
42// exclusive.
43bool GetFullDimension(const TiffDirectory& tiff_directory, std::uint32_t* width,
44 std::uint32_t* height) {
45 if (tiff_directory.Has(kExifTagWidth) && tiff_directory.Has(kExifTagHeight)) {
46 if (!tiff_directory.Get(kExifTagWidth, width) ||
47 !tiff_directory.Get(kExifTagHeight, height)) {
48 return false;
49 }
50 } else if (tiff_directory.Has(kTiffTagImageWidth) &&
51 tiff_directory.Has(kTiffTagImageLength)) {
52 if (!tiff_directory.Get(kTiffTagImageWidth, width) ||
53 !tiff_directory.Get(kTiffTagImageLength, height)) {
54 return false;
55 }
56 } else if (tiff_directory.Has(kPanaTagTopBorder) &&
57 tiff_directory.Has(kPanaTagLeftBorder) &&
58 tiff_directory.Has(kPanaTagBottomBorder) &&
59 tiff_directory.Has(kPanaTagRightBorder)) {
60 std::uint32_t left;
61 std::uint32_t right;
62 std::uint32_t top;
63 std::uint32_t bottom;
64 if (tiff_directory.Get(kPanaTagLeftBorder, &left) &&
65 tiff_directory.Get(kPanaTagRightBorder, &right) &&
66 tiff_directory.Get(kPanaTagTopBorder, &top) &&
67 tiff_directory.Get(kPanaTagBottomBorder, &bottom) && bottom > top &&
68 right > left) {
69 *height = bottom - top;
70 *width = right - left;
71 } else {
72 return false;
73 }
74 } else if (tiff_directory.Has(kExifTagDefaultCropSize)) {
75 std::vector<std::uint32_t> crop(2);
76 std::vector<Rational> crop_rational(2);
77 if (tiff_directory.Get(kExifTagDefaultCropSize, &crop)) {
78 *width = crop[0];
79 *height = crop[1];
80 } else if (tiff_directory.Get(kExifTagDefaultCropSize, &crop_rational) &&
81 crop_rational[0].denominator != 0 &&
82 crop_rational[1].denominator != 0) {
83 *width = crop_rational[0].numerator / crop_rational[0].denominator;
84 *height = crop_rational[1].numerator / crop_rational[1].denominator;
85 } else {
86 return false;
87 }
88 }
89 return true;
90}
91
92bool GetRational(const Tags& tag, const TiffDirectory& directory,
93 const int data_size, PreviewImageData::Rational* data) {
94 std::vector<Rational> value;
95 if (directory.Get(tag, &value)) {
96 for (size_t i = 0; i < value.size(); ++i) {
97 data[i].numerator = value[i].numerator;
98 data[i].denominator = value[i].denominator;
99 }
100 return true;
101 }
102 return false;
103}
104
105void FillGpsPreviewImageData(const TiffDirectory& gps_directory,
106 PreviewImageData* preview_image_data) {
107 if (gps_directory.Has(kGpsTagLatitudeRef) &&
108 gps_directory.Has(kGpsTagLatitude) &&
109 gps_directory.Has(kGpsTagLongitudeRef) &&
110 gps_directory.Has(kGpsTagLongitude) &&
111 gps_directory.Has(kGpsTagTimeStamp) &&
112 gps_directory.Has(kGpsTagDateStamp)) {
113 preview_image_data->gps.is_valid = false;
114 std::string value;
115 if (!gps_directory.Get(kGpsTagLatitudeRef, &value) || value.empty() ||
116 (value[0] != 'N' && value[0] != 'S') ||
117 !GetRational(kGpsTagLatitude, gps_directory, 3 /* data size */,
118 preview_image_data->gps.latitude)) {
119 return;
120 }
121 preview_image_data->gps.latitude_ref = value[0];
122
123 if (!gps_directory.Get(kGpsTagLongitudeRef, &value) || value.empty() ||
124 (value[0] != 'E' && value[0] != 'W') ||
125 !GetRational(kGpsTagLongitude, gps_directory, 3 /* data size */,
126 preview_image_data->gps.longitude)) {
127 return;
128 }
129 preview_image_data->gps.longitude_ref = value[0];
130
131 if (!GetRational(kGpsTagTimeStamp, gps_directory, 3 /* data size */,
132 preview_image_data->gps.time_stamp)) {
133 return;
134 }
135
136 constexpr size_t kGpsDateStampSize = 11;
137 if (!gps_directory.Get(kGpsTagDateStamp,
138 &preview_image_data->gps.date_stamp)) {
139 return;
140 }
141 if (preview_image_data->gps.date_stamp.size() == kGpsDateStampSize) {
142 // Resize the date_stamp to remove the "NULL" at the end of string.
143 preview_image_data->gps.date_stamp.resize(kGpsDateStampSize - 1);
144 } else {
145 return;
146 }
147
148 if (gps_directory.Has(kGpsTagAltitudeRef) &&
149 gps_directory.Has(kGpsTagAltitude)) {
150 std::vector<std::uint8_t> bytes;
151 if (!gps_directory.Get(kGpsTagAltitudeRef, &bytes) || bytes.empty() ||
152 !GetRational(kGpsTagAltitude, gps_directory, 1,
153 &preview_image_data->gps.altitude)) {
154 return;
155 }
156 preview_image_data->gps.altitude_ref = bytes[0] != 0;
157 }
158 preview_image_data->gps.is_valid = true;
159 }
160}
161
162Error FillPreviewImageData(const TiffDirectory& tiff_directory,
163 PreviewImageData* preview_image_data) {
164 bool success = true;
Yujie Qin3eaa8312016-01-15 16:22:20 +0100165 // Get preview_offset and preview_length
Jaesung Chung29345532016-01-14 02:28:47 +0900166 if (tiff_directory.Has(kTiffTagStripOffsets) &&
167 tiff_directory.Has(kTiffTagStripByteCounts)) {
168 std::vector<std::uint32_t> strip_offsets;
169 std::vector<std::uint32_t> strip_byte_counts;
170 if (!tiff_directory.Get(kTiffTagStripOffsets, &strip_offsets) ||
171 !tiff_directory.Get(kTiffTagStripByteCounts, &strip_byte_counts)) {
172 return kFail;
173 }
174 if (strip_offsets.size() == 1 && strip_byte_counts.size() == 1) {
175 preview_image_data->preview_offset = strip_offsets[0];
176 preview_image_data->preview_length = strip_byte_counts[0];
177 }
178 } else if (tiff_directory.Has(kTiffTagJpegOffset) &&
179 tiff_directory.Has(kTiffTagJpegByteCount)) {
180 success &= tiff_directory.Get(kTiffTagJpegOffset,
181 &preview_image_data->preview_offset);
182 success &= tiff_directory.Get(kTiffTagJpegByteCount,
183 &preview_image_data->preview_length);
184 } else if (tiff_directory.Has(kPanaTagJpegImage)) {
185 if (!tiff_directory.GetOffsetAndLength(
186 kPanaTagJpegImage, TIFF_TYPE_UNDEFINED,
187 &preview_image_data->preview_offset,
188 &preview_image_data->preview_length)) {
189 return kFail;
190 }
191 }
192
Yujie Qin3eaa8312016-01-15 16:22:20 +0100193 // Get exif_orientation if it was not set already.
194 if (tiff_directory.Has(kTiffTagOrientation) &&
195 preview_image_data->exif_orientation == 1) {
Jaesung Chung29345532016-01-14 02:28:47 +0900196 success &= tiff_directory.Get(kTiffTagOrientation,
197 &preview_image_data->exif_orientation);
198 }
199
200 // Get color_space
201 if (tiff_directory.Has(kExifTagColorSpace)) {
202 std::uint32_t color_space;
203 success &= tiff_directory.Get(kExifTagColorSpace, &color_space);
204 if (color_space == 1) {
205 preview_image_data->color_space = PreviewImageData::kSrgb;
Yujie Qin3eaa8312016-01-15 16:22:20 +0100206 } else if (color_space == 65535 || color_space == 2) {
Jaesung Chung29345532016-01-14 02:28:47 +0900207 preview_image_data->color_space = PreviewImageData::kAdobeRgb;
208 }
209 }
210
211 success &= GetFullDimension(tiff_directory, &preview_image_data->full_width,
212 &preview_image_data->full_height);
213
214 if (tiff_directory.Has(kTiffTagMake)) {
215 success &= tiff_directory.Get(kTiffTagMake, &preview_image_data->maker);
216 }
217
218 if (tiff_directory.Has(kTiffTagModel)) {
219 success &= tiff_directory.Get(kTiffTagModel, &preview_image_data->model);
220 }
221
222 if (tiff_directory.Has(kExifTagDateTimeOriginal)) {
223 success &= tiff_directory.Get(kExifTagDateTimeOriginal,
224 &preview_image_data->date_time);
225 }
226
227 if (tiff_directory.Has(kExifTagIsoSpeed)) {
228 success &= tiff_directory.Get(kExifTagIsoSpeed, &preview_image_data->iso);
229 } else if (tiff_directory.Has(kPanaTagIso)) {
230 success &= tiff_directory.Get(kPanaTagIso, &preview_image_data->iso);
231 }
232
233 if (tiff_directory.Has(kExifTagExposureTime)) {
234 success &= GetRational(kExifTagExposureTime, tiff_directory, 1,
235 &preview_image_data->exposure_time);
236 }
237
238 if (tiff_directory.Has(kExifTagFnumber)) {
239 success &= GetRational(kExifTagFnumber, tiff_directory, 1,
240 &preview_image_data->fnumber);
241 }
242
243 if (tiff_directory.Has(kExifTagFocalLength)) {
244 success &= GetRational(kExifTagFocalLength, tiff_directory, 1,
245 &preview_image_data->focal_length);
246 }
247
248 if (!success) {
249 return kFail;
250 }
251
252 return kOk;
253}
254
255const TiffDirectory* FindFirstTagInIfds(const Tags& tag,
256 const IfdVector& tiff_directory) {
257 for (std::uint32_t i = 0; i < tiff_directory.size(); ++i) {
258 if (tiff_directory[i].Has(tag)) {
259 return &tiff_directory[i];
260 }
261
262 // Recursively search sub directories.
263 const TiffDirectory* sub_directory =
264 FindFirstTagInIfds(tag, tiff_directory[i].GetSubDirectories());
265 if (sub_directory != NULL) {
266 return sub_directory;
267 }
268 }
269 return NULL;
270}
271
272// Gets the SubIfd content.
273void ParseSubIfds(const std::uint32_t tiff_offset, const TagSet& desired_tags,
274 const std::uint32_t max_number_ifds, const Endian endian,
275 StreamInterface* stream, TiffDirectory* tiff_ifd,
276 Error* error) {
277 if (*error == kOk && tiff_ifd->Has(kTiffTagSubIfd)) {
278 std::uint32_t offset = 0;
279 std::uint32_t length = 0;
280 tiff_ifd->GetOffsetAndLength(kTiffTagSubIfd, TIFF_TYPE_LONG, &offset,
281 &length);
282 length /= 4; // length in bytes divided by 4 gives number of IFDs.
283 for (std::uint32_t j = 0; j < length && j < max_number_ifds; ++j) {
284 std::uint32_t sub_offset;
285 if (!Get32u(stream, offset + 4 * j, endian, &sub_offset)) {
286 *error = kFail;
287 return;
288 }
289
290 std::uint32_t next_ifd_offset;
291 TiffDirectory sub_ifd(static_cast<Endian>(endian));
292 *error = ParseDirectory(tiff_offset, sub_offset, endian, desired_tags,
293 stream, &sub_ifd, &next_ifd_offset);
294 if (*error != kOk) {
295 return;
296 }
297
298 tiff_ifd->AddSubDirectory(sub_ifd);
299 }
300 }
301}
302
303} // namespace
304
305bool Get16u(StreamInterface* stream, const std::uint32_t offset,
306 const Endian& endian, std::uint16_t* value) {
307 std::uint8_t data[2];
308 if (stream->GetData(offset, 2, data) == kOk) {
309 if (endian == kBigEndian) {
310 *value = (data[0] * 0x100) | data[1];
311 } else {
312 *value = (data[1] * 0x100) | data[0];
313 }
314 return true;
315 } else {
316 return false;
317 }
318}
319
320bool Get32u(StreamInterface* stream, const std::uint32_t offset,
321 const Endian& endian, std::uint32_t* value) {
322 std::uint8_t data[4];
323 if (stream->GetData(offset, 4, data) == kOk) {
324 if (endian == kBigEndian) {
325 *value = (data[0] * 0x1000000) | (data[1] * 0x10000) | (data[2] * 0x100) |
326 data[3];
327 } else {
328 *value = (data[3] * 0x1000000) | (data[2] * 0x10000) | (data[1] * 0x100) |
329 data[0];
330 }
331 return true;
332 } else {
333 return false;
334 }
335}
336
337std::vector<std::uint8_t> GetData(const size_t offset, const size_t length,
338 StreamInterface* stream, Error* error) {
339 // Read in chunks with a maximum size of 1 MiB.
340 const size_t kChunkSize = 1048576;
341
342 std::vector<std::uint8_t> data;
343 size_t processed_data = 0;
344 while (*error == kOk && processed_data < length) {
345 size_t chunk_length = kChunkSize;
346 if (length - data.size() < kChunkSize) {
347 chunk_length = length - data.size();
348 }
349
350 data.resize(processed_data + chunk_length);
351 *error = stream->GetData(offset + processed_data, chunk_length,
352 &data[processed_data]);
353
354 processed_data += chunk_length;
355 }
356 return data;
357}
358
359bool GetEndianness(const std::uint32_t tiff_offset, StreamInterface* stream,
360 Endian* endian) {
361 const std::uint8_t kTiffBigEndianMagic[] = {'M', 'M'};
362 const std::uint8_t kTiffLittleEndianMagic[] = {'I', 'I'};
363 std::uint8_t tiff_endian[sizeof(kTiffBigEndianMagic)];
364 if (stream->GetData(tiff_offset, sizeof(tiff_endian), &tiff_endian[0]) !=
365 kOk) {
366 return false;
367 }
368
369 if (!memcmp(tiff_endian, kTiffLittleEndianMagic, sizeof(tiff_endian))) {
370 *endian = kLittleEndian;
371 return true;
372 } else if (!memcmp(tiff_endian, kTiffBigEndianMagic, sizeof(tiff_endian))) {
373 *endian = kBigEndian;
374 return true;
375 } else {
376 return false;
377 }
378}
379
380bool GetPreviewDimensions(const std::uint32_t jpeg_offset,
381 StreamInterface* stream, std::uint16_t* width,
382 std::uint16_t* height) {
383 const Endian endian = kBigEndian;
384 std::uint32_t offset = jpeg_offset;
385 std::uint16_t segment;
386
387 // Parse the JPEG header until we find Frame0 which contains the image width
388 // and height or the actual image data starts (StartOfScan)
389 do {
390 if (!Get16u(stream, offset, endian, &segment)) {
391 return false;
392 }
393 offset += 2;
394
395 switch (segment) {
396 case kStartOfImage:
397 break;
398 case kStartOfFrame:
399 return Get16u(stream, offset + 3, endian, height) &&
400 Get16u(stream, offset + 5, endian, width);
401 default: {
402 std::uint16_t length;
403 if (!Get16u(stream, offset, endian, &length)) {
404 return false;
405 }
406 offset += length;
407 }
408 }
409 } while (segment != kStartOfScan);
410
411 // No width and hight information found.
412 return false;
413}
414
415Error ParseDirectory(const std::uint32_t tiff_offset,
416 const std::uint32_t ifd_offset, const Endian endian,
417 const TagSet& desired_tags, StreamInterface* stream,
418 TiffDirectory* tiff_directory,
419 std::uint32_t* next_ifd_offset) {
420 std::uint16_t number_of_entries;
421 if (!Get16u(stream, ifd_offset, endian, &number_of_entries)) {
422 return kFail;
423 }
424
425 for (std::uint32_t i = 0;
426 i < static_cast<std::uint32_t>(number_of_entries) * 12; i += 12) {
427 std::uint16_t tag;
428 std::uint16_t type;
429 std::uint32_t number_of_elements;
430 if (Get16u(stream, ifd_offset + 2 + i, endian, &tag) &&
431 Get16u(stream, ifd_offset + 4 + i, endian, &type) &&
432 Get32u(stream, ifd_offset + 6 + i, endian, &number_of_elements)) {
433 // Check if the current tag should be handled.
434 if (desired_tags.count(static_cast<Tags>(tag)) != 1) {
435 continue;
436 }
437 } else {
438 return kFail;
439 }
440
441 const size_t type_size = SizeOfType(type, nullptr /* no error */);
442
443 // Check that type_size * number_of_elements does not exceed UINT32_MAX.
444 if (type_size != 0 && number_of_elements > UINT32_MAX / type_size) {
445 return kFail;
446 }
447 const size_t byte_count =
448 type_size * static_cast<size_t>(number_of_elements);
449
450 std::uint32_t value_offset;
451 if (byte_count > 4 &&
452 Get32u(stream, ifd_offset + 10 + i, endian, &value_offset)) {
453 value_offset += tiff_offset;
454 } else if (byte_count != 0) {
455 value_offset = ifd_offset + 10 + i;
456 } else {
457 // Ignore entries with an invalid byte count.
458 continue;
459 }
460
461 Error error = kOk;
462 const std::vector<std::uint8_t> data =
463 GetData(value_offset, byte_count, stream, &error);
464 if (error != kOk) {
465 return error;
466 }
467 tiff_directory->AddEntry(tag, type, number_of_elements, value_offset, data);
468 }
469
470 if (Get32u(stream, ifd_offset + 2 + number_of_entries * 12, endian,
471 next_ifd_offset)) {
472 return kOk;
473 } else {
474 return kFail;
475 }
476}
477
478TiffParser::TiffParser(StreamInterface* stream) : stream_(stream) {}
479
480TiffParser::TiffParser(StreamInterface* stream, const std::uint32_t offset)
481 : stream_(stream), tiff_offset_(offset) {}
482
483Error TiffParser::GetPreviewImageData(const TiffContent& tiff_content,
484 PreviewImageData* preview_image_data) {
485 Error error = kOk;
486 for (const auto& tiff_directory : tiff_content.tiff_directory) {
487 error = FillPreviewImageData(tiff_directory, preview_image_data);
488 if (error == kOk && tiff_directory.Has(kTiffTagExifIfd) &&
489 tiff_content.exif_directory) {
490 error = FillPreviewImageData(*tiff_content.exif_directory,
491 preview_image_data);
492 }
493 if (error == kOk && tiff_directory.Has(kExifTagGps) &&
494 tiff_content.gps_directory) {
495 FillGpsPreviewImageData(*tiff_content.gps_directory, preview_image_data);
496 }
497 for (const auto& sub_directory : tiff_directory.GetSubDirectories()) {
498 if (error == kOk) {
499 error = FillPreviewImageData(sub_directory, preview_image_data);
500 }
501 }
502 }
503 return error;
504}
505
506Error TiffParser::Parse(const TagSet& desired_tags,
507 const std::uint16_t max_number_ifds,
508 TiffContent* tiff_content) {
509 if (!tiff_content->tiff_directory.empty()) {
510 return kFail; // You shall call Parse() only once.
511 }
512
513 const std::uint32_t kTiffIdentifierSize = 4;
514 std::uint32_t offset_to_ifd = 0;
515 if (!GetEndianness(tiff_offset_, stream_, &endian_) ||
516 !Get32u(stream_, tiff_offset_ + kTiffIdentifierSize, endian_,
517 &offset_to_ifd)) {
518 return kFail;
519 }
520
521 Error error = ParseIfd(tiff_offset_ + offset_to_ifd, desired_tags,
522 max_number_ifds, &tiff_content->tiff_directory);
523 if (error != kOk) {
524 return error;
525 }
526
527 // Get the Exif data.
528 const TiffDirectory* tiff_ifd =
529 FindFirstTagInIfds(kTiffTagExifIfd, tiff_content->tiff_directory);
530 if (tiff_ifd != NULL) {
531 std::uint32_t offset;
532 if (tiff_ifd->Get(kTiffTagExifIfd, &offset)) {
533 tiff_content->exif_directory.reset(new TiffDirectory(endian_));
534 std::uint32_t next_ifd_offset;
535 error = ParseDirectory(
536 tiff_offset_, tiff_offset_ + offset, endian_, desired_tags, stream_,
537 tiff_content->exif_directory.get(), &next_ifd_offset);
538 if (error != kOk) {
539 return error;
540 }
541
542 if (tiff_ifd->Get(kExifTagGps, &offset)) {
543 tiff_content->gps_directory.reset(new TiffDirectory(endian_));
544 const TagSet gps_tags = {kGpsTagLatitudeRef, kGpsTagLatitude,
545 kGpsTagLongitudeRef, kGpsTagLongitude,
546 kGpsTagAltitudeRef, kGpsTagAltitude,
547 kGpsTagTimeStamp, kGpsTagDateStamp};
548 return ParseDirectory(
549 tiff_offset_, tiff_offset_ + offset, endian_, gps_tags, stream_,
550 tiff_content->gps_directory.get(), &next_ifd_offset);
551 }
552 }
553 }
554
555 return error;
556}
557
558Error TiffParser::ParseIfd(const std::uint32_t offset_to_ifd,
559 const TagSet& desired_tags,
560 const std::uint16_t max_number_ifds,
561 IfdVector* tiff_directory) {
562 std::uint32_t next_ifd_offset;
563 TiffDirectory tiff_ifd(static_cast<Endian>(endian_));
564 Error error =
565 ParseDirectory(tiff_offset_, offset_to_ifd, endian_, desired_tags,
566 stream_, &tiff_ifd, &next_ifd_offset);
567
568 ParseSubIfds(tiff_offset_, desired_tags, max_number_ifds, endian_, stream_,
569 &tiff_ifd, &error);
570 if (error == kOk) {
571 tiff_directory->push_back(tiff_ifd);
572 if (next_ifd_offset != 0 && tiff_directory->size() < max_number_ifds) {
573 error = ParseIfd(tiff_offset_ + next_ifd_offset, desired_tags,
574 max_number_ifds, tiff_directory);
575 }
576 }
577
578 return error;
579}
580
581} // namespace piex