1 // 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/piex.h"
18
19 #include <cstdint>
20 #include <limits>
21 #include <set>
22 #include <vector>
23
24 #include "src/binary_parse/range_checked_byte_ptr.h"
25 #include "src/image_type_recognition/image_type_recognition_lite.h"
26 #include "src/piex_cr3.h"
27 #include "src/tiff_parser.h"
28
29 namespace piex {
30 namespace {
31
32 using binary_parse::RangeCheckedBytePtr;
33 using image_type_recognition::RawImageTypes;
34 using image_type_recognition::RecognizeRawImageTypeLite;
35 using tiff_directory::Endian;
36 using tiff_directory::TiffDirectory;
37
38 const std::uint32_t kRafOffsetToPreviewOffset = 84;
39
GetDngInformation(const tiff_directory::TiffDirectory & tiff_directory,std::uint32_t * width,std::uint32_t * height,std::vector<std::uint32_t> * cfa_pattern_dim)40 bool GetDngInformation(const tiff_directory::TiffDirectory& tiff_directory,
41 std::uint32_t* width, std::uint32_t* height,
42 std::vector<std::uint32_t>* cfa_pattern_dim) {
43 if (!GetFullDimension32(tiff_directory, width, height) || *width == 0 ||
44 *height == 0) {
45 return false;
46 }
47
48 if (!tiff_directory.Get(kTiffTagCfaPatternDim, cfa_pattern_dim) ||
49 cfa_pattern_dim->size() != 2) {
50 return false;
51 }
52 return true;
53 }
54
GetDngInformation(const TagSet & extended_tags,StreamInterface * data,std::uint32_t * width,std::uint32_t * height,std::vector<std::uint32_t> * cfa_pattern_dim)55 bool GetDngInformation(const TagSet& extended_tags, StreamInterface* data,
56 std::uint32_t* width, std::uint32_t* height,
57 std::vector<std::uint32_t>* cfa_pattern_dim) {
58 TagSet desired_tags = {kExifTagDefaultCropSize, kTiffTagCfaPatternDim,
59 kTiffTagExifIfd, kTiffTagSubFileType};
60 desired_tags.insert(extended_tags.cbegin(), extended_tags.cend());
61
62 TiffParser tiff_parser(data, 0 /* offset */);
63
64 TiffContent tiff_content;
65 if (!tiff_parser.Parse(desired_tags, 1, &tiff_content) ||
66 tiff_content.tiff_directory.empty()) {
67 return false;
68 }
69
70 // If IFD0 contains already the full dimensions we do not parse into the sub
71 // IFD.
72 const TiffDirectory& tiff_directory = tiff_content.tiff_directory[0];
73 if (tiff_directory.GetSubDirectories().empty()) {
74 return GetDngInformation(tiff_directory, width, height, cfa_pattern_dim);
75 } else {
76 return GetDngInformation(tiff_directory.GetSubDirectories()[0], width,
77 height, cfa_pattern_dim);
78 }
79 }
80
GetPreviewData(const TagSet & extended_tags,const std::uint32_t tiff_offset,const std::uint32_t number_of_ifds,StreamInterface * stream,TiffContent * tiff_content,PreviewImageData * preview_image_data)81 bool GetPreviewData(const TagSet& extended_tags,
82 const std::uint32_t tiff_offset,
83 const std::uint32_t number_of_ifds, StreamInterface* stream,
84 TiffContent* tiff_content,
85 PreviewImageData* preview_image_data) {
86 TagSet desired_tags = {
87 kExifTagColorSpace, kExifTagDateTimeOriginal, kExifTagExposureTime,
88 kExifTagFnumber, kExifTagFocalLength, kExifTagGps,
89 kExifTagIsoSpeed, kTiffTagCompression, kTiffTagDateTime,
90 kTiffTagExifIfd, kTiffTagCfaPatternDim, kTiffTagMake,
91 kTiffTagModel, kTiffTagOrientation, kTiffTagPhotometric};
92 desired_tags.insert(extended_tags.cbegin(), extended_tags.cend());
93
94 TiffParser tiff_parser(stream, tiff_offset);
95
96 if (!tiff_parser.Parse(desired_tags, number_of_ifds, tiff_content)) {
97 return false;
98 }
99 if (tiff_content->tiff_directory.empty()) {
100 // Returns false if the stream does not contain any TIFF structure.
101 return false;
102 }
103 return tiff_parser.GetPreviewImageData(*tiff_content, preview_image_data);
104 }
105
GetPreviewData(const TagSet & extended_tags,const std::uint32_t number_of_ifds,StreamInterface * stream,PreviewImageData * preview_image_data)106 bool GetPreviewData(const TagSet& extended_tags,
107 const std::uint32_t number_of_ifds, StreamInterface* stream,
108 PreviewImageData* preview_image_data) {
109 const std::uint32_t kTiffOffset = 0;
110 TiffContent tiff_content;
111 return GetPreviewData(extended_tags, kTiffOffset, number_of_ifds, stream,
112 &tiff_content, preview_image_data);
113 }
114
GetExifData(const std::uint32_t exif_offset,StreamInterface * stream,PreviewImageData * preview_image_data)115 bool GetExifData(const std::uint32_t exif_offset, StreamInterface* stream,
116 PreviewImageData* preview_image_data) {
117 const TagSet kExtendedTags = {kTiffTagJpegByteCount, kTiffTagJpegOffset};
118 const std::uint32_t kNumberOfIfds = 2;
119 TiffContent tiff_content;
120 return GetPreviewData(kExtendedTags, exif_offset, kNumberOfIfds, stream,
121 &tiff_content, preview_image_data);
122 }
123
124 // Reads the jpeg compressed thumbnail information.
GetThumbnailOffsetAndLength(const TagSet & extended_tags,StreamInterface * stream,PreviewImageData * preview_image_data)125 void GetThumbnailOffsetAndLength(const TagSet& extended_tags,
126 StreamInterface* stream,
127 PreviewImageData* preview_image_data) {
128 TagSet desired_tags = {kTiffTagJpegByteCount, kTiffTagJpegOffset};
129 desired_tags.insert(extended_tags.cbegin(), extended_tags.cend());
130
131 const std::uint32_t kNumberOfIfds = 2;
132 PreviewImageData thumbnail_data;
133 if (GetPreviewData(desired_tags, kNumberOfIfds, stream, &thumbnail_data)) {
134 preview_image_data->thumbnail = thumbnail_data.thumbnail;
135 }
136 }
137
GetExifIfd(const Endian endian,StreamInterface * stream,TiffDirectory * exif_ifd)138 bool GetExifIfd(const Endian endian, StreamInterface* stream,
139 TiffDirectory* exif_ifd) {
140 const std::uint32_t kTiffOffset = 0;
141 std::uint32_t offset_to_ifd;
142 if (!Get32u(stream, sizeof(offset_to_ifd), endian, &offset_to_ifd)) {
143 return false;
144 }
145
146 std::uint32_t next_ifd_offset;
147 TiffDirectory tiff_ifd(endian);
148 if (!ParseDirectory(kTiffOffset, offset_to_ifd, endian, {kTiffTagExifIfd},
149 stream, &tiff_ifd, &next_ifd_offset)) {
150 return false;
151 }
152
153 std::uint32_t exif_offset;
154 if (tiff_ifd.Get(kTiffTagExifIfd, &exif_offset)) {
155 return ParseDirectory(kTiffOffset, exif_offset, endian,
156 {kExifTagMakernotes}, stream, exif_ifd,
157 &next_ifd_offset);
158 }
159
160 return true;
161 }
162
GetMakernoteIfd(const TiffDirectory & exif_ifd,const Endian endian,const std::uint32_t skip_offset,StreamInterface * stream,std::uint32_t * makernote_offset,TiffDirectory * makernote_ifd)163 bool GetMakernoteIfd(const TiffDirectory& exif_ifd, const Endian endian,
164 const std::uint32_t skip_offset, StreamInterface* stream,
165 std::uint32_t* makernote_offset,
166 TiffDirectory* makernote_ifd) {
167 std::uint32_t makernote_length;
168 if (!exif_ifd.GetOffsetAndLength(kExifTagMakernotes,
169 tiff_directory::TIFF_TYPE_UNDEFINED,
170 makernote_offset, &makernote_length)) {
171 return false;
172 }
173
174 std::uint32_t next_ifd_offset;
175 return ParseDirectory(*makernote_offset, *makernote_offset + skip_offset,
176 endian, {kTiffTagImageWidth, kOlymTagCameraSettings,
177 kOlymTagRawProcessing, kPentaxTagColorSpace},
178 stream, makernote_ifd, &next_ifd_offset);
179 }
180
GetCameraSettingsIfd(const TiffDirectory & makernote_ifd,const std::uint32_t makernote_offset,const Endian endian,StreamInterface * stream,TiffDirectory * camera_settings_ifd)181 bool GetCameraSettingsIfd(const TiffDirectory& makernote_ifd,
182 const std::uint32_t makernote_offset,
183 const Endian endian, StreamInterface* stream,
184 TiffDirectory* camera_settings_ifd) {
185 std::uint32_t camera_settings_offset;
186 std::uint32_t camera_settings_length;
187 if (!makernote_ifd.GetOffsetAndLength(
188 kOlymTagCameraSettings, tiff_directory::TIFF_IFD,
189 &camera_settings_offset, &camera_settings_length)) {
190 return false;
191 }
192
193 std::uint32_t next_ifd_offset;
194 if (!Get32u(stream, camera_settings_offset, endian,
195 &camera_settings_offset)) {
196 return false;
197 }
198 return ParseDirectory(makernote_offset,
199 makernote_offset + camera_settings_offset, endian,
200 {kTiffTagBitsPerSample, kTiffTagImageLength}, stream,
201 camera_settings_ifd, &next_ifd_offset);
202 }
203
GetRawProcessingIfd(const TagSet & desired_tags,const TiffDirectory & makernote_ifd,const std::uint32_t makernote_offset,const Endian endian,StreamInterface * stream,TiffDirectory * raw_processing_ifd)204 bool GetRawProcessingIfd(const TagSet& desired_tags,
205 const TiffDirectory& makernote_ifd,
206 const std::uint32_t makernote_offset,
207 const Endian endian, StreamInterface* stream,
208 TiffDirectory* raw_processing_ifd) {
209 std::uint32_t raw_processing_offset;
210 std::uint32_t raw_processing_length;
211 if (!makernote_ifd.GetOffsetAndLength(
212 kOlymTagRawProcessing, tiff_directory::TIFF_IFD,
213 &raw_processing_offset, &raw_processing_length)) {
214 return false;
215 }
216
217 std::uint32_t next_ifd_offset;
218 if (!Get32u(stream, raw_processing_offset, endian, &raw_processing_offset)) {
219 return false;
220 }
221
222 return ParseDirectory(
223 makernote_offset, makernote_offset + raw_processing_offset, endian,
224 desired_tags, stream, raw_processing_ifd, &next_ifd_offset);
225 }
226
227 // Retrieves the preview image offset and length from the camera settings and
228 // the 'full_width' and 'full_height' from the raw processing ifd in 'stream'.
229 // Returns false if anything is wrong.
GetOlympusPreviewImage(StreamInterface * stream,PreviewImageData * preview_image_data)230 bool GetOlympusPreviewImage(StreamInterface* stream,
231 PreviewImageData* preview_image_data) {
232 Endian endian;
233 if (!GetEndianness(0 /* tiff offset */, stream, &endian)) {
234 return false;
235 }
236
237 TiffDirectory exif_ifd(endian);
238 if (!GetExifIfd(endian, stream, &exif_ifd)) {
239 return false;
240 }
241
242 std::uint32_t makernote_offset;
243 TiffDirectory makernote_ifd(endian);
244 const std::uint32_t kSkipMakernoteStart = 12;
245 if (!GetMakernoteIfd(exif_ifd, endian, kSkipMakernoteStart, stream,
246 &makernote_offset, &makernote_ifd)) {
247 return false;
248 }
249
250 const std::uint32_t kThumbnailTag = 0x0100;
251 if (makernote_ifd.Has(kThumbnailTag)) {
252 if (!makernote_ifd.GetOffsetAndLength(
253 kThumbnailTag, tiff_directory::TIFF_TYPE_UNDEFINED,
254 &preview_image_data->thumbnail.offset,
255 &preview_image_data->thumbnail.length)) {
256 return false;
257 }
258 }
259
260 TiffDirectory camera_settings_ifd(endian);
261 if (!GetCameraSettingsIfd(makernote_ifd, makernote_offset, endian, stream,
262 &camera_settings_ifd)) {
263 return false;
264 }
265
266 const std::uint32_t kPreviewOffset = 0x0101;
267 const std::uint32_t kPreviewLength = 0x0102;
268 if (!camera_settings_ifd.Has(kPreviewOffset) ||
269 !camera_settings_ifd.Has(kPreviewLength)) {
270 return false;
271 }
272
273 camera_settings_ifd.Get(kPreviewOffset, &preview_image_data->preview.offset);
274 preview_image_data->preview.offset += makernote_offset;
275 camera_settings_ifd.Get(kPreviewLength, &preview_image_data->preview.length);
276
277 // Get the crop size from the raw processing ifd.
278 TiffDirectory raw_processing_ifd(endian);
279 if (!GetRawProcessingIfd({kOlymTagAspectFrame}, makernote_ifd,
280 makernote_offset, endian, stream,
281 &raw_processing_ifd)) {
282 return false;
283 }
284
285 if (raw_processing_ifd.Has(kOlymTagAspectFrame)) {
286 std::vector<std::uint32_t> aspect_frame;
287 if (raw_processing_ifd.Get(kOlymTagAspectFrame, &aspect_frame) &&
288 aspect_frame.size() == 4 && aspect_frame[2] > aspect_frame[0] &&
289 aspect_frame[3] > aspect_frame[1]) {
290 preview_image_data->full_width = aspect_frame[2] - aspect_frame[0] + 1;
291 preview_image_data->full_height = aspect_frame[3] - aspect_frame[1] + 1;
292 if (preview_image_data->full_width < preview_image_data->full_height) {
293 std::swap(preview_image_data->full_width,
294 preview_image_data->full_height);
295 }
296 }
297 }
298
299 return true;
300 }
301
PefGetColorSpace(StreamInterface * stream,PreviewImageData * preview_image_data)302 bool PefGetColorSpace(StreamInterface* stream,
303 PreviewImageData* preview_image_data) {
304 Endian endian;
305 if (!GetEndianness(0 /* tiff offset */, stream, &endian)) {
306 return false;
307 }
308
309 TiffDirectory exif_ifd(endian);
310 if (!GetExifIfd(endian, stream, &exif_ifd)) {
311 return false;
312 }
313
314 std::uint32_t makernote_offset;
315 TiffDirectory makernote_ifd(endian);
316 const std::uint32_t kSkipMakernoteStart = 6;
317 if (!GetMakernoteIfd(exif_ifd, endian, kSkipMakernoteStart, stream,
318 &makernote_offset, &makernote_ifd)) {
319 return false;
320 }
321 if (makernote_ifd.Has(kPentaxTagColorSpace)) {
322 std::uint32_t color_space;
323 if (!makernote_ifd.Get(kPentaxTagColorSpace, &color_space)) {
324 return false;
325 }
326 preview_image_data->color_space = color_space == 0
327 ? PreviewImageData::kSrgb
328 : PreviewImageData::kAdobeRgb;
329 }
330 return true;
331 }
332
RafGetOrientation(StreamInterface * stream,std::uint32_t * orientation)333 bool RafGetOrientation(StreamInterface* stream, std::uint32_t* orientation) {
334 // Parse the Fuji RAW header to get the offset and length of the preview
335 // image, which contains the Exif information.
336 const Endian endian = tiff_directory::kBigEndian;
337 std::uint32_t preview_offset = 0;
338 if (!Get32u(stream, kRafOffsetToPreviewOffset, endian, &preview_offset)) {
339 return false;
340 }
341
342 const std::uint32_t exif_offset = preview_offset + 12;
343 return GetExifOrientation(stream, exif_offset, orientation);
344 }
345
346 // Parses the Fuji Cfa header for the image width and height.
RafGetDimension(StreamInterface * stream,std::uint32_t * width,std::uint32_t * height)347 bool RafGetDimension(StreamInterface* stream, std::uint32_t* width,
348 std::uint32_t* height) {
349 const Endian endian = tiff_directory::kBigEndian;
350 std::uint32_t cfa_header_index = 0; // actual position in the cfa header.
351 std::uint32_t cfa_header_entries = 0;
352 if (!Get32u(stream, 92 /* cfa header offset */, endian, &cfa_header_index) ||
353 !Get32u(stream, cfa_header_index, endian, &cfa_header_entries)) {
354 return false;
355 }
356
357 // Add 4 to point to the actual read position in the cfa header.
358 cfa_header_index += 4;
359
360 for (std::uint32_t i = 0; i < cfa_header_entries; ++i) {
361 std::uint16_t id = 0;
362 std::uint16_t length = 0;
363 if (!Get16u(stream, cfa_header_index, endian, &id) ||
364 !Get16u(stream, cfa_header_index + 2, endian, &length)) {
365 return false;
366 }
367
368 std::uint16_t tmp_width = 0;
369 std::uint16_t tmp_height = 0;
370 if (id == 0x0111 /* tags the crop dimensions */ &&
371 Get16u(stream, cfa_header_index + 4, endian, &tmp_height) &&
372 Get16u(stream, cfa_header_index + 6, endian, &tmp_width)) {
373 *width = tmp_width;
374 *height = tmp_height;
375 return true;
376 }
377 cfa_header_index += 4u + length;
378 }
379 return false;
380 }
381
ArwGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)382 Error ArwGetPreviewData(StreamInterface* stream,
383 PreviewImageData* preview_image_data) {
384 const TagSet extended_tags = {kExifTagHeight, kExifTagWidth,
385 kTiffTagJpegByteCount, kTiffTagJpegOffset,
386 kTiffTagSubIfd};
387
388 GetThumbnailOffsetAndLength(TagSet(), stream, preview_image_data);
389
390 const std::uint32_t kNumberOfIfds = 1;
391 if (GetPreviewData(extended_tags, kNumberOfIfds, stream,
392 preview_image_data)) {
393 return kOk;
394 }
395 return kFail;
396 }
397
Cr2GetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)398 Error Cr2GetPreviewData(StreamInterface* stream,
399 PreviewImageData* preview_image_data) {
400 const TagSet extended_tags = {kExifTagHeight, kExifTagWidth,
401 kTiffTagStripByteCounts, kTiffTagStripOffsets};
402
403 GetThumbnailOffsetAndLength(TagSet(), stream, preview_image_data);
404
405 const std::uint32_t kNumberOfIfds = 1;
406 if (GetPreviewData(extended_tags, kNumberOfIfds, stream,
407 preview_image_data)) {
408 return kOk;
409 }
410 return kFail;
411 }
412
DngGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)413 Error DngGetPreviewData(StreamInterface* stream,
414 PreviewImageData* preview_image_data) {
415 // Some thumbnails from DngCreator are larger than the specified 256 pixel.
416 const int kDngThumbnailMaxDimension = 512;
417
418 const TagSet extended_tags = {
419 kExifTagDefaultCropSize, kTiffTagImageWidth, kTiffTagImageLength,
420 kTiffTagStripByteCounts, kTiffTagStripOffsets, kTiffTagSubIfd};
421
422 TiffContent tiff_content;
423 const std::uint32_t kNumberOfIfds = 3;
424 if (!GetPreviewData(extended_tags, 0, kNumberOfIfds, stream, &tiff_content,
425 preview_image_data)) {
426 return kFail;
427 }
428
429 const TiffDirectory& tiff_directory = tiff_content.tiff_directory[0];
430
431 if (!GetFullCropDimension(tiff_directory, &preview_image_data->full_width,
432 &preview_image_data->full_height)) {
433 return kFail;
434 }
435
436 // Find the jpeg compressed thumbnail and preview image.
437 Image preview;
438 Image thumbnail;
439
440 // Search for images in IFD0
441 Image temp_image;
442 if (GetImageData(tiff_directory, stream, &temp_image)) {
443 if (IsThumbnail(temp_image, kDngThumbnailMaxDimension)) {
444 thumbnail = temp_image;
445 } else if (temp_image.format == Image::kJpegCompressed) {
446 preview = temp_image;
447 }
448 }
449
450 // Search for images in other IFDs
451 for (const auto& ifd : tiff_directory.GetSubDirectories()) {
452 if (GetImageData(ifd, stream, &temp_image)) {
453 // Try to find the largest thumbnail/preview.
454 if (IsThumbnail(temp_image, kDngThumbnailMaxDimension)) {
455 if (temp_image > thumbnail) {
456 thumbnail = temp_image;
457 }
458 } else {
459 if (temp_image > preview &&
460 temp_image.format == Image::kJpegCompressed) {
461 preview = temp_image;
462 }
463 }
464 }
465 }
466 preview_image_data->preview = preview;
467 preview_image_data->thumbnail = thumbnail;
468
469 return kOk;
470 }
471
NefGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)472 Error NefGetPreviewData(StreamInterface* stream,
473 PreviewImageData* preview_image_data) {
474 const TagSet extended_tags = {kTiffTagImageWidth, kTiffTagImageLength,
475 kTiffTagJpegByteCount, kTiffTagJpegOffset,
476 kTiffTagStripByteCounts, kTiffTagStripOffsets,
477 kTiffTagSubIfd};
478 const std::uint32_t kNumberOfIfds = 2;
479 if (!GetPreviewData(extended_tags, kNumberOfIfds, stream,
480 preview_image_data)) {
481 return kFail;
482 }
483
484 if (preview_image_data->thumbnail.length == 0) {
485 PreviewImageData thumbnail_data;
486 GetThumbnailOffsetAndLength(TagSet(), stream, &thumbnail_data);
487 preview_image_data->thumbnail = thumbnail_data.thumbnail;
488 }
489
490 // The Nikon RAW data provides the dimensions of the sensor image, which are
491 // slightly larger than the dimensions of the preview image. In order to
492 // determine the correct full width and height of the image, the preview image
493 // size needs to be taken into account. Based on experiments the preview image
494 // dimensions must be at least 90% of the sensor image dimensions to let it be
495 // a full size preview image.
496 if (preview_image_data->preview.length > 0) { // when preview image exists
497 const float kEpsilon = 0.9f;
498
499 std::uint16_t width;
500 std::uint16_t height;
501 if (!GetJpegDimensions(preview_image_data->preview.offset, stream, &width,
502 &height) ||
503 preview_image_data->full_width == 0 ||
504 preview_image_data->full_height == 0) {
505 return kUnsupported;
506 }
507
508 if (static_cast<float>(width) /
509 static_cast<float>(preview_image_data->full_width) >
510 kEpsilon ||
511 static_cast<float>(height) /
512 static_cast<float>(preview_image_data->full_height) >
513 kEpsilon) {
514 preview_image_data->full_width = width;
515 preview_image_data->full_height = height;
516 }
517 }
518 return kOk;
519 }
520
OrfGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)521 Error OrfGetPreviewData(StreamInterface* stream,
522 PreviewImageData* preview_image_data) {
523 if (!GetExifData(0, stream, preview_image_data)) {
524 return kFail;
525 }
526 // Omit errors, because some images do not contain any preview data.
527 GetOlympusPreviewImage(stream, preview_image_data);
528 return kOk;
529 }
530
PefGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)531 Error PefGetPreviewData(StreamInterface* stream,
532 PreviewImageData* preview_image_data) {
533 const TagSet extended_tags = {kTiffTagImageWidth, kTiffTagImageLength,
534 kTiffTagJpegByteCount, kTiffTagJpegOffset,
535 kTiffTagSubIfd};
536 const std::uint32_t kNumberOfIfds = 3;
537 if (!GetPreviewData(extended_tags, kNumberOfIfds, stream,
538 preview_image_data) ||
539 !PefGetColorSpace(stream, preview_image_data)) {
540 return kFail;
541 }
542
543 PreviewImageData thumbnail_data;
544 GetThumbnailOffsetAndLength(TagSet(), stream, &thumbnail_data);
545 preview_image_data->thumbnail = thumbnail_data.thumbnail;
546
547 return kOk;
548 }
549
RafGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)550 Error RafGetPreviewData(StreamInterface* stream,
551 PreviewImageData* preview_image_data) {
552 // Parse the Fuji RAW header to get the offset and length of the preview
553 // image, which contains the Exif information.
554 const Endian endian = tiff_directory::kBigEndian;
555 std::uint32_t preview_offset = 0;
556 std::uint32_t preview_length = 0;
557 if (!Get32u(stream, kRafOffsetToPreviewOffset, endian, &preview_offset) ||
558 !Get32u(stream, kRafOffsetToPreviewOffset + 4, endian, &preview_length)) {
559 return kFail;
560 }
561
562 if (!RafGetDimension(stream, &preview_image_data->full_width,
563 &preview_image_data->full_height)) {
564 return kFail;
565 }
566
567 if (preview_length > 0) { // when preview image exists
568 // Parse the Exif information from the preview image.
569 const std::uint32_t exif_offset = preview_offset + 12;
570 if (!GetExifData(exif_offset, stream, preview_image_data)) {
571 return kFail;
572 }
573 }
574
575 // Merge the Exif data with the RAW data to form the preview_image_data.
576 preview_image_data->thumbnail.offset += 160; // Skip the cfa header.
577 preview_image_data->preview.offset = preview_offset;
578 preview_image_data->preview.length = preview_length;
579 return kOk;
580 }
581
Rw2GetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)582 Error Rw2GetPreviewData(StreamInterface* stream,
583 PreviewImageData* preview_image_data) {
584 const TagSet extended_tags = {kPanaTagTopBorder, kPanaTagLeftBorder,
585 kPanaTagBottomBorder, kPanaTagRightBorder,
586 kPanaTagIso, kPanaTagJpegImage,
587 kTiffTagJpegByteCount, kTiffTagJpegOffset};
588 // Parse the RAW data to get the ISO, offset and length of the preview image,
589 // which contains the Exif information.
590 const std::uint32_t kNumberOfIfds = 1;
591 PreviewImageData preview_data;
592 if (!GetPreviewData(extended_tags, kNumberOfIfds, stream, &preview_data)) {
593 return kFail;
594 }
595
596 if (preview_data.preview.length > 0) { // when preview image exists
597 // Parse the Exif information from the preview image.
598 const std::uint32_t exif_offset = preview_data.preview.offset + 12;
599 if (!GetExifData(exif_offset, stream, preview_image_data)) {
600 return kFail;
601 }
602 preview_image_data->thumbnail.offset += exif_offset;
603 }
604
605 // Merge the Exif data with the RAW data to form the preview_image_data.
606 preview_image_data->preview = preview_data.preview;
607 preview_image_data->iso = preview_data.iso;
608 preview_image_data->full_width = preview_data.full_width;
609 preview_image_data->full_height = preview_data.full_height;
610
611 return kOk;
612 }
613
SrwGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)614 Error SrwGetPreviewData(StreamInterface* stream,
615 PreviewImageData* preview_image_data) {
616 GetThumbnailOffsetAndLength({kTiffTagSubIfd}, stream, preview_image_data);
617
618 const TagSet extended_tags = {kExifTagWidth, kExifTagHeight,
619 kTiffTagJpegByteCount, kTiffTagJpegOffset,
620 kTiffTagSubIfd};
621 const std::uint32_t kNumberOfIfds = 1;
622 if (!GetPreviewData(extended_tags, kNumberOfIfds, stream,
623 preview_image_data)) {
624 return kFail;
625 }
626 return kOk;
627 }
628
629 } // namespace
630
BytesRequiredForIsRaw()631 size_t BytesRequiredForIsRaw() {
632 return image_type_recognition::GetNumberOfBytesForIsRawLite();
633 }
634
IsRaw(StreamInterface * data)635 bool IsRaw(StreamInterface* data) {
636 const size_t bytes = BytesRequiredForIsRaw();
637 if (data == nullptr) {
638 return false;
639 }
640
641 // Read required number of bytes into a vector.
642 std::vector<std::uint8_t> file_header(bytes);
643 if (data->GetData(0, file_header.size(), file_header.data()) != kOk) {
644 return false;
645 }
646
647 RangeCheckedBytePtr data_buffer(file_header.data(), file_header.size());
648
649 return image_type_recognition::IsRawLite(data_buffer);
650 }
651
GetPreviewImageData(StreamInterface * data,PreviewImageData * preview_image_data,RawImageTypes * output_type)652 Error GetPreviewImageData(StreamInterface* data,
653 PreviewImageData* preview_image_data,
654 RawImageTypes* output_type) {
655 const size_t bytes = BytesRequiredForIsRaw();
656 if (data == nullptr || bytes == 0) {
657 return kFail;
658 }
659
660 std::vector<std::uint8_t> file_header(bytes);
661 Error error = data->GetData(0, file_header.size(), file_header.data());
662 if (error != kOk) {
663 return error;
664 }
665 RangeCheckedBytePtr header_buffer(file_header.data(), file_header.size());
666
667 RawImageTypes type = RecognizeRawImageTypeLite(header_buffer);
668 if (output_type != nullptr) *output_type = type;
669 switch (type) {
670 case image_type_recognition::kArwImage:
671 return ArwGetPreviewData(data, preview_image_data);
672 case image_type_recognition::kCr2Image:
673 return Cr2GetPreviewData(data, preview_image_data);
674 case image_type_recognition::kCr3Image:
675 return Cr3GetPreviewData(data, preview_image_data);
676 case image_type_recognition::kDngImage:
677 return DngGetPreviewData(data, preview_image_data);
678 case image_type_recognition::kNefImage:
679 case image_type_recognition::kNrwImage:
680 return NefGetPreviewData(data, preview_image_data);
681 case image_type_recognition::kOrfImage:
682 return OrfGetPreviewData(data, preview_image_data);
683 case image_type_recognition::kPefImage:
684 return PefGetPreviewData(data, preview_image_data);
685 case image_type_recognition::kRafImage:
686 return RafGetPreviewData(data, preview_image_data);
687 case image_type_recognition::kRw2Image:
688 return Rw2GetPreviewData(data, preview_image_data);
689 case image_type_recognition::kSrwImage:
690 return SrwGetPreviewData(data, preview_image_data);
691 default:
692 return kUnsupported;
693 }
694 }
695
GetDngInformation(StreamInterface * data,std::uint32_t * width,std::uint32_t * height,std::vector<std::uint32_t> * cfa_pattern_dim)696 bool GetDngInformation(StreamInterface* data, std::uint32_t* width,
697 std::uint32_t* height,
698 std::vector<std::uint32_t>* cfa_pattern_dim) {
699 // If IFD0 contains already the full dimensions we do not parse into the sub
700 // IFD.
701 if (!GetDngInformation({}, data, width, height, cfa_pattern_dim)) {
702 return GetDngInformation({kTiffTagSubIfd}, data, width, height,
703 cfa_pattern_dim);
704 }
705 return true;
706 }
707
GetOrientation(StreamInterface * data,std::uint32_t * orientation)708 bool GetOrientation(StreamInterface* data, std::uint32_t* orientation) {
709 using image_type_recognition::GetNumberOfBytesForIsOfType;
710 using image_type_recognition::IsOfType;
711
712 size_t min_header_bytes =
713 std::max(GetNumberOfBytesForIsOfType(image_type_recognition::kRafImage),
714 GetNumberOfBytesForIsOfType(image_type_recognition::kCr3Image));
715
716 std::vector<std::uint8_t> file_header(min_header_bytes);
717 if (data->GetData(0, file_header.size(), file_header.data()) != kOk) {
718 return false;
719 }
720
721 // For RAF and CR# files a special routine is necessary to get orientation.
722 // For others the general approach is sufficient.
723 if (IsOfType(RangeCheckedBytePtr(file_header.data(), file_header.size()),
724 image_type_recognition::kRafImage)) {
725 return RafGetOrientation(data, orientation);
726 } else if (IsOfType(
727 RangeCheckedBytePtr(file_header.data(), file_header.size()),
728 image_type_recognition::kCr3Image)) {
729 return Cr3GetOrientation(data, orientation);
730 } else {
731 return GetExifOrientation(data, 0 /* offset */, orientation);
732 }
733 }
734
SupportedExtensions()735 std::vector<std::string> SupportedExtensions() {
736 return {"ARW", "CR2", "CR3", "DNG", "NEF", "NRW",
737 "ORF", "PEF", "RAF", "RW2", "SRW"};
738 }
739
740 } // namespace piex
741