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