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