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 = 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 464Error 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 513Error 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 523Error 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 542Error 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 574Error 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 606Error 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 623size_t BytesRequiredForIsRaw() { 624 return image_type_recognition::GetNumberOfBytesForIsRawLite(); 625} 626 627bool 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 644Error 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 683bool 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 695bool 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 715std::vector<std::string> SupportedExtensions() { 716 return {"ARW", "CR2", "DNG", "NEF", "NRW", "ORF", "PEF", "RAF", "RW2", "SRW"}; 717} 718 719} // namespace piex 720