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