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