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