1 /* 2 * Copyright 2016 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkCodec.h" 9 #include "SkCodecPriv.h" 10 #include "SkColorSpacePriv.h" 11 #include "SkColorData.h" 12 #include "SkData.h" 13 #include "SkJpegCodec.h" 14 #include "SkMakeUnique.h" 15 #include "SkMutex.h" 16 #include "SkRawCodec.h" 17 #include "SkRefCnt.h" 18 #include "SkStream.h" 19 #include "SkStreamPriv.h" 20 #include "SkTArray.h" 21 #include "SkTaskGroup.h" 22 #include "SkTemplates.h" 23 #include "SkTypes.h" 24 25 #include "dng_area_task.h" 26 #include "dng_color_space.h" 27 #include "dng_errors.h" 28 #include "dng_exceptions.h" 29 #include "dng_host.h" 30 #include "dng_info.h" 31 #include "dng_memory.h" 32 #include "dng_render.h" 33 #include "dng_stream.h" 34 35 #include "src/piex.h" 36 37 #include <cmath> // for std::round,floor,ceil 38 #include <limits> 39 40 namespace { 41 42 // Caluclates the number of tiles of tile_size that fit into the area in vertical and horizontal 43 // directions. 44 dng_point num_tiles_in_area(const dng_point &areaSize, 45 const dng_point_real64 &tileSize) { 46 // FIXME: Add a ceil_div() helper in SkCodecPriv.h 47 return dng_point(static_cast<int32>((areaSize.v + tileSize.v - 1) / tileSize.v), 48 static_cast<int32>((areaSize.h + tileSize.h - 1) / tileSize.h)); 49 } 50 51 int num_tasks_required(const dng_point& tilesInTask, 52 const dng_point& tilesInArea) { 53 return ((tilesInArea.v + tilesInTask.v - 1) / tilesInTask.v) * 54 ((tilesInArea.h + tilesInTask.h - 1) / tilesInTask.h); 55 } 56 57 // Calculate the number of tiles to process per task, taking into account the maximum number of 58 // tasks. It prefers to increase horizontally for better locality of reference. 59 dng_point num_tiles_per_task(const int maxTasks, 60 const dng_point &tilesInArea) { 61 dng_point tilesInTask = {1, 1}; 62 while (num_tasks_required(tilesInTask, tilesInArea) > maxTasks) { 63 if (tilesInTask.h < tilesInArea.h) { 64 ++tilesInTask.h; 65 } else if (tilesInTask.v < tilesInArea.v) { 66 ++tilesInTask.v; 67 } else { 68 ThrowProgramError("num_tiles_per_task calculation is wrong."); 69 } 70 } 71 return tilesInTask; 72 } 73 74 std::vector<dng_rect> compute_task_areas(const int maxTasks, const dng_rect& area, 75 const dng_point& tileSize) { 76 std::vector<dng_rect> taskAreas; 77 const dng_point tilesInArea = num_tiles_in_area(area.Size(), tileSize); 78 const dng_point tilesPerTask = num_tiles_per_task(maxTasks, tilesInArea); 79 const dng_point taskAreaSize = {tilesPerTask.v * tileSize.v, 80 tilesPerTask.h * tileSize.h}; 81 for (int v = 0; v < tilesInArea.v; v += tilesPerTask.v) { 82 for (int h = 0; h < tilesInArea.h; h += tilesPerTask.h) { 83 dng_rect taskArea; 84 taskArea.t = area.t + v * tileSize.v; 85 taskArea.l = area.l + h * tileSize.h; 86 taskArea.b = Min_int32(taskArea.t + taskAreaSize.v, area.b); 87 taskArea.r = Min_int32(taskArea.l + taskAreaSize.h, area.r); 88 89 taskAreas.push_back(taskArea); 90 } 91 } 92 return taskAreas; 93 } 94 95 class SkDngHost : public dng_host { 96 public: 97 explicit SkDngHost(dng_memory_allocator* allocater) : dng_host(allocater) {} 98 99 void PerformAreaTask(dng_area_task& task, const dng_rect& area) override { 100 SkTaskGroup taskGroup; 101 102 // tileSize is typically 256x256 103 const dng_point tileSize(task.FindTileSize(area)); 104 const std::vector<dng_rect> taskAreas = compute_task_areas(this->PerformAreaTaskThreads(), 105 area, tileSize); 106 const int numTasks = static_cast<int>(taskAreas.size()); 107 108 SkMutex mutex; 109 SkTArray<dng_exception> exceptions; 110 task.Start(numTasks, tileSize, &Allocator(), Sniffer()); 111 for (int taskIndex = 0; taskIndex < numTasks; ++taskIndex) { 112 taskGroup.add([&mutex, &exceptions, &task, this, taskIndex, taskAreas, tileSize] { 113 try { 114 task.ProcessOnThread(taskIndex, taskAreas[taskIndex], tileSize, this->Sniffer()); 115 } catch (dng_exception& exception) { 116 SkAutoMutexAcquire lock(mutex); 117 exceptions.push_back(exception); 118 } catch (...) { 119 SkAutoMutexAcquire lock(mutex); 120 exceptions.push_back(dng_exception(dng_error_unknown)); 121 } 122 }); 123 } 124 125 taskGroup.wait(); 126 task.Finish(numTasks); 127 128 // We only re-throw the first exception. 129 if (!exceptions.empty()) { 130 Throw_dng_error(exceptions.front().ErrorCode(), nullptr, nullptr); 131 } 132 } 133 134 uint32 PerformAreaTaskThreads() override { 135 #ifdef SK_BUILD_FOR_ANDROID 136 // Only use 1 thread. DNGs with the warp effect require a lot of memory, 137 // and the amount of memory required scales linearly with the number of 138 // threads. The sample used in CTS requires over 500 MB, so even two 139 // threads is significantly expensive. There is no good way to tell 140 // whether the image has the warp effect. 141 return 1; 142 #else 143 return kMaxMPThreads; 144 #endif 145 } 146 147 private: 148 typedef dng_host INHERITED; 149 }; 150 151 // T must be unsigned type. 152 template <class T> 153 bool safe_add_to_size_t(T arg1, T arg2, size_t* result) { 154 SkASSERT(arg1 >= 0); 155 SkASSERT(arg2 >= 0); 156 if (arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) { 157 T sum = arg1 + arg2; 158 if (sum <= std::numeric_limits<size_t>::max()) { 159 *result = static_cast<size_t>(sum); 160 return true; 161 } 162 } 163 return false; 164 } 165 166 bool is_asset_stream(const SkStream& stream) { 167 return stream.hasLength() && stream.hasPosition(); 168 } 169 170 } // namespace 171 172 class SkRawStream { 173 public: 174 virtual ~SkRawStream() {} 175 176 /* 177 * Gets the length of the stream. Depending on the type of stream, this may require reading to 178 * the end of the stream. 179 */ 180 virtual uint64 getLength() = 0; 181 182 virtual bool read(void* data, size_t offset, size_t length) = 0; 183 184 /* 185 * Creates an SkMemoryStream from the offset with size. 186 * Note: for performance reason, this function is destructive to the SkRawStream. One should 187 * abandon current object after the function call. 188 */ 189 virtual std::unique_ptr<SkMemoryStream> transferBuffer(size_t offset, size_t size) = 0; 190 }; 191 192 class SkRawLimitedDynamicMemoryWStream : public SkDynamicMemoryWStream { 193 public: 194 ~SkRawLimitedDynamicMemoryWStream() override {} 195 196 bool write(const void* buffer, size_t size) override { 197 size_t newSize; 198 if (!safe_add_to_size_t(this->bytesWritten(), size, &newSize) || 199 newSize > kMaxStreamSize) 200 { 201 SkCodecPrintf("Error: Stream size exceeds the limit.\n"); 202 return false; 203 } 204 return this->INHERITED::write(buffer, size); 205 } 206 207 private: 208 // Most of valid RAW images will not be larger than 100MB. This limit is helpful to avoid 209 // streaming too large data chunk. We can always adjust the limit here if we need. 210 const size_t kMaxStreamSize = 100 * 1024 * 1024; // 100MB 211 212 typedef SkDynamicMemoryWStream INHERITED; 213 }; 214 215 // Note: the maximum buffer size is 100MB (limited by SkRawLimitedDynamicMemoryWStream). 216 class SkRawBufferedStream : public SkRawStream { 217 public: 218 explicit SkRawBufferedStream(std::unique_ptr<SkStream> stream) 219 : fStream(std::move(stream)) 220 , fWholeStreamRead(false) 221 { 222 // Only use SkRawBufferedStream when the stream is not an asset stream. 223 SkASSERT(!is_asset_stream(*fStream)); 224 } 225 226 ~SkRawBufferedStream() override {} 227 228 uint64 getLength() override { 229 if (!this->bufferMoreData(kReadToEnd)) { // read whole stream 230 ThrowReadFile(); 231 } 232 return fStreamBuffer.bytesWritten(); 233 } 234 235 bool read(void* data, size_t offset, size_t length) override { 236 if (length == 0) { 237 return true; 238 } 239 240 size_t sum; 241 if (!safe_add_to_size_t(offset, length, &sum)) { 242 return false; 243 } 244 245 return this->bufferMoreData(sum) && fStreamBuffer.read(data, offset, length); 246 } 247 248 std::unique_ptr<SkMemoryStream> transferBuffer(size_t offset, size_t size) override { 249 sk_sp<SkData> data(SkData::MakeUninitialized(size)); 250 if (offset > fStreamBuffer.bytesWritten()) { 251 // If the offset is not buffered, read from fStream directly and skip the buffering. 252 const size_t skipLength = offset - fStreamBuffer.bytesWritten(); 253 if (fStream->skip(skipLength) != skipLength) { 254 return nullptr; 255 } 256 const size_t bytesRead = fStream->read(data->writable_data(), size); 257 if (bytesRead < size) { 258 data = SkData::MakeSubset(data.get(), 0, bytesRead); 259 } 260 } else { 261 const size_t alreadyBuffered = SkTMin(fStreamBuffer.bytesWritten() - offset, size); 262 if (alreadyBuffered > 0 && 263 !fStreamBuffer.read(data->writable_data(), offset, alreadyBuffered)) { 264 return nullptr; 265 } 266 267 const size_t remaining = size - alreadyBuffered; 268 if (remaining) { 269 auto* dst = static_cast<uint8_t*>(data->writable_data()) + alreadyBuffered; 270 const size_t bytesRead = fStream->read(dst, remaining); 271 size_t newSize; 272 if (bytesRead < remaining) { 273 if (!safe_add_to_size_t(alreadyBuffered, bytesRead, &newSize)) { 274 return nullptr; 275 } 276 data = SkData::MakeSubset(data.get(), 0, newSize); 277 } 278 } 279 } 280 return SkMemoryStream::Make(data); 281 } 282 283 private: 284 // Note: if the newSize == kReadToEnd (0), this function will read to the end of stream. 285 bool bufferMoreData(size_t newSize) { 286 if (newSize == kReadToEnd) { 287 if (fWholeStreamRead) { // already read-to-end. 288 return true; 289 } 290 291 // TODO: optimize for the special case when the input is SkMemoryStream. 292 return SkStreamCopy(&fStreamBuffer, fStream.get()); 293 } 294 295 if (newSize <= fStreamBuffer.bytesWritten()) { // already buffered to newSize 296 return true; 297 } 298 if (fWholeStreamRead) { // newSize is larger than the whole stream. 299 return false; 300 } 301 302 // Try to read at least 8192 bytes to avoid to many small reads. 303 const size_t kMinSizeToRead = 8192; 304 const size_t sizeRequested = newSize - fStreamBuffer.bytesWritten(); 305 const size_t sizeToRead = SkTMax(kMinSizeToRead, sizeRequested); 306 SkAutoSTMalloc<kMinSizeToRead, uint8> tempBuffer(sizeToRead); 307 const size_t bytesRead = fStream->read(tempBuffer.get(), sizeToRead); 308 if (bytesRead < sizeRequested) { 309 return false; 310 } 311 return fStreamBuffer.write(tempBuffer.get(), bytesRead); 312 } 313 314 std::unique_ptr<SkStream> fStream; 315 bool fWholeStreamRead; 316 317 // Use a size-limited stream to avoid holding too huge buffer. 318 SkRawLimitedDynamicMemoryWStream fStreamBuffer; 319 320 const size_t kReadToEnd = 0; 321 }; 322 323 class SkRawAssetStream : public SkRawStream { 324 public: 325 explicit SkRawAssetStream(std::unique_ptr<SkStream> stream) 326 : fStream(std::move(stream)) 327 { 328 // Only use SkRawAssetStream when the stream is an asset stream. 329 SkASSERT(is_asset_stream(*fStream)); 330 } 331 332 ~SkRawAssetStream() override {} 333 334 uint64 getLength() override { 335 return fStream->getLength(); 336 } 337 338 339 bool read(void* data, size_t offset, size_t length) override { 340 if (length == 0) { 341 return true; 342 } 343 344 size_t sum; 345 if (!safe_add_to_size_t(offset, length, &sum)) { 346 return false; 347 } 348 349 return fStream->seek(offset) && (fStream->read(data, length) == length); 350 } 351 352 std::unique_ptr<SkMemoryStream> transferBuffer(size_t offset, size_t size) override { 353 if (fStream->getLength() < offset) { 354 return nullptr; 355 } 356 357 size_t sum; 358 if (!safe_add_to_size_t(offset, size, &sum)) { 359 return nullptr; 360 } 361 362 // This will allow read less than the requested "size", because the JPEG codec wants to 363 // handle also a partial JPEG file. 364 const size_t bytesToRead = SkTMin(sum, fStream->getLength()) - offset; 365 if (bytesToRead == 0) { 366 return nullptr; 367 } 368 369 if (fStream->getMemoryBase()) { // directly copy if getMemoryBase() is available. 370 sk_sp<SkData> data(SkData::MakeWithCopy( 371 static_cast<const uint8_t*>(fStream->getMemoryBase()) + offset, bytesToRead)); 372 fStream.reset(); 373 return SkMemoryStream::Make(data); 374 } else { 375 sk_sp<SkData> data(SkData::MakeUninitialized(bytesToRead)); 376 if (!fStream->seek(offset)) { 377 return nullptr; 378 } 379 const size_t bytesRead = fStream->read(data->writable_data(), bytesToRead); 380 if (bytesRead < bytesToRead) { 381 data = SkData::MakeSubset(data.get(), 0, bytesRead); 382 } 383 return SkMemoryStream::Make(data); 384 } 385 } 386 private: 387 std::unique_ptr<SkStream> fStream; 388 }; 389 390 class SkPiexStream : public ::piex::StreamInterface { 391 public: 392 // Will NOT take the ownership of the stream. 393 explicit SkPiexStream(SkRawStream* stream) : fStream(stream) {} 394 395 ~SkPiexStream() override {} 396 397 ::piex::Error GetData(const size_t offset, const size_t length, 398 uint8* data) override { 399 return fStream->read(static_cast<void*>(data), offset, length) ? 400 ::piex::Error::kOk : ::piex::Error::kFail; 401 } 402 403 private: 404 SkRawStream* fStream; 405 }; 406 407 class SkDngStream : public dng_stream { 408 public: 409 // Will NOT take the ownership of the stream. 410 SkDngStream(SkRawStream* stream) : fStream(stream) {} 411 412 ~SkDngStream() override {} 413 414 uint64 DoGetLength() override { return fStream->getLength(); } 415 416 void DoRead(void* data, uint32 count, uint64 offset) override { 417 size_t sum; 418 if (!safe_add_to_size_t(static_cast<uint64>(count), offset, &sum) || 419 !fStream->read(data, static_cast<size_t>(offset), static_cast<size_t>(count))) { 420 ThrowReadFile(); 421 } 422 } 423 424 private: 425 SkRawStream* fStream; 426 }; 427 428 class SkDngImage { 429 public: 430 /* 431 * Initializes the object with the information from Piex in a first attempt. This way it can 432 * save time and storage to obtain the DNG dimensions and color filter array (CFA) pattern 433 * which is essential for the demosaicing of the sensor image. 434 * Note: this will take the ownership of the stream. 435 */ 436 static SkDngImage* NewFromStream(SkRawStream* stream) { 437 std::unique_ptr<SkDngImage> dngImage(new SkDngImage(stream)); 438 #if defined(IS_FUZZING_WITH_LIBFUZZER) 439 // Libfuzzer easily runs out of memory after here. To avoid that 440 // We just pretend all streams are invalid. Our AFL-fuzzer 441 // should still exercise this code; it's more resistant to OOM. 442 return nullptr; 443 #endif 444 if (!dngImage->initFromPiex() && !dngImage->readDng()) { 445 return nullptr; 446 } 447 448 return dngImage.release(); 449 } 450 451 /* 452 * Renders the DNG image to the size. The DNG SDK only allows scaling close to integer factors 453 * down to 80 pixels on the short edge. The rendered image will be close to the specified size, 454 * but there is no guarantee that any of the edges will match the requested size. E.g. 455 * 100% size: 4000 x 3000 456 * requested size: 1600 x 1200 457 * returned size could be: 2000 x 1500 458 */ 459 dng_image* render(int width, int height) { 460 if (!fHost || !fInfo || !fNegative || !fDngStream) { 461 if (!this->readDng()) { 462 return nullptr; 463 } 464 } 465 466 // DNG SDK preserves the aspect ratio, so it only needs to know the longer dimension. 467 const int preferredSize = SkTMax(width, height); 468 try { 469 // render() takes ownership of fHost, fInfo, fNegative and fDngStream when available. 470 std::unique_ptr<dng_host> host(fHost.release()); 471 std::unique_ptr<dng_info> info(fInfo.release()); 472 std::unique_ptr<dng_negative> negative(fNegative.release()); 473 std::unique_ptr<dng_stream> dngStream(fDngStream.release()); 474 475 host->SetPreferredSize(preferredSize); 476 host->ValidateSizes(); 477 478 negative->ReadStage1Image(*host, *dngStream, *info); 479 480 if (info->fMaskIndex != -1) { 481 negative->ReadTransparencyMask(*host, *dngStream, *info); 482 } 483 484 negative->ValidateRawImageDigest(*host); 485 if (negative->IsDamaged()) { 486 return nullptr; 487 } 488 489 const int32 kMosaicPlane = -1; 490 negative->BuildStage2Image(*host); 491 negative->BuildStage3Image(*host, kMosaicPlane); 492 493 dng_render render(*host, *negative); 494 render.SetFinalSpace(dng_space_sRGB::Get()); 495 render.SetFinalPixelType(ttByte); 496 497 dng_point stage3_size = negative->Stage3Image()->Size(); 498 render.SetMaximumSize(SkTMax(stage3_size.h, stage3_size.v)); 499 500 return render.Render(); 501 } catch (...) { 502 return nullptr; 503 } 504 } 505 506 int width() const { 507 return fWidth; 508 } 509 510 int height() const { 511 return fHeight; 512 } 513 514 bool isScalable() const { 515 return fIsScalable; 516 } 517 518 bool isXtransImage() const { 519 return fIsXtransImage; 520 } 521 522 // Quick check if the image contains a valid TIFF header as requested by DNG format. 523 // Does not affect ownership of stream. 524 static bool IsTiffHeaderValid(SkRawStream* stream) { 525 const size_t kHeaderSize = 4; 526 unsigned char header[kHeaderSize]; 527 if (!stream->read(header, 0 /* offset */, kHeaderSize)) { 528 return false; 529 } 530 531 // Check if the header is valid (endian info and magic number "42"). 532 bool littleEndian; 533 if (!is_valid_endian_marker(header, &littleEndian)) { 534 return false; 535 } 536 537 return 0x2A == get_endian_short(header + 2, littleEndian); 538 } 539 540 private: 541 bool init(int width, int height, const dng_point& cfaPatternSize) { 542 fWidth = width; 543 fHeight = height; 544 545 // The DNG SDK scales only during demosaicing, so scaling is only possible when 546 // a mosaic info is available. 547 fIsScalable = cfaPatternSize.v != 0 && cfaPatternSize.h != 0; 548 fIsXtransImage = fIsScalable ? (cfaPatternSize.v == 6 && cfaPatternSize.h == 6) : false; 549 550 return width > 0 && height > 0; 551 } 552 553 bool initFromPiex() { 554 // Does not take the ownership of rawStream. 555 SkPiexStream piexStream(fStream.get()); 556 ::piex::PreviewImageData imageData; 557 if (::piex::IsRaw(&piexStream) 558 && ::piex::GetPreviewImageData(&piexStream, &imageData) == ::piex::Error::kOk) 559 { 560 dng_point cfaPatternSize(imageData.cfa_pattern_dim[1], imageData.cfa_pattern_dim[0]); 561 return this->init(static_cast<int>(imageData.full_width), 562 static_cast<int>(imageData.full_height), cfaPatternSize); 563 } 564 return false; 565 } 566 567 bool readDng() { 568 try { 569 // Due to the limit of DNG SDK, we need to reset host and info. 570 fHost.reset(new SkDngHost(&fAllocator)); 571 fInfo.reset(new dng_info); 572 fDngStream.reset(new SkDngStream(fStream.get())); 573 574 fHost->ValidateSizes(); 575 fInfo->Parse(*fHost, *fDngStream); 576 fInfo->PostParse(*fHost); 577 if (!fInfo->IsValidDNG()) { 578 return false; 579 } 580 581 fNegative.reset(fHost->Make_dng_negative()); 582 fNegative->Parse(*fHost, *fDngStream, *fInfo); 583 fNegative->PostParse(*fHost, *fDngStream, *fInfo); 584 fNegative->SynchronizeMetadata(); 585 586 dng_point cfaPatternSize(0, 0); 587 if (fNegative->GetMosaicInfo() != nullptr) { 588 cfaPatternSize = fNegative->GetMosaicInfo()->fCFAPatternSize; 589 } 590 return this->init(static_cast<int>(fNegative->DefaultCropSizeH().As_real64()), 591 static_cast<int>(fNegative->DefaultCropSizeV().As_real64()), 592 cfaPatternSize); 593 } catch (...) { 594 return false; 595 } 596 } 597 598 SkDngImage(SkRawStream* stream) 599 : fStream(stream) 600 {} 601 602 dng_memory_allocator fAllocator; 603 std::unique_ptr<SkRawStream> fStream; 604 std::unique_ptr<dng_host> fHost; 605 std::unique_ptr<dng_info> fInfo; 606 std::unique_ptr<dng_negative> fNegative; 607 std::unique_ptr<dng_stream> fDngStream; 608 609 int fWidth; 610 int fHeight; 611 bool fIsScalable; 612 bool fIsXtransImage; 613 }; 614 615 /* 616 * Tries to handle the image with PIEX. If PIEX returns kOk and finds the preview image, create a 617 * SkJpegCodec. If PIEX returns kFail, then the file is invalid, return nullptr. In other cases, 618 * fallback to create SkRawCodec for DNG images. 619 */ 620 std::unique_ptr<SkCodec> SkRawCodec::MakeFromStream(std::unique_ptr<SkStream> stream, 621 Result* result) { 622 std::unique_ptr<SkRawStream> rawStream; 623 if (is_asset_stream(*stream)) { 624 rawStream.reset(new SkRawAssetStream(std::move(stream))); 625 } else { 626 rawStream.reset(new SkRawBufferedStream(std::move(stream))); 627 } 628 629 // Does not take the ownership of rawStream. 630 SkPiexStream piexStream(rawStream.get()); 631 ::piex::PreviewImageData imageData; 632 if (::piex::IsRaw(&piexStream)) { 633 ::piex::Error error = ::piex::GetPreviewImageData(&piexStream, &imageData); 634 if (error == ::piex::Error::kFail) { 635 *result = kInvalidInput; 636 return nullptr; 637 } 638 639 std::unique_ptr<SkEncodedInfo::ICCProfile> profile; 640 if (imageData.color_space == ::piex::PreviewImageData::kAdobeRgb) { 641 skcms_ICCProfile skcmsProfile; 642 skcms_Init(&skcmsProfile); 643 skcms_SetTransferFunction(&skcmsProfile, &SkNamedTransferFn::k2Dot2); 644 skcms_SetXYZD50(&skcmsProfile, &SkNamedGamut::kAdobeRGB); 645 profile = SkEncodedInfo::ICCProfile::Make(skcmsProfile); 646 } 647 648 // Theoretically PIEX can return JPEG compressed image or uncompressed RGB image. We only 649 // handle the JPEG compressed preview image here. 650 if (error == ::piex::Error::kOk && imageData.preview.length > 0 && 651 imageData.preview.format == ::piex::Image::kJpegCompressed) 652 { 653 // transferBuffer() is destructive to the rawStream. Abandon the rawStream after this 654 // function call. 655 // FIXME: one may avoid the copy of memoryStream and use the buffered rawStream. 656 auto memoryStream = rawStream->transferBuffer(imageData.preview.offset, 657 imageData.preview.length); 658 if (!memoryStream) { 659 *result = kInvalidInput; 660 return nullptr; 661 } 662 return SkJpegCodec::MakeFromStream(std::move(memoryStream), result, 663 std::move(profile)); 664 } 665 } 666 667 if (!SkDngImage::IsTiffHeaderValid(rawStream.get())) { 668 *result = kUnimplemented; 669 return nullptr; 670 } 671 672 // Takes the ownership of the rawStream. 673 std::unique_ptr<SkDngImage> dngImage(SkDngImage::NewFromStream(rawStream.release())); 674 if (!dngImage) { 675 *result = kInvalidInput; 676 return nullptr; 677 } 678 679 *result = kSuccess; 680 return std::unique_ptr<SkCodec>(new SkRawCodec(dngImage.release())); 681 } 682 683 SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, 684 size_t dstRowBytes, const Options& options, 685 int* rowsDecoded) { 686 const int width = dstInfo.width(); 687 const int height = dstInfo.height(); 688 std::unique_ptr<dng_image> image(fDngImage->render(width, height)); 689 if (!image) { 690 return kInvalidInput; 691 } 692 693 // Because the DNG SDK can not guarantee to render to requested size, we allow a small 694 // difference. Only the overlapping region will be converted. 695 const float maxDiffRatio = 1.03f; 696 const dng_point& imageSize = image->Size(); 697 if (imageSize.h / (float) width > maxDiffRatio || imageSize.h < width || 698 imageSize.v / (float) height > maxDiffRatio || imageSize.v < height) { 699 return SkCodec::kInvalidScale; 700 } 701 702 void* dstRow = dst; 703 SkAutoTMalloc<uint8_t> srcRow(width * 3); 704 705 dng_pixel_buffer buffer; 706 buffer.fData = &srcRow[0]; 707 buffer.fPlane = 0; 708 buffer.fPlanes = 3; 709 buffer.fColStep = buffer.fPlanes; 710 buffer.fPlaneStep = 1; 711 buffer.fPixelType = ttByte; 712 buffer.fPixelSize = sizeof(uint8_t); 713 buffer.fRowStep = width * 3; 714 715 constexpr auto srcFormat = skcms_PixelFormat_RGB_888; 716 skcms_PixelFormat dstFormat; 717 if (!sk_select_xform_format(dstInfo.colorType(), false, &dstFormat)) { 718 return kInvalidConversion; 719 } 720 721 const skcms_ICCProfile* const srcProfile = this->getEncodedInfo().profile(); 722 skcms_ICCProfile dstProfileStorage; 723 const skcms_ICCProfile* dstProfile = nullptr; 724 if (auto cs = dstInfo.colorSpace()) { 725 cs->toProfile(&dstProfileStorage); 726 dstProfile = &dstProfileStorage; 727 } 728 729 for (int i = 0; i < height; ++i) { 730 buffer.fArea = dng_rect(i, 0, i + 1, width); 731 732 try { 733 image->Get(buffer, dng_image::edge_zero); 734 } catch (...) { 735 *rowsDecoded = i; 736 return kIncompleteInput; 737 } 738 739 if (!skcms_Transform(&srcRow[0], srcFormat, skcms_AlphaFormat_Unpremul, srcProfile, 740 dstRow, dstFormat, skcms_AlphaFormat_Unpremul, dstProfile, 741 dstInfo.width())) { 742 SkDebugf("failed to transform\n"); 743 *rowsDecoded = i; 744 return kInternalError; 745 } 746 747 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); 748 } 749 return kSuccess; 750 } 751 752 SkISize SkRawCodec::onGetScaledDimensions(float desiredScale) const { 753 SkASSERT(desiredScale <= 1.f); 754 755 const SkISize dim = this->dimensions(); 756 SkASSERT(dim.fWidth != 0 && dim.fHeight != 0); 757 758 if (!fDngImage->isScalable()) { 759 return dim; 760 } 761 762 // Limits the minimum size to be 80 on the short edge. 763 const float shortEdge = static_cast<float>(SkTMin(dim.fWidth, dim.fHeight)); 764 if (desiredScale < 80.f / shortEdge) { 765 desiredScale = 80.f / shortEdge; 766 } 767 768 // For Xtrans images, the integer-factor scaling does not support the half-size scaling case 769 // (stronger downscalings are fine). In this case, returns the factor "3" scaling instead. 770 if (fDngImage->isXtransImage() && desiredScale > 1.f / 3.f && desiredScale < 1.f) { 771 desiredScale = 1.f / 3.f; 772 } 773 774 // Round to integer-factors. 775 const float finalScale = std::floor(1.f/ desiredScale); 776 return SkISize::Make(static_cast<int32_t>(std::floor(dim.fWidth / finalScale)), 777 static_cast<int32_t>(std::floor(dim.fHeight / finalScale))); 778 } 779 780 bool SkRawCodec::onDimensionsSupported(const SkISize& dim) { 781 const SkISize fullDim = this->dimensions(); 782 const float fullShortEdge = static_cast<float>(SkTMin(fullDim.fWidth, fullDim.fHeight)); 783 const float shortEdge = static_cast<float>(SkTMin(dim.fWidth, dim.fHeight)); 784 785 SkISize sizeFloor = this->onGetScaledDimensions(1.f / std::floor(fullShortEdge / shortEdge)); 786 SkISize sizeCeil = this->onGetScaledDimensions(1.f / std::ceil(fullShortEdge / shortEdge)); 787 return sizeFloor == dim || sizeCeil == dim; 788 } 789 790 SkRawCodec::~SkRawCodec() {} 791 792 SkRawCodec::SkRawCodec(SkDngImage* dngImage) 793 : INHERITED(SkEncodedInfo::Make(dngImage->width(), dngImage->height(), 794 SkEncodedInfo::kRGB_Color, 795 SkEncodedInfo::kOpaque_Alpha, 8), 796 skcms_PixelFormat_RGBA_8888, nullptr) 797 , fDngImage(dngImage) {} 798