• 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 = 4;
423   if (!GetPreviewData(extended_tags, 0, kNumberOfIfds, stream, &tiff_content,
424                       preview_image_data)) {
425     return kFail;
426   }
427 
428   // Find the jpeg compressed thumbnail and preview image.
429   Image preview;
430   Image thumbnail;
431 
432   // Search for images in IFD0
433   Image temp_image;
434   if (GetImageData(tiff_content.tiff_directory[0], stream, &temp_image)) {
435     if (IsThumbnail(temp_image, kDngThumbnailMaxDimension)) {
436       thumbnail = temp_image;
437     } else if (temp_image.format == Image::kJpegCompressed) {
438       preview = temp_image;
439     }
440   }
441 
442   // Search for images in other IFDs
443   for (const auto& ifd : tiff_content.tiff_directory[0].GetSubDirectories()) {
444     if (GetImageData(ifd, stream, &temp_image)) {
445       // Try to find the largest thumbnail/preview.
446       if (IsThumbnail(temp_image, kDngThumbnailMaxDimension)) {
447         if (temp_image > thumbnail) {
448           thumbnail = temp_image;
449         }
450       } else {
451         if (temp_image > preview &&
452             temp_image.format == Image::kJpegCompressed) {
453           preview = temp_image;
454         }
455       }
456     }
457   }
458   preview_image_data->preview = preview;
459   preview_image_data->thumbnail = thumbnail;
460 
461   return kOk;
462 }
463 
NefGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)464 Error NefGetPreviewData(StreamInterface* stream,
465                         PreviewImageData* preview_image_data) {
466   const TagSet extended_tags = {kTiffTagImageWidth,      kTiffTagImageLength,
467                                 kTiffTagJpegByteCount,   kTiffTagJpegOffset,
468                                 kTiffTagStripByteCounts, kTiffTagStripOffsets,
469                                 kTiffTagSubIfd};
470   const std::uint32_t kNumberOfIfds = 2;
471   if (!GetPreviewData(extended_tags, kNumberOfIfds, stream,
472                       preview_image_data)) {
473     return kFail;
474   }
475 
476   if (preview_image_data->thumbnail.length == 0) {
477     PreviewImageData thumbnail_data;
478     GetThumbnailOffsetAndLength(TagSet(), stream, &thumbnail_data);
479     preview_image_data->thumbnail = thumbnail_data.thumbnail;
480   }
481 
482   // The Nikon RAW data provides the dimensions of the sensor image, which are
483   // slightly larger than the dimensions of the preview image. In order to
484   // determine the correct full width and height of the image, the preview image
485   // size needs to be taken into account. Based on experiments the preview image
486   // dimensions must be at least 90% of the sensor image dimensions to let it be
487   // a full size preview image.
488   if (preview_image_data->preview.length > 0) {  // when preview image exists
489     const float kEpsilon = 0.9f;
490 
491     std::uint16_t width;
492     std::uint16_t height;
493     if (!GetJpegDimensions(preview_image_data->preview.offset, stream, &width,
494                            &height) ||
495         preview_image_data->full_width == 0 ||
496         preview_image_data->full_height == 0) {
497       return kUnsupported;
498     }
499 
500     if (static_cast<float>(width) /
501                 static_cast<float>(preview_image_data->full_width) >
502             kEpsilon ||
503         static_cast<float>(height) /
504                 static_cast<float>(preview_image_data->full_height) >
505             kEpsilon) {
506       preview_image_data->full_width = width;
507       preview_image_data->full_height = height;
508     }
509   }
510   return kOk;
511 }
512 
OrfGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)513 Error OrfGetPreviewData(StreamInterface* stream,
514                         PreviewImageData* preview_image_data) {
515   if (!GetExifData(0, stream, preview_image_data)) {
516     return kFail;
517   }
518   // Omit errors, because some images do not contain any preview data.
519   GetOlympusPreviewImage(stream, preview_image_data);
520   return kOk;
521 }
522 
PefGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)523 Error PefGetPreviewData(StreamInterface* stream,
524                         PreviewImageData* preview_image_data) {
525   const TagSet extended_tags = {kTiffTagImageWidth, kTiffTagImageLength,
526                                 kTiffTagJpegByteCount, kTiffTagJpegOffset,
527                                 kTiffTagSubIfd};
528   const std::uint32_t kNumberOfIfds = 3;
529   if (!GetPreviewData(extended_tags, kNumberOfIfds, stream,
530                       preview_image_data) ||
531       !PefGetColorSpace(stream, preview_image_data)) {
532     return kFail;
533   }
534 
535   PreviewImageData thumbnail_data;
536   GetThumbnailOffsetAndLength(TagSet(), stream, &thumbnail_data);
537   preview_image_data->thumbnail = thumbnail_data.thumbnail;
538 
539   return kOk;
540 }
541 
RafGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)542 Error RafGetPreviewData(StreamInterface* stream,
543                         PreviewImageData* preview_image_data) {
544   // Parse the Fuji RAW header to get the offset and length of the preview
545   // image, which contains the Exif information.
546   const Endian endian = tiff_directory::kBigEndian;
547   std::uint32_t preview_offset = 0;
548   std::uint32_t preview_length = 0;
549   if (!Get32u(stream, kRafOffsetToPreviewOffset, endian, &preview_offset) ||
550       !Get32u(stream, kRafOffsetToPreviewOffset + 4, endian, &preview_length)) {
551     return kFail;
552   }
553 
554   if (!RafGetDimension(stream, &preview_image_data->full_width,
555                        &preview_image_data->full_height)) {
556     return kFail;
557   }
558 
559   if (preview_length > 0) {  // when preview image exists
560     // Parse the Exif information from the preview image.
561     const std::uint32_t exif_offset = preview_offset + 12;
562     if (!GetExifData(exif_offset, stream, preview_image_data)) {
563       return kFail;
564     }
565   }
566 
567   // Merge the Exif data with the RAW data to form the preview_image_data.
568   preview_image_data->thumbnail.offset += 160;  // Skip the cfa header.
569   preview_image_data->preview.offset = preview_offset;
570   preview_image_data->preview.length = preview_length;
571   return kOk;
572 }
573 
Rw2GetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)574 Error Rw2GetPreviewData(StreamInterface* stream,
575                         PreviewImageData* preview_image_data) {
576   const TagSet extended_tags = {kPanaTagTopBorder,     kPanaTagLeftBorder,
577                                 kPanaTagBottomBorder,  kPanaTagRightBorder,
578                                 kPanaTagIso,           kPanaTagJpegImage,
579                                 kTiffTagJpegByteCount, kTiffTagJpegOffset};
580   // Parse the RAW data to get the ISO, offset and length of the preview image,
581   // which contains the Exif information.
582   const std::uint32_t kNumberOfIfds = 1;
583   PreviewImageData preview_data;
584   if (!GetPreviewData(extended_tags, kNumberOfIfds, stream, &preview_data)) {
585     return kFail;
586   }
587 
588   if (preview_data.preview.length > 0) {  // when preview image exists
589     // Parse the Exif information from the preview image.
590     const std::uint32_t exif_offset = preview_data.preview.offset + 12;
591     if (!GetExifData(exif_offset, stream, preview_image_data)) {
592       return kFail;
593     }
594     preview_image_data->thumbnail.offset += exif_offset;
595   }
596 
597   // Merge the Exif data with the RAW data to form the preview_image_data.
598   preview_image_data->preview = preview_data.preview;
599   preview_image_data->iso = preview_data.iso;
600   preview_image_data->full_width = preview_data.full_width;
601   preview_image_data->full_height = preview_data.full_height;
602 
603   return kOk;
604 }
605 
SrwGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)606 Error SrwGetPreviewData(StreamInterface* stream,
607                         PreviewImageData* preview_image_data) {
608   GetThumbnailOffsetAndLength({kTiffTagSubIfd}, stream, preview_image_data);
609 
610   const TagSet extended_tags = {kExifTagWidth, kExifTagHeight,
611                                 kTiffTagJpegByteCount, kTiffTagJpegOffset,
612                                 kTiffTagSubIfd};
613   const std::uint32_t kNumberOfIfds = 1;
614   if (!GetPreviewData(extended_tags, kNumberOfIfds, stream,
615                       preview_image_data)) {
616     return kFail;
617   }
618   return kOk;
619 }
620 
621 }  // namespace
622 
BytesRequiredForIsRaw()623 size_t BytesRequiredForIsRaw() {
624   return image_type_recognition::GetNumberOfBytesForIsRawLite();
625 }
626 
IsRaw(StreamInterface * data)627 bool IsRaw(StreamInterface* data) {
628   const size_t bytes = BytesRequiredForIsRaw();
629   if (data == nullptr) {
630     return false;
631   }
632 
633   // Read required number of bytes into a vector.
634   std::vector<std::uint8_t> file_header(bytes);
635   if (data->GetData(0, file_header.size(), file_header.data()) != kOk) {
636     return false;
637   }
638 
639   RangeCheckedBytePtr data_buffer(file_header.data(), file_header.size());
640 
641   return image_type_recognition::IsRawLite(data_buffer);
642 }
643 
GetPreviewImageData(StreamInterface * data,PreviewImageData * preview_image_data)644 Error GetPreviewImageData(StreamInterface* data,
645                           PreviewImageData* preview_image_data) {
646   const size_t bytes = BytesRequiredForIsRaw();
647   if (data == nullptr || bytes == 0) {
648     return kFail;
649   }
650 
651   std::vector<std::uint8_t> file_header(bytes);
652   Error error = data->GetData(0, file_header.size(), file_header.data());
653   if (error != kOk) {
654     return error;
655   }
656   RangeCheckedBytePtr header_buffer(file_header.data(), file_header.size());
657 
658   switch (RecognizeRawImageTypeLite(header_buffer)) {
659     case image_type_recognition::kArwImage:
660       return ArwGetPreviewData(data, preview_image_data);
661     case image_type_recognition::kCr2Image:
662       return Cr2GetPreviewData(data, preview_image_data);
663     case image_type_recognition::kDngImage:
664       return DngGetPreviewData(data, preview_image_data);
665     case image_type_recognition::kNefImage:
666     case image_type_recognition::kNrwImage:
667       return NefGetPreviewData(data, preview_image_data);
668     case image_type_recognition::kOrfImage:
669       return OrfGetPreviewData(data, preview_image_data);
670     case image_type_recognition::kPefImage:
671       return PefGetPreviewData(data, preview_image_data);
672     case image_type_recognition::kRafImage:
673       return RafGetPreviewData(data, preview_image_data);
674     case image_type_recognition::kRw2Image:
675       return Rw2GetPreviewData(data, preview_image_data);
676     case image_type_recognition::kSrwImage:
677       return SrwGetPreviewData(data, preview_image_data);
678     default:
679       return kUnsupported;
680   }
681 }
682 
GetDngInformation(StreamInterface * data,std::uint32_t * width,std::uint32_t * height,std::vector<std::uint32_t> * cfa_pattern_dim)683 bool GetDngInformation(StreamInterface* data, std::uint32_t* width,
684                        std::uint32_t* height,
685                        std::vector<std::uint32_t>* cfa_pattern_dim) {
686   // If IFD0 contains already the full dimensions we do not parse into the sub
687   // IFD.
688   if (!GetDngInformation({}, data, width, height, cfa_pattern_dim)) {
689     return GetDngInformation({kTiffTagSubIfd}, data, width, height,
690                              cfa_pattern_dim);
691   }
692   return true;
693 }
694 
GetOrientation(StreamInterface * data,std::uint32_t * orientation)695 bool GetOrientation(StreamInterface* data, std::uint32_t* orientation) {
696   using image_type_recognition::GetNumberOfBytesForIsOfType;
697   using image_type_recognition::IsOfType;
698 
699   std::vector<std::uint8_t> file_header(
700       GetNumberOfBytesForIsOfType(image_type_recognition::kRafImage));
701   if (data->GetData(0, file_header.size(), file_header.data()) != kOk) {
702     return false;
703   }
704 
705   // For RAF files a special routine is necessary to get orientation. For others
706   // the general approach is sufficient.
707   if (IsOfType(RangeCheckedBytePtr(file_header.data(), file_header.size()),
708                image_type_recognition::kRafImage)) {
709     return RafGetOrientation(data, orientation);
710   } else {
711     return GetExifOrientation(data, 0 /* offset */, orientation);
712   }
713 }
714 
SupportedExtensions()715 std::vector<std::string> SupportedExtensions() {
716   return {"ARW", "CR2", "DNG", "NEF", "NRW", "ORF", "PEF", "RAF", "RW2", "SRW"};
717 }
718 
719 }  // namespace piex
720