• 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/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