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