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