xref: /aosp_15_r20/external/piex/src/piex.cc (revision 4d671364a067eb4f124488347677d916765212d1)
1*4d671364SKiyoung Kim // Copyright 2015 Google Inc.
2*4d671364SKiyoung Kim //
3*4d671364SKiyoung Kim // Licensed under the Apache License, Version 2.0 (the "License");
4*4d671364SKiyoung Kim // you may not use this file except in compliance with the License.
5*4d671364SKiyoung Kim // You may obtain a copy of the License at
6*4d671364SKiyoung Kim //
7*4d671364SKiyoung Kim //      http://www.apache.org/licenses/LICENSE-2.0
8*4d671364SKiyoung Kim //
9*4d671364SKiyoung Kim // Unless required by applicable law or agreed to in writing, software
10*4d671364SKiyoung Kim // distributed under the License is distributed on an "AS IS" BASIS,
11*4d671364SKiyoung Kim // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*4d671364SKiyoung Kim // See the License for the specific language governing permissions and
13*4d671364SKiyoung Kim // limitations under the License.
14*4d671364SKiyoung Kim //
15*4d671364SKiyoung Kim ////////////////////////////////////////////////////////////////////////////////
16*4d671364SKiyoung Kim 
17*4d671364SKiyoung Kim #include "src/piex.h"
18*4d671364SKiyoung Kim 
19*4d671364SKiyoung Kim #include <cstdint>
20*4d671364SKiyoung Kim #include <limits>
21*4d671364SKiyoung Kim #include <set>
22*4d671364SKiyoung Kim #include <vector>
23*4d671364SKiyoung Kim 
24*4d671364SKiyoung Kim #include "src/binary_parse/range_checked_byte_ptr.h"
25*4d671364SKiyoung Kim #include "src/image_type_recognition/image_type_recognition_lite.h"
26*4d671364SKiyoung Kim #include "src/piex_cr3.h"
27*4d671364SKiyoung Kim #include "src/tiff_parser.h"
28*4d671364SKiyoung Kim 
29*4d671364SKiyoung Kim namespace piex {
30*4d671364SKiyoung Kim namespace {
31*4d671364SKiyoung Kim 
32*4d671364SKiyoung Kim using binary_parse::RangeCheckedBytePtr;
33*4d671364SKiyoung Kim using image_type_recognition::RawImageTypes;
34*4d671364SKiyoung Kim using image_type_recognition::RecognizeRawImageTypeLite;
35*4d671364SKiyoung Kim using tiff_directory::Endian;
36*4d671364SKiyoung Kim using tiff_directory::TiffDirectory;
37*4d671364SKiyoung Kim 
38*4d671364SKiyoung Kim const std::uint32_t kRafOffsetToPreviewOffset = 84;
39*4d671364SKiyoung Kim 
GetDngInformation(const tiff_directory::TiffDirectory & tiff_directory,std::uint32_t * width,std::uint32_t * height,std::vector<std::uint32_t> * cfa_pattern_dim)40*4d671364SKiyoung Kim bool GetDngInformation(const tiff_directory::TiffDirectory& tiff_directory,
41*4d671364SKiyoung Kim                        std::uint32_t* width, std::uint32_t* height,
42*4d671364SKiyoung Kim                        std::vector<std::uint32_t>* cfa_pattern_dim) {
43*4d671364SKiyoung Kim   if (!GetFullDimension32(tiff_directory, width, height) || *width == 0 ||
44*4d671364SKiyoung Kim       *height == 0) {
45*4d671364SKiyoung Kim     return false;
46*4d671364SKiyoung Kim   }
47*4d671364SKiyoung Kim 
48*4d671364SKiyoung Kim   if (!tiff_directory.Get(kTiffTagCfaPatternDim, cfa_pattern_dim) ||
49*4d671364SKiyoung Kim       cfa_pattern_dim->size() != 2) {
50*4d671364SKiyoung Kim     return false;
51*4d671364SKiyoung Kim   }
52*4d671364SKiyoung Kim   return true;
53*4d671364SKiyoung Kim }
54*4d671364SKiyoung Kim 
GetDngInformation(const TagSet & extended_tags,StreamInterface * data,std::uint32_t * width,std::uint32_t * height,std::vector<std::uint32_t> * cfa_pattern_dim)55*4d671364SKiyoung Kim bool GetDngInformation(const TagSet& extended_tags, StreamInterface* data,
56*4d671364SKiyoung Kim                        std::uint32_t* width, std::uint32_t* height,
57*4d671364SKiyoung Kim                        std::vector<std::uint32_t>* cfa_pattern_dim) {
58*4d671364SKiyoung Kim   TagSet desired_tags = {kExifTagDefaultCropSize, kTiffTagCfaPatternDim,
59*4d671364SKiyoung Kim                          kTiffTagExifIfd, kTiffTagSubFileType};
60*4d671364SKiyoung Kim   desired_tags.insert(extended_tags.cbegin(), extended_tags.cend());
61*4d671364SKiyoung Kim 
62*4d671364SKiyoung Kim   TiffParser tiff_parser(data, 0 /* offset */);
63*4d671364SKiyoung Kim 
64*4d671364SKiyoung Kim   TiffContent tiff_content;
65*4d671364SKiyoung Kim   if (!tiff_parser.Parse(desired_tags, 1, &tiff_content) ||
66*4d671364SKiyoung Kim       tiff_content.tiff_directory.empty()) {
67*4d671364SKiyoung Kim     return false;
68*4d671364SKiyoung Kim   }
69*4d671364SKiyoung Kim 
70*4d671364SKiyoung Kim   // If IFD0 contains already the full dimensions we do not parse into the sub
71*4d671364SKiyoung Kim   // IFD.
72*4d671364SKiyoung Kim   const TiffDirectory& tiff_directory = tiff_content.tiff_directory[0];
73*4d671364SKiyoung Kim   if (tiff_directory.GetSubDirectories().empty()) {
74*4d671364SKiyoung Kim     return GetDngInformation(tiff_directory, width, height, cfa_pattern_dim);
75*4d671364SKiyoung Kim   } else {
76*4d671364SKiyoung Kim     return GetDngInformation(tiff_directory.GetSubDirectories()[0], width,
77*4d671364SKiyoung Kim                              height, cfa_pattern_dim);
78*4d671364SKiyoung Kim   }
79*4d671364SKiyoung Kim }
80*4d671364SKiyoung Kim 
GetPreviewData(const TagSet & extended_tags,const std::uint32_t tiff_offset,const std::uint32_t number_of_ifds,StreamInterface * stream,TiffContent * tiff_content,PreviewImageData * preview_image_data)81*4d671364SKiyoung Kim bool GetPreviewData(const TagSet& extended_tags,
82*4d671364SKiyoung Kim                     const std::uint32_t tiff_offset,
83*4d671364SKiyoung Kim                     const std::uint32_t number_of_ifds, StreamInterface* stream,
84*4d671364SKiyoung Kim                     TiffContent* tiff_content,
85*4d671364SKiyoung Kim                     PreviewImageData* preview_image_data) {
86*4d671364SKiyoung Kim   TagSet desired_tags = {
87*4d671364SKiyoung Kim       kExifTagColorSpace, kExifTagDateTimeOriginal, kExifTagExposureTime,
88*4d671364SKiyoung Kim       kExifTagFnumber,    kExifTagFocalLength,      kExifTagGps,
89*4d671364SKiyoung Kim       kExifTagIsoSpeed,   kTiffTagCompression,      kTiffTagDateTime,
90*4d671364SKiyoung Kim       kTiffTagExifIfd,    kTiffTagCfaPatternDim,    kTiffTagMake,
91*4d671364SKiyoung Kim       kTiffTagModel,      kTiffTagOrientation,      kTiffTagPhotometric};
92*4d671364SKiyoung Kim   desired_tags.insert(extended_tags.cbegin(), extended_tags.cend());
93*4d671364SKiyoung Kim 
94*4d671364SKiyoung Kim   TiffParser tiff_parser(stream, tiff_offset);
95*4d671364SKiyoung Kim 
96*4d671364SKiyoung Kim   if (!tiff_parser.Parse(desired_tags, number_of_ifds, tiff_content)) {
97*4d671364SKiyoung Kim     return false;
98*4d671364SKiyoung Kim   }
99*4d671364SKiyoung Kim   if (tiff_content->tiff_directory.empty()) {
100*4d671364SKiyoung Kim     // Returns false if the stream does not contain any TIFF structure.
101*4d671364SKiyoung Kim     return false;
102*4d671364SKiyoung Kim   }
103*4d671364SKiyoung Kim   return tiff_parser.GetPreviewImageData(*tiff_content, preview_image_data);
104*4d671364SKiyoung Kim }
105*4d671364SKiyoung Kim 
GetPreviewData(const TagSet & extended_tags,const std::uint32_t number_of_ifds,StreamInterface * stream,PreviewImageData * preview_image_data)106*4d671364SKiyoung Kim bool GetPreviewData(const TagSet& extended_tags,
107*4d671364SKiyoung Kim                     const std::uint32_t number_of_ifds, StreamInterface* stream,
108*4d671364SKiyoung Kim                     PreviewImageData* preview_image_data) {
109*4d671364SKiyoung Kim   const std::uint32_t kTiffOffset = 0;
110*4d671364SKiyoung Kim   TiffContent tiff_content;
111*4d671364SKiyoung Kim   return GetPreviewData(extended_tags, kTiffOffset, number_of_ifds, stream,
112*4d671364SKiyoung Kim                         &tiff_content, preview_image_data);
113*4d671364SKiyoung Kim }
114*4d671364SKiyoung Kim 
GetExifData(const std::uint32_t exif_offset,StreamInterface * stream,PreviewImageData * preview_image_data)115*4d671364SKiyoung Kim bool GetExifData(const std::uint32_t exif_offset, StreamInterface* stream,
116*4d671364SKiyoung Kim                  PreviewImageData* preview_image_data) {
117*4d671364SKiyoung Kim   const TagSet kExtendedTags = {kTiffTagJpegByteCount, kTiffTagJpegOffset};
118*4d671364SKiyoung Kim   const std::uint32_t kNumberOfIfds = 2;
119*4d671364SKiyoung Kim   TiffContent tiff_content;
120*4d671364SKiyoung Kim   return GetPreviewData(kExtendedTags, exif_offset, kNumberOfIfds, stream,
121*4d671364SKiyoung Kim                         &tiff_content, preview_image_data);
122*4d671364SKiyoung Kim }
123*4d671364SKiyoung Kim 
124*4d671364SKiyoung Kim // Reads the jpeg compressed thumbnail information.
GetThumbnailOffsetAndLength(const TagSet & extended_tags,StreamInterface * stream,PreviewImageData * preview_image_data)125*4d671364SKiyoung Kim void GetThumbnailOffsetAndLength(const TagSet& extended_tags,
126*4d671364SKiyoung Kim                                  StreamInterface* stream,
127*4d671364SKiyoung Kim                                  PreviewImageData* preview_image_data) {
128*4d671364SKiyoung Kim   TagSet desired_tags = {kTiffTagJpegByteCount, kTiffTagJpegOffset};
129*4d671364SKiyoung Kim   desired_tags.insert(extended_tags.cbegin(), extended_tags.cend());
130*4d671364SKiyoung Kim 
131*4d671364SKiyoung Kim   const std::uint32_t kNumberOfIfds = 2;
132*4d671364SKiyoung Kim   PreviewImageData thumbnail_data;
133*4d671364SKiyoung Kim   if (GetPreviewData(desired_tags, kNumberOfIfds, stream, &thumbnail_data)) {
134*4d671364SKiyoung Kim     preview_image_data->thumbnail = thumbnail_data.thumbnail;
135*4d671364SKiyoung Kim   }
136*4d671364SKiyoung Kim }
137*4d671364SKiyoung Kim 
GetExifIfd(const Endian endian,StreamInterface * stream,TiffDirectory * exif_ifd)138*4d671364SKiyoung Kim bool GetExifIfd(const Endian endian, StreamInterface* stream,
139*4d671364SKiyoung Kim                 TiffDirectory* exif_ifd) {
140*4d671364SKiyoung Kim   const std::uint32_t kTiffOffset = 0;
141*4d671364SKiyoung Kim   std::uint32_t offset_to_ifd;
142*4d671364SKiyoung Kim   if (!Get32u(stream, sizeof(offset_to_ifd), endian, &offset_to_ifd)) {
143*4d671364SKiyoung Kim     return false;
144*4d671364SKiyoung Kim   }
145*4d671364SKiyoung Kim 
146*4d671364SKiyoung Kim   std::uint32_t next_ifd_offset;
147*4d671364SKiyoung Kim   TiffDirectory tiff_ifd(endian);
148*4d671364SKiyoung Kim   if (!ParseDirectory(kTiffOffset, offset_to_ifd, endian, {kTiffTagExifIfd},
149*4d671364SKiyoung Kim                       stream, &tiff_ifd, &next_ifd_offset)) {
150*4d671364SKiyoung Kim     return false;
151*4d671364SKiyoung Kim   }
152*4d671364SKiyoung Kim 
153*4d671364SKiyoung Kim   std::uint32_t exif_offset;
154*4d671364SKiyoung Kim   if (tiff_ifd.Get(kTiffTagExifIfd, &exif_offset)) {
155*4d671364SKiyoung Kim     return ParseDirectory(kTiffOffset, exif_offset, endian,
156*4d671364SKiyoung Kim                           {kExifTagMakernotes}, stream, exif_ifd,
157*4d671364SKiyoung Kim                           &next_ifd_offset);
158*4d671364SKiyoung Kim   }
159*4d671364SKiyoung Kim 
160*4d671364SKiyoung Kim   return true;
161*4d671364SKiyoung Kim }
162*4d671364SKiyoung Kim 
GetMakernoteIfd(const TiffDirectory & exif_ifd,const Endian endian,const std::uint32_t skip_offset,StreamInterface * stream,std::uint32_t * makernote_offset,TiffDirectory * makernote_ifd)163*4d671364SKiyoung Kim bool GetMakernoteIfd(const TiffDirectory& exif_ifd, const Endian endian,
164*4d671364SKiyoung Kim                      const std::uint32_t skip_offset, StreamInterface* stream,
165*4d671364SKiyoung Kim                      std::uint32_t* makernote_offset,
166*4d671364SKiyoung Kim                      TiffDirectory* makernote_ifd) {
167*4d671364SKiyoung Kim   std::uint32_t makernote_length;
168*4d671364SKiyoung Kim   if (!exif_ifd.GetOffsetAndLength(kExifTagMakernotes,
169*4d671364SKiyoung Kim                                    tiff_directory::TIFF_TYPE_UNDEFINED,
170*4d671364SKiyoung Kim                                    makernote_offset, &makernote_length)) {
171*4d671364SKiyoung Kim     return false;
172*4d671364SKiyoung Kim   }
173*4d671364SKiyoung Kim 
174*4d671364SKiyoung Kim   std::uint32_t next_ifd_offset;
175*4d671364SKiyoung Kim   return ParseDirectory(*makernote_offset, *makernote_offset + skip_offset,
176*4d671364SKiyoung Kim                         endian, {kTiffTagImageWidth, kOlymTagCameraSettings,
177*4d671364SKiyoung Kim                                  kOlymTagRawProcessing, kPentaxTagColorSpace},
178*4d671364SKiyoung Kim                         stream, makernote_ifd, &next_ifd_offset);
179*4d671364SKiyoung Kim }
180*4d671364SKiyoung Kim 
GetCameraSettingsIfd(const TiffDirectory & makernote_ifd,const std::uint32_t makernote_offset,const Endian endian,StreamInterface * stream,TiffDirectory * camera_settings_ifd)181*4d671364SKiyoung Kim bool GetCameraSettingsIfd(const TiffDirectory& makernote_ifd,
182*4d671364SKiyoung Kim                           const std::uint32_t makernote_offset,
183*4d671364SKiyoung Kim                           const Endian endian, StreamInterface* stream,
184*4d671364SKiyoung Kim                           TiffDirectory* camera_settings_ifd) {
185*4d671364SKiyoung Kim   std::uint32_t camera_settings_offset;
186*4d671364SKiyoung Kim   std::uint32_t camera_settings_length;
187*4d671364SKiyoung Kim   if (!makernote_ifd.GetOffsetAndLength(
188*4d671364SKiyoung Kim           kOlymTagCameraSettings, tiff_directory::TIFF_IFD,
189*4d671364SKiyoung Kim           &camera_settings_offset, &camera_settings_length)) {
190*4d671364SKiyoung Kim     return false;
191*4d671364SKiyoung Kim   }
192*4d671364SKiyoung Kim 
193*4d671364SKiyoung Kim   std::uint32_t next_ifd_offset;
194*4d671364SKiyoung Kim   if (!Get32u(stream, camera_settings_offset, endian,
195*4d671364SKiyoung Kim               &camera_settings_offset)) {
196*4d671364SKiyoung Kim     return false;
197*4d671364SKiyoung Kim   }
198*4d671364SKiyoung Kim   return ParseDirectory(makernote_offset,
199*4d671364SKiyoung Kim                         makernote_offset + camera_settings_offset, endian,
200*4d671364SKiyoung Kim                         {kTiffTagBitsPerSample, kTiffTagImageLength}, stream,
201*4d671364SKiyoung Kim                         camera_settings_ifd, &next_ifd_offset);
202*4d671364SKiyoung Kim }
203*4d671364SKiyoung Kim 
GetRawProcessingIfd(const TagSet & desired_tags,const TiffDirectory & makernote_ifd,const std::uint32_t makernote_offset,const Endian endian,StreamInterface * stream,TiffDirectory * raw_processing_ifd)204*4d671364SKiyoung Kim bool GetRawProcessingIfd(const TagSet& desired_tags,
205*4d671364SKiyoung Kim                          const TiffDirectory& makernote_ifd,
206*4d671364SKiyoung Kim                          const std::uint32_t makernote_offset,
207*4d671364SKiyoung Kim                          const Endian endian, StreamInterface* stream,
208*4d671364SKiyoung Kim                          TiffDirectory* raw_processing_ifd) {
209*4d671364SKiyoung Kim   std::uint32_t raw_processing_offset;
210*4d671364SKiyoung Kim   std::uint32_t raw_processing_length;
211*4d671364SKiyoung Kim   if (!makernote_ifd.GetOffsetAndLength(
212*4d671364SKiyoung Kim           kOlymTagRawProcessing, tiff_directory::TIFF_IFD,
213*4d671364SKiyoung Kim           &raw_processing_offset, &raw_processing_length)) {
214*4d671364SKiyoung Kim     return false;
215*4d671364SKiyoung Kim   }
216*4d671364SKiyoung Kim 
217*4d671364SKiyoung Kim   std::uint32_t next_ifd_offset;
218*4d671364SKiyoung Kim   if (!Get32u(stream, raw_processing_offset, endian, &raw_processing_offset)) {
219*4d671364SKiyoung Kim     return false;
220*4d671364SKiyoung Kim   }
221*4d671364SKiyoung Kim 
222*4d671364SKiyoung Kim   return ParseDirectory(
223*4d671364SKiyoung Kim       makernote_offset, makernote_offset + raw_processing_offset, endian,
224*4d671364SKiyoung Kim       desired_tags, stream, raw_processing_ifd, &next_ifd_offset);
225*4d671364SKiyoung Kim }
226*4d671364SKiyoung Kim 
227*4d671364SKiyoung Kim // Retrieves the preview image offset and length from the camera settings and
228*4d671364SKiyoung Kim // the 'full_width' and 'full_height' from the raw processing ifd in 'stream'.
229*4d671364SKiyoung Kim // Returns false if anything is wrong.
GetOlympusPreviewImage(StreamInterface * stream,PreviewImageData * preview_image_data)230*4d671364SKiyoung Kim bool GetOlympusPreviewImage(StreamInterface* stream,
231*4d671364SKiyoung Kim                             PreviewImageData* preview_image_data) {
232*4d671364SKiyoung Kim   Endian endian;
233*4d671364SKiyoung Kim   if (!GetEndianness(0 /* tiff offset */, stream, &endian)) {
234*4d671364SKiyoung Kim     return false;
235*4d671364SKiyoung Kim   }
236*4d671364SKiyoung Kim 
237*4d671364SKiyoung Kim   TiffDirectory exif_ifd(endian);
238*4d671364SKiyoung Kim   if (!GetExifIfd(endian, stream, &exif_ifd)) {
239*4d671364SKiyoung Kim     return false;
240*4d671364SKiyoung Kim   }
241*4d671364SKiyoung Kim 
242*4d671364SKiyoung Kim   std::uint32_t makernote_offset;
243*4d671364SKiyoung Kim   TiffDirectory makernote_ifd(endian);
244*4d671364SKiyoung Kim   const std::uint32_t kSkipMakernoteStart = 12;
245*4d671364SKiyoung Kim   if (!GetMakernoteIfd(exif_ifd, endian, kSkipMakernoteStart, stream,
246*4d671364SKiyoung Kim                        &makernote_offset, &makernote_ifd)) {
247*4d671364SKiyoung Kim     return false;
248*4d671364SKiyoung Kim   }
249*4d671364SKiyoung Kim 
250*4d671364SKiyoung Kim   const std::uint32_t kThumbnailTag = 0x0100;
251*4d671364SKiyoung Kim   if (makernote_ifd.Has(kThumbnailTag)) {
252*4d671364SKiyoung Kim     if (!makernote_ifd.GetOffsetAndLength(
253*4d671364SKiyoung Kim             kThumbnailTag, tiff_directory::TIFF_TYPE_UNDEFINED,
254*4d671364SKiyoung Kim             &preview_image_data->thumbnail.offset,
255*4d671364SKiyoung Kim             &preview_image_data->thumbnail.length)) {
256*4d671364SKiyoung Kim       return false;
257*4d671364SKiyoung Kim     }
258*4d671364SKiyoung Kim   }
259*4d671364SKiyoung Kim 
260*4d671364SKiyoung Kim   TiffDirectory camera_settings_ifd(endian);
261*4d671364SKiyoung Kim   if (!GetCameraSettingsIfd(makernote_ifd, makernote_offset, endian, stream,
262*4d671364SKiyoung Kim                             &camera_settings_ifd)) {
263*4d671364SKiyoung Kim     return false;
264*4d671364SKiyoung Kim   }
265*4d671364SKiyoung Kim 
266*4d671364SKiyoung Kim   const std::uint32_t kPreviewOffset = 0x0101;
267*4d671364SKiyoung Kim   const std::uint32_t kPreviewLength = 0x0102;
268*4d671364SKiyoung Kim   if (!camera_settings_ifd.Has(kPreviewOffset) ||
269*4d671364SKiyoung Kim       !camera_settings_ifd.Has(kPreviewLength)) {
270*4d671364SKiyoung Kim     return false;
271*4d671364SKiyoung Kim   }
272*4d671364SKiyoung Kim 
273*4d671364SKiyoung Kim   camera_settings_ifd.Get(kPreviewOffset, &preview_image_data->preview.offset);
274*4d671364SKiyoung Kim   preview_image_data->preview.offset += makernote_offset;
275*4d671364SKiyoung Kim   camera_settings_ifd.Get(kPreviewLength, &preview_image_data->preview.length);
276*4d671364SKiyoung Kim 
277*4d671364SKiyoung Kim   // Get the crop size from the raw processing ifd.
278*4d671364SKiyoung Kim   TiffDirectory raw_processing_ifd(endian);
279*4d671364SKiyoung Kim   if (!GetRawProcessingIfd({kOlymTagAspectFrame}, makernote_ifd,
280*4d671364SKiyoung Kim                            makernote_offset, endian, stream,
281*4d671364SKiyoung Kim                            &raw_processing_ifd)) {
282*4d671364SKiyoung Kim     return false;
283*4d671364SKiyoung Kim   }
284*4d671364SKiyoung Kim 
285*4d671364SKiyoung Kim   if (raw_processing_ifd.Has(kOlymTagAspectFrame)) {
286*4d671364SKiyoung Kim     std::vector<std::uint32_t> aspect_frame;
287*4d671364SKiyoung Kim     if (raw_processing_ifd.Get(kOlymTagAspectFrame, &aspect_frame) &&
288*4d671364SKiyoung Kim         aspect_frame.size() == 4 && aspect_frame[2] > aspect_frame[0] &&
289*4d671364SKiyoung Kim         aspect_frame[3] > aspect_frame[1]) {
290*4d671364SKiyoung Kim       preview_image_data->full_width = aspect_frame[2] - aspect_frame[0] + 1;
291*4d671364SKiyoung Kim       preview_image_data->full_height = aspect_frame[3] - aspect_frame[1] + 1;
292*4d671364SKiyoung Kim       if (preview_image_data->full_width < preview_image_data->full_height) {
293*4d671364SKiyoung Kim         std::swap(preview_image_data->full_width,
294*4d671364SKiyoung Kim                   preview_image_data->full_height);
295*4d671364SKiyoung Kim       }
296*4d671364SKiyoung Kim     }
297*4d671364SKiyoung Kim   }
298*4d671364SKiyoung Kim 
299*4d671364SKiyoung Kim   return true;
300*4d671364SKiyoung Kim }
301*4d671364SKiyoung Kim 
PefGetColorSpace(StreamInterface * stream,PreviewImageData * preview_image_data)302*4d671364SKiyoung Kim bool PefGetColorSpace(StreamInterface* stream,
303*4d671364SKiyoung Kim                       PreviewImageData* preview_image_data) {
304*4d671364SKiyoung Kim   Endian endian;
305*4d671364SKiyoung Kim   if (!GetEndianness(0 /* tiff offset */, stream, &endian)) {
306*4d671364SKiyoung Kim     return false;
307*4d671364SKiyoung Kim   }
308*4d671364SKiyoung Kim 
309*4d671364SKiyoung Kim   TiffDirectory exif_ifd(endian);
310*4d671364SKiyoung Kim   if (!GetExifIfd(endian, stream, &exif_ifd)) {
311*4d671364SKiyoung Kim     return false;
312*4d671364SKiyoung Kim   }
313*4d671364SKiyoung Kim 
314*4d671364SKiyoung Kim   std::uint32_t makernote_offset;
315*4d671364SKiyoung Kim   TiffDirectory makernote_ifd(endian);
316*4d671364SKiyoung Kim   const std::uint32_t kSkipMakernoteStart = 6;
317*4d671364SKiyoung Kim   if (!GetMakernoteIfd(exif_ifd, endian, kSkipMakernoteStart, stream,
318*4d671364SKiyoung Kim                        &makernote_offset, &makernote_ifd)) {
319*4d671364SKiyoung Kim     return false;
320*4d671364SKiyoung Kim   }
321*4d671364SKiyoung Kim   if (makernote_ifd.Has(kPentaxTagColorSpace)) {
322*4d671364SKiyoung Kim     std::uint32_t color_space;
323*4d671364SKiyoung Kim     if (!makernote_ifd.Get(kPentaxTagColorSpace, &color_space)) {
324*4d671364SKiyoung Kim       return false;
325*4d671364SKiyoung Kim     }
326*4d671364SKiyoung Kim     preview_image_data->color_space = color_space == 0
327*4d671364SKiyoung Kim                                           ? PreviewImageData::kSrgb
328*4d671364SKiyoung Kim                                           : PreviewImageData::kAdobeRgb;
329*4d671364SKiyoung Kim   }
330*4d671364SKiyoung Kim   return true;
331*4d671364SKiyoung Kim }
332*4d671364SKiyoung Kim 
RafGetOrientation(StreamInterface * stream,std::uint32_t * orientation)333*4d671364SKiyoung Kim bool RafGetOrientation(StreamInterface* stream, std::uint32_t* orientation) {
334*4d671364SKiyoung Kim   // Parse the Fuji RAW header to get the offset and length of the preview
335*4d671364SKiyoung Kim   // image, which contains the Exif information.
336*4d671364SKiyoung Kim   const Endian endian = tiff_directory::kBigEndian;
337*4d671364SKiyoung Kim   std::uint32_t preview_offset = 0;
338*4d671364SKiyoung Kim   if (!Get32u(stream, kRafOffsetToPreviewOffset, endian, &preview_offset)) {
339*4d671364SKiyoung Kim     return false;
340*4d671364SKiyoung Kim   }
341*4d671364SKiyoung Kim 
342*4d671364SKiyoung Kim   const std::uint32_t exif_offset = preview_offset + 12;
343*4d671364SKiyoung Kim   return GetExifOrientation(stream, exif_offset, orientation);
344*4d671364SKiyoung Kim }
345*4d671364SKiyoung Kim 
346*4d671364SKiyoung Kim // Parses the Fuji Cfa header for the image width and height.
RafGetDimension(StreamInterface * stream,std::uint32_t * width,std::uint32_t * height)347*4d671364SKiyoung Kim bool RafGetDimension(StreamInterface* stream, std::uint32_t* width,
348*4d671364SKiyoung Kim                      std::uint32_t* height) {
349*4d671364SKiyoung Kim   const Endian endian = tiff_directory::kBigEndian;
350*4d671364SKiyoung Kim   std::uint32_t cfa_header_index = 0;  // actual position in the cfa header.
351*4d671364SKiyoung Kim   std::uint32_t cfa_header_entries = 0;
352*4d671364SKiyoung Kim   if (!Get32u(stream, 92 /* cfa header offset */, endian, &cfa_header_index) ||
353*4d671364SKiyoung Kim       !Get32u(stream, cfa_header_index, endian, &cfa_header_entries)) {
354*4d671364SKiyoung Kim     return false;
355*4d671364SKiyoung Kim   }
356*4d671364SKiyoung Kim 
357*4d671364SKiyoung Kim   // Add 4 to point to the actual read position in the cfa header.
358*4d671364SKiyoung Kim   cfa_header_index += 4;
359*4d671364SKiyoung Kim 
360*4d671364SKiyoung Kim   for (std::uint32_t i = 0; i < cfa_header_entries; ++i) {
361*4d671364SKiyoung Kim     std::uint16_t id = 0;
362*4d671364SKiyoung Kim     std::uint16_t length = 0;
363*4d671364SKiyoung Kim     if (!Get16u(stream, cfa_header_index, endian, &id) ||
364*4d671364SKiyoung Kim         !Get16u(stream, cfa_header_index + 2, endian, &length)) {
365*4d671364SKiyoung Kim       return false;
366*4d671364SKiyoung Kim     }
367*4d671364SKiyoung Kim 
368*4d671364SKiyoung Kim     std::uint16_t tmp_width = 0;
369*4d671364SKiyoung Kim     std::uint16_t tmp_height = 0;
370*4d671364SKiyoung Kim     if (id == 0x0111 /* tags the crop dimensions */ &&
371*4d671364SKiyoung Kim         Get16u(stream, cfa_header_index + 4, endian, &tmp_height) &&
372*4d671364SKiyoung Kim         Get16u(stream, cfa_header_index + 6, endian, &tmp_width)) {
373*4d671364SKiyoung Kim       *width = tmp_width;
374*4d671364SKiyoung Kim       *height = tmp_height;
375*4d671364SKiyoung Kim       return true;
376*4d671364SKiyoung Kim     }
377*4d671364SKiyoung Kim     cfa_header_index += 4u + length;
378*4d671364SKiyoung Kim   }
379*4d671364SKiyoung Kim   return false;
380*4d671364SKiyoung Kim }
381*4d671364SKiyoung Kim 
ArwGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)382*4d671364SKiyoung Kim Error ArwGetPreviewData(StreamInterface* stream,
383*4d671364SKiyoung Kim                         PreviewImageData* preview_image_data) {
384*4d671364SKiyoung Kim   const TagSet extended_tags = {kExifTagHeight, kExifTagWidth,
385*4d671364SKiyoung Kim                                 kTiffTagJpegByteCount, kTiffTagJpegOffset,
386*4d671364SKiyoung Kim                                 kTiffTagSubIfd};
387*4d671364SKiyoung Kim 
388*4d671364SKiyoung Kim   GetThumbnailOffsetAndLength(TagSet(), stream, preview_image_data);
389*4d671364SKiyoung Kim 
390*4d671364SKiyoung Kim   const std::uint32_t kNumberOfIfds = 1;
391*4d671364SKiyoung Kim   if (GetPreviewData(extended_tags, kNumberOfIfds, stream,
392*4d671364SKiyoung Kim                      preview_image_data)) {
393*4d671364SKiyoung Kim     return kOk;
394*4d671364SKiyoung Kim   }
395*4d671364SKiyoung Kim   return kFail;
396*4d671364SKiyoung Kim }
397*4d671364SKiyoung Kim 
Cr2GetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)398*4d671364SKiyoung Kim Error Cr2GetPreviewData(StreamInterface* stream,
399*4d671364SKiyoung Kim                         PreviewImageData* preview_image_data) {
400*4d671364SKiyoung Kim   const TagSet extended_tags = {kExifTagHeight, kExifTagWidth,
401*4d671364SKiyoung Kim                                 kTiffTagStripByteCounts, kTiffTagStripOffsets};
402*4d671364SKiyoung Kim 
403*4d671364SKiyoung Kim   GetThumbnailOffsetAndLength(TagSet(), stream, preview_image_data);
404*4d671364SKiyoung Kim 
405*4d671364SKiyoung Kim   const std::uint32_t kNumberOfIfds = 1;
406*4d671364SKiyoung Kim   if (GetPreviewData(extended_tags, kNumberOfIfds, stream,
407*4d671364SKiyoung Kim                      preview_image_data)) {
408*4d671364SKiyoung Kim     return kOk;
409*4d671364SKiyoung Kim   }
410*4d671364SKiyoung Kim   return kFail;
411*4d671364SKiyoung Kim }
412*4d671364SKiyoung Kim 
DngGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)413*4d671364SKiyoung Kim Error DngGetPreviewData(StreamInterface* stream,
414*4d671364SKiyoung Kim                         PreviewImageData* preview_image_data) {
415*4d671364SKiyoung Kim   // Some thumbnails from DngCreator are larger than the specified 256 pixel.
416*4d671364SKiyoung Kim   const int kDngThumbnailMaxDimension = 512;
417*4d671364SKiyoung Kim 
418*4d671364SKiyoung Kim   const TagSet extended_tags = {
419*4d671364SKiyoung Kim       kExifTagDefaultCropSize, kTiffTagImageWidth,   kTiffTagImageLength,
420*4d671364SKiyoung Kim       kTiffTagStripByteCounts, kTiffTagStripOffsets, kTiffTagSubIfd};
421*4d671364SKiyoung Kim 
422*4d671364SKiyoung Kim   TiffContent tiff_content;
423*4d671364SKiyoung Kim   const std::uint32_t kNumberOfIfds = 3;
424*4d671364SKiyoung Kim   if (!GetPreviewData(extended_tags, 0, kNumberOfIfds, stream, &tiff_content,
425*4d671364SKiyoung Kim                       preview_image_data)) {
426*4d671364SKiyoung Kim     return kFail;
427*4d671364SKiyoung Kim   }
428*4d671364SKiyoung Kim 
429*4d671364SKiyoung Kim   const TiffDirectory& tiff_directory = tiff_content.tiff_directory[0];
430*4d671364SKiyoung Kim 
431*4d671364SKiyoung Kim   if (!GetFullCropDimension(tiff_directory, &preview_image_data->full_width,
432*4d671364SKiyoung Kim                             &preview_image_data->full_height)) {
433*4d671364SKiyoung Kim     return kFail;
434*4d671364SKiyoung Kim   }
435*4d671364SKiyoung Kim 
436*4d671364SKiyoung Kim   // Find the jpeg compressed thumbnail and preview image.
437*4d671364SKiyoung Kim   Image preview;
438*4d671364SKiyoung Kim   Image thumbnail;
439*4d671364SKiyoung Kim 
440*4d671364SKiyoung Kim   // Search for images in IFD0
441*4d671364SKiyoung Kim   Image temp_image;
442*4d671364SKiyoung Kim   if (GetImageData(tiff_directory, stream, &temp_image)) {
443*4d671364SKiyoung Kim     if (IsThumbnail(temp_image, kDngThumbnailMaxDimension)) {
444*4d671364SKiyoung Kim       thumbnail = temp_image;
445*4d671364SKiyoung Kim     } else if (temp_image.format == Image::kJpegCompressed) {
446*4d671364SKiyoung Kim       preview = temp_image;
447*4d671364SKiyoung Kim     }
448*4d671364SKiyoung Kim   }
449*4d671364SKiyoung Kim 
450*4d671364SKiyoung Kim   // Search for images in other IFDs
451*4d671364SKiyoung Kim   for (const auto& ifd : tiff_directory.GetSubDirectories()) {
452*4d671364SKiyoung Kim     if (GetImageData(ifd, stream, &temp_image)) {
453*4d671364SKiyoung Kim       // Try to find the largest thumbnail/preview.
454*4d671364SKiyoung Kim       if (IsThumbnail(temp_image, kDngThumbnailMaxDimension)) {
455*4d671364SKiyoung Kim         if (temp_image > thumbnail) {
456*4d671364SKiyoung Kim           thumbnail = temp_image;
457*4d671364SKiyoung Kim         }
458*4d671364SKiyoung Kim       } else {
459*4d671364SKiyoung Kim         if (temp_image > preview &&
460*4d671364SKiyoung Kim             temp_image.format == Image::kJpegCompressed) {
461*4d671364SKiyoung Kim           preview = temp_image;
462*4d671364SKiyoung Kim         }
463*4d671364SKiyoung Kim       }
464*4d671364SKiyoung Kim     }
465*4d671364SKiyoung Kim   }
466*4d671364SKiyoung Kim   preview_image_data->preview = preview;
467*4d671364SKiyoung Kim   preview_image_data->thumbnail = thumbnail;
468*4d671364SKiyoung Kim 
469*4d671364SKiyoung Kim   return kOk;
470*4d671364SKiyoung Kim }
471*4d671364SKiyoung Kim 
NefGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)472*4d671364SKiyoung Kim Error NefGetPreviewData(StreamInterface* stream,
473*4d671364SKiyoung Kim                         PreviewImageData* preview_image_data) {
474*4d671364SKiyoung Kim   const TagSet extended_tags = {kTiffTagImageWidth,      kTiffTagImageLength,
475*4d671364SKiyoung Kim                                 kTiffTagJpegByteCount,   kTiffTagJpegOffset,
476*4d671364SKiyoung Kim                                 kTiffTagStripByteCounts, kTiffTagStripOffsets,
477*4d671364SKiyoung Kim                                 kTiffTagSubIfd};
478*4d671364SKiyoung Kim   const std::uint32_t kNumberOfIfds = 2;
479*4d671364SKiyoung Kim   if (!GetPreviewData(extended_tags, kNumberOfIfds, stream,
480*4d671364SKiyoung Kim                       preview_image_data)) {
481*4d671364SKiyoung Kim     return kFail;
482*4d671364SKiyoung Kim   }
483*4d671364SKiyoung Kim 
484*4d671364SKiyoung Kim   if (preview_image_data->thumbnail.length == 0) {
485*4d671364SKiyoung Kim     PreviewImageData thumbnail_data;
486*4d671364SKiyoung Kim     GetThumbnailOffsetAndLength(TagSet(), stream, &thumbnail_data);
487*4d671364SKiyoung Kim     preview_image_data->thumbnail = thumbnail_data.thumbnail;
488*4d671364SKiyoung Kim   }
489*4d671364SKiyoung Kim 
490*4d671364SKiyoung Kim   // The Nikon RAW data provides the dimensions of the sensor image, which are
491*4d671364SKiyoung Kim   // slightly larger than the dimensions of the preview image. In order to
492*4d671364SKiyoung Kim   // determine the correct full width and height of the image, the preview image
493*4d671364SKiyoung Kim   // size needs to be taken into account. Based on experiments the preview image
494*4d671364SKiyoung Kim   // dimensions must be at least 90% of the sensor image dimensions to let it be
495*4d671364SKiyoung Kim   // a full size preview image.
496*4d671364SKiyoung Kim   if (preview_image_data->preview.length > 0) {  // when preview image exists
497*4d671364SKiyoung Kim     const float kEpsilon = 0.9f;
498*4d671364SKiyoung Kim 
499*4d671364SKiyoung Kim     std::uint16_t width;
500*4d671364SKiyoung Kim     std::uint16_t height;
501*4d671364SKiyoung Kim     if (!GetJpegDimensions(preview_image_data->preview.offset, stream, &width,
502*4d671364SKiyoung Kim                            &height) ||
503*4d671364SKiyoung Kim         preview_image_data->full_width == 0 ||
504*4d671364SKiyoung Kim         preview_image_data->full_height == 0) {
505*4d671364SKiyoung Kim       return kUnsupported;
506*4d671364SKiyoung Kim     }
507*4d671364SKiyoung Kim 
508*4d671364SKiyoung Kim     if (static_cast<float>(width) /
509*4d671364SKiyoung Kim                 static_cast<float>(preview_image_data->full_width) >
510*4d671364SKiyoung Kim             kEpsilon ||
511*4d671364SKiyoung Kim         static_cast<float>(height) /
512*4d671364SKiyoung Kim                 static_cast<float>(preview_image_data->full_height) >
513*4d671364SKiyoung Kim             kEpsilon) {
514*4d671364SKiyoung Kim       preview_image_data->full_width = width;
515*4d671364SKiyoung Kim       preview_image_data->full_height = height;
516*4d671364SKiyoung Kim     }
517*4d671364SKiyoung Kim   }
518*4d671364SKiyoung Kim   return kOk;
519*4d671364SKiyoung Kim }
520*4d671364SKiyoung Kim 
OrfGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)521*4d671364SKiyoung Kim Error OrfGetPreviewData(StreamInterface* stream,
522*4d671364SKiyoung Kim                         PreviewImageData* preview_image_data) {
523*4d671364SKiyoung Kim   if (!GetExifData(0, stream, preview_image_data)) {
524*4d671364SKiyoung Kim     return kFail;
525*4d671364SKiyoung Kim   }
526*4d671364SKiyoung Kim   // Omit errors, because some images do not contain any preview data.
527*4d671364SKiyoung Kim   GetOlympusPreviewImage(stream, preview_image_data);
528*4d671364SKiyoung Kim   return kOk;
529*4d671364SKiyoung Kim }
530*4d671364SKiyoung Kim 
PefGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)531*4d671364SKiyoung Kim Error PefGetPreviewData(StreamInterface* stream,
532*4d671364SKiyoung Kim                         PreviewImageData* preview_image_data) {
533*4d671364SKiyoung Kim   const TagSet extended_tags = {kTiffTagImageWidth, kTiffTagImageLength,
534*4d671364SKiyoung Kim                                 kTiffTagJpegByteCount, kTiffTagJpegOffset,
535*4d671364SKiyoung Kim                                 kTiffTagSubIfd};
536*4d671364SKiyoung Kim   const std::uint32_t kNumberOfIfds = 3;
537*4d671364SKiyoung Kim   if (!GetPreviewData(extended_tags, kNumberOfIfds, stream,
538*4d671364SKiyoung Kim                       preview_image_data) ||
539*4d671364SKiyoung Kim       !PefGetColorSpace(stream, preview_image_data)) {
540*4d671364SKiyoung Kim     return kFail;
541*4d671364SKiyoung Kim   }
542*4d671364SKiyoung Kim 
543*4d671364SKiyoung Kim   PreviewImageData thumbnail_data;
544*4d671364SKiyoung Kim   GetThumbnailOffsetAndLength(TagSet(), stream, &thumbnail_data);
545*4d671364SKiyoung Kim   preview_image_data->thumbnail = thumbnail_data.thumbnail;
546*4d671364SKiyoung Kim 
547*4d671364SKiyoung Kim   return kOk;
548*4d671364SKiyoung Kim }
549*4d671364SKiyoung Kim 
RafGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)550*4d671364SKiyoung Kim Error RafGetPreviewData(StreamInterface* stream,
551*4d671364SKiyoung Kim                         PreviewImageData* preview_image_data) {
552*4d671364SKiyoung Kim   // Parse the Fuji RAW header to get the offset and length of the preview
553*4d671364SKiyoung Kim   // image, which contains the Exif information.
554*4d671364SKiyoung Kim   const Endian endian = tiff_directory::kBigEndian;
555*4d671364SKiyoung Kim   std::uint32_t preview_offset = 0;
556*4d671364SKiyoung Kim   std::uint32_t preview_length = 0;
557*4d671364SKiyoung Kim   if (!Get32u(stream, kRafOffsetToPreviewOffset, endian, &preview_offset) ||
558*4d671364SKiyoung Kim       !Get32u(stream, kRafOffsetToPreviewOffset + 4, endian, &preview_length)) {
559*4d671364SKiyoung Kim     return kFail;
560*4d671364SKiyoung Kim   }
561*4d671364SKiyoung Kim 
562*4d671364SKiyoung Kim   if (!RafGetDimension(stream, &preview_image_data->full_width,
563*4d671364SKiyoung Kim                        &preview_image_data->full_height)) {
564*4d671364SKiyoung Kim     return kFail;
565*4d671364SKiyoung Kim   }
566*4d671364SKiyoung Kim 
567*4d671364SKiyoung Kim   if (preview_length > 0) {  // when preview image exists
568*4d671364SKiyoung Kim     // Parse the Exif information from the preview image.
569*4d671364SKiyoung Kim     const std::uint32_t exif_offset = preview_offset + 12;
570*4d671364SKiyoung Kim     if (!GetExifData(exif_offset, stream, preview_image_data)) {
571*4d671364SKiyoung Kim       return kFail;
572*4d671364SKiyoung Kim     }
573*4d671364SKiyoung Kim   }
574*4d671364SKiyoung Kim 
575*4d671364SKiyoung Kim   // Merge the Exif data with the RAW data to form the preview_image_data.
576*4d671364SKiyoung Kim   preview_image_data->thumbnail.offset += 160;  // Skip the cfa header.
577*4d671364SKiyoung Kim   preview_image_data->preview.offset = preview_offset;
578*4d671364SKiyoung Kim   preview_image_data->preview.length = preview_length;
579*4d671364SKiyoung Kim   return kOk;
580*4d671364SKiyoung Kim }
581*4d671364SKiyoung Kim 
Rw2GetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)582*4d671364SKiyoung Kim Error Rw2GetPreviewData(StreamInterface* stream,
583*4d671364SKiyoung Kim                         PreviewImageData* preview_image_data) {
584*4d671364SKiyoung Kim   const TagSet extended_tags = {kPanaTagTopBorder,     kPanaTagLeftBorder,
585*4d671364SKiyoung Kim                                 kPanaTagBottomBorder,  kPanaTagRightBorder,
586*4d671364SKiyoung Kim                                 kPanaTagIso,           kPanaTagJpegImage,
587*4d671364SKiyoung Kim                                 kTiffTagJpegByteCount, kTiffTagJpegOffset};
588*4d671364SKiyoung Kim   // Parse the RAW data to get the ISO, offset and length of the preview image,
589*4d671364SKiyoung Kim   // which contains the Exif information.
590*4d671364SKiyoung Kim   const std::uint32_t kNumberOfIfds = 1;
591*4d671364SKiyoung Kim   PreviewImageData preview_data;
592*4d671364SKiyoung Kim   if (!GetPreviewData(extended_tags, kNumberOfIfds, stream, &preview_data)) {
593*4d671364SKiyoung Kim     return kFail;
594*4d671364SKiyoung Kim   }
595*4d671364SKiyoung Kim 
596*4d671364SKiyoung Kim   if (preview_data.preview.length > 0) {  // when preview image exists
597*4d671364SKiyoung Kim     // Parse the Exif information from the preview image.
598*4d671364SKiyoung Kim     const std::uint32_t exif_offset = preview_data.preview.offset + 12;
599*4d671364SKiyoung Kim     if (!GetExifData(exif_offset, stream, preview_image_data)) {
600*4d671364SKiyoung Kim       return kFail;
601*4d671364SKiyoung Kim     }
602*4d671364SKiyoung Kim     preview_image_data->thumbnail.offset += exif_offset;
603*4d671364SKiyoung Kim   }
604*4d671364SKiyoung Kim 
605*4d671364SKiyoung Kim   // Merge the Exif data with the RAW data to form the preview_image_data.
606*4d671364SKiyoung Kim   preview_image_data->preview = preview_data.preview;
607*4d671364SKiyoung Kim   preview_image_data->iso = preview_data.iso;
608*4d671364SKiyoung Kim   preview_image_data->full_width = preview_data.full_width;
609*4d671364SKiyoung Kim   preview_image_data->full_height = preview_data.full_height;
610*4d671364SKiyoung Kim 
611*4d671364SKiyoung Kim   return kOk;
612*4d671364SKiyoung Kim }
613*4d671364SKiyoung Kim 
SrwGetPreviewData(StreamInterface * stream,PreviewImageData * preview_image_data)614*4d671364SKiyoung Kim Error SrwGetPreviewData(StreamInterface* stream,
615*4d671364SKiyoung Kim                         PreviewImageData* preview_image_data) {
616*4d671364SKiyoung Kim   GetThumbnailOffsetAndLength({kTiffTagSubIfd}, stream, preview_image_data);
617*4d671364SKiyoung Kim 
618*4d671364SKiyoung Kim   const TagSet extended_tags = {kExifTagWidth, kExifTagHeight,
619*4d671364SKiyoung Kim                                 kTiffTagJpegByteCount, kTiffTagJpegOffset,
620*4d671364SKiyoung Kim                                 kTiffTagSubIfd};
621*4d671364SKiyoung Kim   const std::uint32_t kNumberOfIfds = 1;
622*4d671364SKiyoung Kim   if (!GetPreviewData(extended_tags, kNumberOfIfds, stream,
623*4d671364SKiyoung Kim                       preview_image_data)) {
624*4d671364SKiyoung Kim     return kFail;
625*4d671364SKiyoung Kim   }
626*4d671364SKiyoung Kim   return kOk;
627*4d671364SKiyoung Kim }
628*4d671364SKiyoung Kim 
629*4d671364SKiyoung Kim }  // namespace
630*4d671364SKiyoung Kim 
BytesRequiredForIsRaw()631*4d671364SKiyoung Kim size_t BytesRequiredForIsRaw() {
632*4d671364SKiyoung Kim   return image_type_recognition::GetNumberOfBytesForIsRawLite();
633*4d671364SKiyoung Kim }
634*4d671364SKiyoung Kim 
IsRaw(StreamInterface * data)635*4d671364SKiyoung Kim bool IsRaw(StreamInterface* data) {
636*4d671364SKiyoung Kim   const size_t bytes = BytesRequiredForIsRaw();
637*4d671364SKiyoung Kim   if (data == nullptr) {
638*4d671364SKiyoung Kim     return false;
639*4d671364SKiyoung Kim   }
640*4d671364SKiyoung Kim 
641*4d671364SKiyoung Kim   // Read required number of bytes into a vector.
642*4d671364SKiyoung Kim   std::vector<std::uint8_t> file_header(bytes);
643*4d671364SKiyoung Kim   if (data->GetData(0, file_header.size(), file_header.data()) != kOk) {
644*4d671364SKiyoung Kim     return false;
645*4d671364SKiyoung Kim   }
646*4d671364SKiyoung Kim 
647*4d671364SKiyoung Kim   RangeCheckedBytePtr data_buffer(file_header.data(), file_header.size());
648*4d671364SKiyoung Kim 
649*4d671364SKiyoung Kim   return image_type_recognition::IsRawLite(data_buffer);
650*4d671364SKiyoung Kim }
651*4d671364SKiyoung Kim 
GetPreviewImageData(StreamInterface * data,PreviewImageData * preview_image_data,RawImageTypes * output_type)652*4d671364SKiyoung Kim Error GetPreviewImageData(StreamInterface* data,
653*4d671364SKiyoung Kim                           PreviewImageData* preview_image_data,
654*4d671364SKiyoung Kim                           RawImageTypes* output_type) {
655*4d671364SKiyoung Kim   const size_t bytes = BytesRequiredForIsRaw();
656*4d671364SKiyoung Kim   if (data == nullptr || bytes == 0) {
657*4d671364SKiyoung Kim     return kFail;
658*4d671364SKiyoung Kim   }
659*4d671364SKiyoung Kim 
660*4d671364SKiyoung Kim   std::vector<std::uint8_t> file_header(bytes);
661*4d671364SKiyoung Kim   Error error = data->GetData(0, file_header.size(), file_header.data());
662*4d671364SKiyoung Kim   if (error != kOk) {
663*4d671364SKiyoung Kim     return error;
664*4d671364SKiyoung Kim   }
665*4d671364SKiyoung Kim   RangeCheckedBytePtr header_buffer(file_header.data(), file_header.size());
666*4d671364SKiyoung Kim 
667*4d671364SKiyoung Kim   RawImageTypes type = RecognizeRawImageTypeLite(header_buffer);
668*4d671364SKiyoung Kim   if (output_type != nullptr) *output_type = type;
669*4d671364SKiyoung Kim   switch (type) {
670*4d671364SKiyoung Kim     case image_type_recognition::kArwImage:
671*4d671364SKiyoung Kim       return ArwGetPreviewData(data, preview_image_data);
672*4d671364SKiyoung Kim     case image_type_recognition::kCr2Image:
673*4d671364SKiyoung Kim       return Cr2GetPreviewData(data, preview_image_data);
674*4d671364SKiyoung Kim     case image_type_recognition::kCr3Image:
675*4d671364SKiyoung Kim       return Cr3GetPreviewData(data, preview_image_data);
676*4d671364SKiyoung Kim     case image_type_recognition::kDngImage:
677*4d671364SKiyoung Kim       return DngGetPreviewData(data, preview_image_data);
678*4d671364SKiyoung Kim     case image_type_recognition::kNefImage:
679*4d671364SKiyoung Kim     case image_type_recognition::kNrwImage:
680*4d671364SKiyoung Kim       return NefGetPreviewData(data, preview_image_data);
681*4d671364SKiyoung Kim     case image_type_recognition::kOrfImage:
682*4d671364SKiyoung Kim       return OrfGetPreviewData(data, preview_image_data);
683*4d671364SKiyoung Kim     case image_type_recognition::kPefImage:
684*4d671364SKiyoung Kim       return PefGetPreviewData(data, preview_image_data);
685*4d671364SKiyoung Kim     case image_type_recognition::kRafImage:
686*4d671364SKiyoung Kim       return RafGetPreviewData(data, preview_image_data);
687*4d671364SKiyoung Kim     case image_type_recognition::kRw2Image:
688*4d671364SKiyoung Kim       return Rw2GetPreviewData(data, preview_image_data);
689*4d671364SKiyoung Kim     case image_type_recognition::kSrwImage:
690*4d671364SKiyoung Kim       return SrwGetPreviewData(data, preview_image_data);
691*4d671364SKiyoung Kim     default:
692*4d671364SKiyoung Kim       return kUnsupported;
693*4d671364SKiyoung Kim   }
694*4d671364SKiyoung Kim }
695*4d671364SKiyoung Kim 
GetDngInformation(StreamInterface * data,std::uint32_t * width,std::uint32_t * height,std::vector<std::uint32_t> * cfa_pattern_dim)696*4d671364SKiyoung Kim bool GetDngInformation(StreamInterface* data, std::uint32_t* width,
697*4d671364SKiyoung Kim                        std::uint32_t* height,
698*4d671364SKiyoung Kim                        std::vector<std::uint32_t>* cfa_pattern_dim) {
699*4d671364SKiyoung Kim   // If IFD0 contains already the full dimensions we do not parse into the sub
700*4d671364SKiyoung Kim   // IFD.
701*4d671364SKiyoung Kim   if (!GetDngInformation({}, data, width, height, cfa_pattern_dim)) {
702*4d671364SKiyoung Kim     return GetDngInformation({kTiffTagSubIfd}, data, width, height,
703*4d671364SKiyoung Kim                              cfa_pattern_dim);
704*4d671364SKiyoung Kim   }
705*4d671364SKiyoung Kim   return true;
706*4d671364SKiyoung Kim }
707*4d671364SKiyoung Kim 
GetOrientation(StreamInterface * data,std::uint32_t * orientation)708*4d671364SKiyoung Kim bool GetOrientation(StreamInterface* data, std::uint32_t* orientation) {
709*4d671364SKiyoung Kim   using image_type_recognition::GetNumberOfBytesForIsOfType;
710*4d671364SKiyoung Kim   using image_type_recognition::IsOfType;
711*4d671364SKiyoung Kim 
712*4d671364SKiyoung Kim   size_t min_header_bytes =
713*4d671364SKiyoung Kim       std::max(GetNumberOfBytesForIsOfType(image_type_recognition::kRafImage),
714*4d671364SKiyoung Kim                GetNumberOfBytesForIsOfType(image_type_recognition::kCr3Image));
715*4d671364SKiyoung Kim 
716*4d671364SKiyoung Kim   std::vector<std::uint8_t> file_header(min_header_bytes);
717*4d671364SKiyoung Kim   if (data->GetData(0, file_header.size(), file_header.data()) != kOk) {
718*4d671364SKiyoung Kim     return false;
719*4d671364SKiyoung Kim   }
720*4d671364SKiyoung Kim 
721*4d671364SKiyoung Kim   // For RAF and CR# files a special routine is necessary to get orientation.
722*4d671364SKiyoung Kim   // For others the general approach is sufficient.
723*4d671364SKiyoung Kim   if (IsOfType(RangeCheckedBytePtr(file_header.data(), file_header.size()),
724*4d671364SKiyoung Kim                image_type_recognition::kRafImage)) {
725*4d671364SKiyoung Kim     return RafGetOrientation(data, orientation);
726*4d671364SKiyoung Kim   } else if (IsOfType(
727*4d671364SKiyoung Kim                  RangeCheckedBytePtr(file_header.data(), file_header.size()),
728*4d671364SKiyoung Kim                  image_type_recognition::kCr3Image)) {
729*4d671364SKiyoung Kim     return Cr3GetOrientation(data, orientation);
730*4d671364SKiyoung Kim   } else {
731*4d671364SKiyoung Kim     return GetExifOrientation(data, 0 /* offset */, orientation);
732*4d671364SKiyoung Kim   }
733*4d671364SKiyoung Kim }
734*4d671364SKiyoung Kim 
SupportedExtensions()735*4d671364SKiyoung Kim std::vector<std::string> SupportedExtensions() {
736*4d671364SKiyoung Kim   return {"ARW", "CR2", "CR3", "DNG", "NEF", "NRW",
737*4d671364SKiyoung Kim           "ORF", "PEF", "RAF", "RW2", "SRW"};
738*4d671364SKiyoung Kim }
739*4d671364SKiyoung Kim 
740*4d671364SKiyoung Kim }  // namespace piex
741