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