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
37const std::uint32_t kRafOffsetToPreviewOffset = 84;
38
39bool 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
54bool 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
80bool 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
105bool 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
114bool 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.
124void 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
137bool 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
162bool 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
180bool 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
203bool 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.
229bool 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
301bool 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
332bool 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.
346bool 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
381Error 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
397Error 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
412Error 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
464Error 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
513Error 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
523Error 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
542Error 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
574Error 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
606Error 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
623size_t BytesRequiredForIsRaw() {
624  return image_type_recognition::GetNumberOfBytesForIsRawLite();
625}
626
627bool 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
644Error 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
683bool 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
695bool 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
715std::vector<std::string> SupportedExtensions() {
716  return {"ARW", "CR2", "DNG", "NEF", "NRW", "ORF", "PEF", "RAF", "RW2", "SRW"};
717}
718
719}  // namespace piex
720