Flutter Engine
The Flutter Engine
SkWuffsCodec.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2018 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
16#include "include/core/SkData.h"
21#include "include/core/SkRect.h"
24#include "include/core/SkSize.h"
30#include "modules/skcms/skcms.h"
33#include "src/codec/SkSampler.h"
35#include "src/core/SkDraw.h"
38
39#include <climits>
40#include <cstdint>
41#include <cstring>
42#include <memory>
43#include <utility>
44#include <vector>
45
46// Documentation on the Wuffs language and standard library (in general) and
47// its image decoding API (in particular) is at:
48//
49// - https://github.com/google/wuffs/tree/master/doc
50// - https://github.com/google/wuffs/blob/master/doc/std/image-decoders.md
51
52// Wuffs ships as a "single file C library" or "header file library" as per
53// https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
54//
55// As we have not #define'd WUFFS_IMPLEMENTATION, the #include here is
56// including a header file, even though that file name ends in ".c".
57#if defined(WUFFS_IMPLEMENTATION)
58#error "SkWuffsCodec should not #define WUFFS_IMPLEMENTATION"
59#endif
60#include "wuffs-v0.3.c" // NO_G3_REWRITE
61// Commit count 2514 is Wuffs 0.3.0-alpha.4.
62#if WUFFS_VERSION_BUILD_METADATA_COMMIT_COUNT < 2514
63#error "Wuffs version is too old. Upgrade to the latest version."
64#endif
65
66#define SK_WUFFS_CODEC_BUFFER_SIZE 4096
67
68// Configuring a Skia build with
69// SK_WUFFS_FAVORS_PERFORMANCE_OVER_ADDITIONAL_MEMORY_SAFETY can improve decode
70// performance by some fixed amount (independent of the image size), which can
71// be a noticeable proportional improvement if the input is relatively small.
72//
73// The Wuffs library is still memory-safe either way, in that there are no
74// out-of-bounds reads or writes, and the library endeavours not to read
75// uninitialized memory. There are just fewer compiler-enforced guarantees
76// against reading uninitialized memory. For more detail, see
77// https://github.com/google/wuffs/blob/master/doc/note/initialization.md#partial-zero-initialization
78#if defined(SK_WUFFS_FAVORS_PERFORMANCE_OVER_ADDITIONAL_MEMORY_SAFETY)
79#define SK_WUFFS_INITIALIZE_FLAGS WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED
80#else
81#define SK_WUFFS_INITIALIZE_FLAGS WUFFS_INITIALIZE__DEFAULT_OPTIONS
82#endif
83
84static bool fill_buffer(wuffs_base__io_buffer* b, SkStream* s) {
85 b->compact();
86 size_t num_read = s->read(b->data.ptr + b->meta.wi, b->data.len - b->meta.wi);
87 b->meta.wi += num_read;
88 // We hard-code false instead of s->isAtEnd(). In theory, Skia's
89 // SkStream::isAtEnd() method has the same semantics as Wuffs'
90 // wuffs_base__io_buffer_meta::closed field. Specifically, both are false
91 // when reading from a network socket when all bytes *available right now*
92 // have been read but there might be more later.
93 //
94 // However, SkStream is designed around synchronous I/O. The SkStream::read
95 // method does not take a callback and, per its documentation comments, a
96 // read request for N bytes should block until a full N bytes are
97 // available. In practice, Blink's SkStream subclass builds on top of async
98 // I/O and cannot afford to block. While it satisfies "the letter of the
99 // law", in terms of what the C++ compiler needs, it does not satisfy "the
100 // spirit of the law". Its read() can return short without blocking and its
101 // isAtEnd() can return false positives.
102 //
103 // When closed is true, Wuffs treats incomplete input as a fatal error
104 // instead of a recoverable "short read" suspension. We therefore hard-code
105 // false and return kIncompleteInput (instead of kErrorInInput) up the call
106 // stack even if the SkStream isAtEnd. The caller usually has more context
107 // (more than what's in the SkStream) to differentiate the two, like this:
108 // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.cc;l=115;drc=277dcc4d810ae4c0286d8af96d270ed9b686c5ff
109 b->meta.closed = false;
110 return num_read > 0;
111}
112
113static bool seek_buffer(wuffs_base__io_buffer* b, SkStream* s, uint64_t pos) {
114 // Try to re-position the io_buffer's meta.ri read-index first, which is
115 // cheaper than seeking in the backing SkStream.
116 if ((pos >= b->meta.pos) && (pos - b->meta.pos <= b->meta.wi)) {
117 b->meta.ri = pos - b->meta.pos;
118 return true;
119 }
120 // Seek in the backing SkStream.
121 if ((pos > SIZE_MAX) || (!s->seek(pos))) {
122 return false;
123 }
124 b->meta.wi = 0;
125 b->meta.ri = 0;
126 b->meta.pos = pos;
127 b->meta.closed = false;
128 return true;
129}
130
132 wuffs_base__animation_disposal w) {
133 switch (w) {
134 case WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_BACKGROUND:
136 case WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_PREVIOUS:
138 default:
140 }
141}
142
143static SkAlphaType to_alpha_type(bool opaque) {
145}
146
147static SkCodec::Result reset_and_decode_image_config(wuffs_gif__decoder* decoder,
148 wuffs_base__image_config* imgcfg,
149 wuffs_base__io_buffer* b,
150 SkStream* s) {
151 // Calling decoder->initialize will memset most or all of it to zero,
152 // depending on SK_WUFFS_INITIALIZE_FLAGS.
153 wuffs_base__status status =
154 decoder->initialize(sizeof__wuffs_gif__decoder(), WUFFS_VERSION, SK_WUFFS_INITIALIZE_FLAGS);
155 if (status.repr != nullptr) {
156 SkCodecPrintf("initialize: %s", status.message());
158 }
159
160 // See https://bugs.chromium.org/p/skia/issues/detail?id=12055
161 decoder->set_quirk_enabled(WUFFS_GIF__QUIRK_IGNORE_TOO_MUCH_PIXEL_DATA, true);
162
163 while (true) {
164 status = decoder->decode_image_config(imgcfg, b);
165 if (status.repr == nullptr) {
166 break;
167 } else if (status.repr != wuffs_base__suspension__short_read) {
168 SkCodecPrintf("decode_image_config: %s", status.message());
170 } else if (!fill_buffer(b, s)) {
172 }
173 }
174
175 // A GIF image's natural color model is indexed color: 1 byte per pixel,
176 // indexing a 256-element palette.
177 //
178 // For Skia, we override that to decode to 4 bytes per pixel, BGRA or RGBA.
179 uint32_t pixfmt = WUFFS_BASE__PIXEL_FORMAT__INVALID;
180 switch (kN32_SkColorType) {
182 pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL;
183 break;
185 pixfmt = WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL;
186 break;
187 default:
189 }
190 if (imgcfg) {
191 imgcfg->pixcfg.set(pixfmt, WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, imgcfg->pixcfg.width(),
192 imgcfg->pixcfg.height());
193 }
194
195 return SkCodec::kSuccess;
196}
197
198// -------------------------------- Class definitions
199
200class SkWuffsCodec;
201
202class SkWuffsFrame final : public SkFrame {
203public:
204 SkWuffsFrame(wuffs_base__frame_config* fc);
205
206 uint64_t ioPosition() const;
207
208 // SkFrame overrides.
209 SkEncodedInfo::Alpha onReportedAlpha() const override;
210
211private:
212 uint64_t fIOPosition;
213 SkEncodedInfo::Alpha fReportedAlpha;
214
215 using INHERITED = SkFrame;
216};
217
218// SkWuffsFrameHolder is a trivial indirector that forwards its calls onto a
219// SkWuffsCodec. It is a separate class as SkWuffsCodec would otherwise
220// inherit from both SkCodec and SkFrameHolder, and Skia style discourages
221// multiple inheritance (e.g. with its "typedef Foo INHERITED" convention).
222class SkWuffsFrameHolder final : public SkFrameHolder {
223public:
225
226 void init(SkWuffsCodec* codec, int width, int height);
227
228 // SkFrameHolder overrides.
229 const SkFrame* onGetFrame(int i) const override;
230
231private:
232 const SkWuffsCodec* fCodec;
233
234 using INHERITED = SkFrameHolder;
235};
236
237class SkWuffsCodec final : public SkScalingCodec {
238public:
239 SkWuffsCodec(SkEncodedInfo&& encodedInfo,
240 std::unique_ptr<SkStream> stream,
241 bool canSeek,
242 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> dec,
243 std::unique_ptr<uint8_t, decltype(&sk_free)> workbuf_ptr,
244 size_t workbuf_len,
245 wuffs_base__image_config imgcfg,
246 wuffs_base__io_buffer iobuf);
247
248 const SkWuffsFrame* frame(int i) const;
249
250 std::unique_ptr<SkStream> getEncodedData() const override;
251
252private:
253 // SkCodec overrides.
254 SkEncodedImageFormat onGetEncodedFormat() const override;
255 Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*) override;
256 const SkFrameHolder* getFrameHolder() const override;
257 Result onStartIncrementalDecode(const SkImageInfo& dstInfo,
258 void* dst,
259 size_t rowBytes,
260 const SkCodec::Options& options) override;
261 Result onIncrementalDecode(int* rowsDecoded) override;
262 int onGetFrameCount() override;
263 bool onGetFrameInfo(int, FrameInfo*) const override;
264 int onGetRepetitionCount() override;
265
266 // Two separate implementations of onStartIncrementalDecode and
267 // onIncrementalDecode, named "one pass" and "two pass" decoding. One pass
268 // decoding writes directly from the Wuffs image decoder to the dst buffer
269 // (the dst argument to onStartIncrementalDecode). Two pass decoding first
270 // writes into an intermediate buffer, and then composites and transforms
271 // the intermediate buffer into the dst buffer.
272 //
273 // In the general case, we need the two pass decoder, because of Skia API
274 // features that Wuffs doesn't support (e.g. color correction, scaling,
275 // RGB565). But as an optimization, we use one pass decoding (it's faster
276 // and uses less memory) if applicable (see the assignment to
277 // fIncrDecOnePass that calculates when we can do so).
278 Result onStartIncrementalDecodeOnePass(const SkImageInfo& dstInfo,
279 uint8_t* dst,
280 size_t rowBytes,
282 uint32_t pixelFormat,
283 size_t bytesPerPixel);
284 Result onStartIncrementalDecodeTwoPass();
285 Result onIncrementalDecodeOnePass();
286 Result onIncrementalDecodeTwoPass();
287
288 void onGetFrameCountInternal();
289 Result seekFrame(int frameIndex);
290 Result resetDecoder();
291 const char* decodeFrameConfig();
292 const char* decodeFrame();
293 void updateNumFullyReceivedFrames();
294
295 SkWuffsFrameHolder fFrameHolder;
296 std::unique_ptr<SkStream> fPrivStream;
297 std::unique_ptr<uint8_t, decltype(&sk_free)> fWorkbufPtr;
298 size_t fWorkbufLen;
299
300 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> fDecoder;
301
302 const uint64_t fFirstFrameIOPosition;
303 wuffs_base__frame_config fFrameConfig;
304 wuffs_base__pixel_config fPixelConfig;
305 wuffs_base__pixel_buffer fPixelBuffer;
306 wuffs_base__io_buffer fIOBuffer;
307
308 // Incremental decoding state.
309 uint8_t* fIncrDecDst;
310 size_t fIncrDecRowBytes;
311 wuffs_base__pixel_blend fIncrDecPixelBlend;
312 bool fIncrDecOnePass;
313 bool fFirstCallToIncrementalDecode;
314
315 // Lazily allocated intermediate pixel buffer, for two pass decoding.
316 std::unique_ptr<uint8_t, decltype(&sk_free)> fTwoPassPixbufPtr;
317 size_t fTwoPassPixbufLen;
318
319 uint64_t fNumFullyReceivedFrames;
320 std::vector<SkWuffsFrame> fFrames;
321 bool fFramesComplete;
322
323 // If calling an fDecoder method returns an incomplete status, then
324 // fDecoder is suspended in a coroutine (i.e. waiting on I/O or halted on a
325 // non-recoverable error). To keep its internal proof-of-safety invariants
326 // consistent, there's only two things you can safely do with a suspended
327 // Wuffs object: resume the coroutine, or reset all state (memset to zero
328 // and start again).
329 //
330 // If fDecoderIsSuspended, and we aren't sure that we're going to resume
331 // the coroutine, then we will need to call this->resetDecoder before
332 // calling other fDecoder methods.
333 bool fDecoderIsSuspended;
334
335 uint8_t fBuffer[SK_WUFFS_CODEC_BUFFER_SIZE];
336
337 const bool fCanSeek;
338
340};
341
342// -------------------------------- SkWuffsFrame implementation
343
344SkWuffsFrame::SkWuffsFrame(wuffs_base__frame_config* fc)
345 : INHERITED(fc->index()),
346 fIOPosition(fc->io_position()),
347 fReportedAlpha(fc->opaque_within_bounds() ? SkEncodedInfo::kOpaque_Alpha
348 : SkEncodedInfo::kUnpremul_Alpha) {
349 wuffs_base__rect_ie_u32 r = fc->bounds();
350 this->setXYWH(r.min_incl_x, r.min_incl_y, r.width(), r.height());
352 this->setDuration(fc->duration() / WUFFS_BASE__FLICKS_PER_MILLISECOND);
353 this->setBlend(fc->overwrite_instead_of_blend() ? SkCodecAnimation::Blend::kSrc
355}
356
357uint64_t SkWuffsFrame::ioPosition() const {
358 return fIOPosition;
359}
360
362 return fReportedAlpha;
363}
364
365// -------------------------------- SkWuffsFrameHolder implementation
366
368 fCodec = codec;
369 // Initialize SkFrameHolder's (the superclass) fields.
372}
373
375 return fCodec->frame(i);
376}
377
378// -------------------------------- SkWuffsCodec implementation
379
381 std::unique_ptr<SkStream> stream,
382 bool canSeek,
383 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> dec,
384 std::unique_ptr<uint8_t, decltype(&sk_free)> workbuf_ptr,
385 size_t workbuf_len,
386 wuffs_base__image_config imgcfg,
387 wuffs_base__io_buffer iobuf)
388 : INHERITED(std::move(encodedInfo),
390 // Pass a nullptr SkStream to the SkCodec constructor. We
391 // manage the stream ourselves, as the default SkCodec behavior
392 // is too trigger-happy on rewinding the stream.
393 nullptr),
394 fFrameHolder(),
395 fPrivStream(std::move(stream)),
396 fWorkbufPtr(std::move(workbuf_ptr)),
397 fWorkbufLen(workbuf_len),
398 fDecoder(std::move(dec)),
399 fFirstFrameIOPosition(imgcfg.first_frame_io_position()),
400 fFrameConfig(wuffs_base__null_frame_config()),
401 fPixelConfig(imgcfg.pixcfg),
402 fPixelBuffer(wuffs_base__null_pixel_buffer()),
403 fIOBuffer(wuffs_base__empty_io_buffer()),
404 fIncrDecDst(nullptr),
405 fIncrDecRowBytes(0),
406 fIncrDecPixelBlend(WUFFS_BASE__PIXEL_BLEND__SRC),
407 fIncrDecOnePass(false),
408 fFirstCallToIncrementalDecode(false),
409 fTwoPassPixbufPtr(nullptr, &sk_free),
410 fTwoPassPixbufLen(0),
411 fNumFullyReceivedFrames(0),
412 fFramesComplete(false),
413 fDecoderIsSuspended(false),
414 fCanSeek(canSeek) {
415 fFrameHolder.init(this, imgcfg.pixcfg.width(), imgcfg.pixcfg.height());
416
417 // Initialize fIOBuffer's fields, copying any outstanding data from iobuf to
418 // fIOBuffer, as iobuf's backing array may not be valid for the lifetime of
419 // this SkWuffsCodec object, but fIOBuffer's backing array (fBuffer) is.
420 SkASSERT(iobuf.data.len == SK_WUFFS_CODEC_BUFFER_SIZE);
421 memmove(fBuffer, iobuf.data.ptr, iobuf.meta.wi);
422 fIOBuffer.data = wuffs_base__make_slice_u8(fBuffer, SK_WUFFS_CODEC_BUFFER_SIZE);
423 fIOBuffer.meta = iobuf.meta;
424}
425
427 if ((0 <= i) && (static_cast<size_t>(i) < fFrames.size())) {
428 return &fFrames[i];
429 }
430 return nullptr;
431}
432
433SkEncodedImageFormat SkWuffsCodec::onGetEncodedFormat() const {
435}
436
437SkCodec::Result SkWuffsCodec::onGetPixels(const SkImageInfo& dstInfo,
438 void* dst,
439 size_t rowBytes,
440 const Options& options,
441 int* rowsDecoded) {
442 SkCodec::Result result = this->onStartIncrementalDecode(dstInfo, dst, rowBytes, options);
443 if (result != kSuccess) {
444 return result;
445 }
446 return this->onIncrementalDecode(rowsDecoded);
447}
448
449const SkFrameHolder* SkWuffsCodec::getFrameHolder() const {
450 return &fFrameHolder;
451}
452
453SkCodec::Result SkWuffsCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
454 void* dst,
455 size_t rowBytes,
456 const SkCodec::Options& options) {
457 if (!dst) {
459 }
460 if (options.fSubset) {
462 }
463 SkCodec::Result result = this->seekFrame(options.fFrameIndex);
464 if (result != SkCodec::kSuccess) {
465 return result;
466 }
467
468 const char* status = this->decodeFrameConfig();
469 if (status == wuffs_base__suspension__short_read) {
471 } else if (status != nullptr) {
472 SkCodecPrintf("decodeFrameConfig: %s", status);
474 }
475
476 uint32_t pixelFormat = WUFFS_BASE__PIXEL_FORMAT__INVALID;
477 size_t bytesPerPixel = 0;
478
479 switch (dstInfo.colorType()) {
481 pixelFormat = WUFFS_BASE__PIXEL_FORMAT__BGR_565;
482 bytesPerPixel = 2;
483 break;
485 pixelFormat = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL;
486 bytesPerPixel = 4;
487 break;
489 pixelFormat = WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL;
490 bytesPerPixel = 4;
491 break;
492 default:
493 break;
494 }
495
496 // We can use "one pass" decoding if we have a Skia pixel format that Wuffs
497 // supports...
498 fIncrDecOnePass = (pixelFormat != WUFFS_BASE__PIXEL_FORMAT__INVALID) &&
499 // ...and no color profile (as Wuffs does not support them)...
500 (!getEncodedInfo().profile()) &&
501 // ...and we use the identity transform (as Wuffs does
502 // not support scaling).
503 (this->dimensions() == dstInfo.dimensions());
504
505 result = fIncrDecOnePass ? this->onStartIncrementalDecodeOnePass(
506 dstInfo, static_cast<uint8_t*>(dst), rowBytes, options,
507 pixelFormat, bytesPerPixel)
508 : this->onStartIncrementalDecodeTwoPass();
509 if (result != SkCodec::kSuccess) {
510 return result;
511 }
512
513 fIncrDecDst = static_cast<uint8_t*>(dst);
514 fIncrDecRowBytes = rowBytes;
515 fFirstCallToIncrementalDecode = true;
516 return SkCodec::kSuccess;
517}
518
519SkCodec::Result SkWuffsCodec::onStartIncrementalDecodeOnePass(const SkImageInfo& dstInfo,
520 uint8_t* dst,
521 size_t rowBytes,
523 uint32_t pixelFormat,
524 size_t bytesPerPixel) {
525 wuffs_base__pixel_config pixelConfig;
526 pixelConfig.set(pixelFormat, WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, dstInfo.width(),
527 dstInfo.height());
528
529 wuffs_base__table_u8 table;
530 table.ptr = dst;
531 table.width = static_cast<size_t>(dstInfo.width()) * bytesPerPixel;
532 table.height = dstInfo.height();
533 table.stride = rowBytes;
534
535 wuffs_base__status status = fPixelBuffer.set_from_table(&pixelConfig, table);
536 if (status.repr != nullptr) {
537 SkCodecPrintf("set_from_table: %s", status.message());
539 }
540
541 // SRC is usually faster than SRC_OVER, but for a dependent frame, dst is
542 // assumed to hold the previous frame's pixels (after processing the
543 // DisposalMethod). For one-pass decoding, we therefore use SRC_OVER.
544 if ((options.fFrameIndex != 0) &&
545 (this->frame(options.fFrameIndex)->getRequiredFrame() != SkCodec::kNoFrame)) {
546 fIncrDecPixelBlend = WUFFS_BASE__PIXEL_BLEND__SRC_OVER;
547 } else {
549 fIncrDecPixelBlend = WUFFS_BASE__PIXEL_BLEND__SRC;
550 }
551
552 return SkCodec::kSuccess;
553}
554
555SkCodec::Result SkWuffsCodec::onStartIncrementalDecodeTwoPass() {
556 // Either re-use the previously allocated "two pass" pixel buffer (and
557 // memset to zero), or allocate (and zero initialize) a new one.
558 bool already_zeroed = false;
559
560 if (!fTwoPassPixbufPtr) {
561 uint64_t pixbuf_len = fPixelConfig.pixbuf_len();
562 void* pixbuf_ptr_raw = (pixbuf_len <= SIZE_MAX)
564 : nullptr;
565 if (!pixbuf_ptr_raw) {
567 }
568 fTwoPassPixbufPtr.reset(reinterpret_cast<uint8_t*>(pixbuf_ptr_raw));
569 fTwoPassPixbufLen = SkToSizeT(pixbuf_len);
570 already_zeroed = true;
571 }
572
573 wuffs_base__status status = fPixelBuffer.set_from_slice(
574 &fPixelConfig, wuffs_base__make_slice_u8(fTwoPassPixbufPtr.get(), fTwoPassPixbufLen));
575 if (status.repr != nullptr) {
576 SkCodecPrintf("set_from_slice: %s", status.message());
578 }
579
580 if (!already_zeroed) {
581 uint32_t src_bits_per_pixel = fPixelConfig.pixel_format().bits_per_pixel();
582 if ((src_bits_per_pixel == 0) || (src_bits_per_pixel % 8 != 0)) {
584 }
585 size_t src_bytes_per_pixel = src_bits_per_pixel / 8;
586
587 wuffs_base__rect_ie_u32 frame_rect = fFrameConfig.bounds();
588 wuffs_base__table_u8 pixels = fPixelBuffer.plane(0);
589
590 uint8_t* ptr = pixels.ptr + (frame_rect.min_incl_y * pixels.stride) +
591 (frame_rect.min_incl_x * src_bytes_per_pixel);
592 size_t len = frame_rect.width() * src_bytes_per_pixel;
593
594 // As an optimization, issue a single sk_bzero call, if possible.
595 // Otherwise, zero out each row separately.
596 if ((len == pixels.stride) && (frame_rect.min_incl_y < frame_rect.max_excl_y)) {
597 sk_bzero(ptr, len * (frame_rect.max_excl_y - frame_rect.min_incl_y));
598 } else {
599 for (uint32_t y = frame_rect.min_incl_y; y < frame_rect.max_excl_y; y++) {
600 sk_bzero(ptr, len);
601 ptr += pixels.stride;
602 }
603 }
604 }
605
606 fIncrDecPixelBlend = WUFFS_BASE__PIXEL_BLEND__SRC;
607 return SkCodec::kSuccess;
608}
609
610SkCodec::Result SkWuffsCodec::onIncrementalDecode(int* rowsDecoded) {
611 if (!fIncrDecDst) {
613 }
614
615 if (rowsDecoded) {
616 *rowsDecoded = dstInfo().height();
617 }
618
620 fIncrDecOnePass ? this->onIncrementalDecodeOnePass() : this->onIncrementalDecodeTwoPass();
621 if (result == SkCodec::kSuccess) {
622 fIncrDecDst = nullptr;
623 fIncrDecRowBytes = 0;
624 fIncrDecPixelBlend = WUFFS_BASE__PIXEL_BLEND__SRC;
625 fIncrDecOnePass = false;
626 }
627 return result;
628}
629
630SkCodec::Result SkWuffsCodec::onIncrementalDecodeOnePass() {
631 const char* status = this->decodeFrame();
632 if (status != nullptr) {
633 if (status == wuffs_base__suspension__short_read) {
635 } else {
636 SkCodecPrintf("decodeFrame: %s", status);
638 }
639 }
640 return SkCodec::kSuccess;
641}
642
643SkCodec::Result SkWuffsCodec::onIncrementalDecodeTwoPass() {
645 const char* status = this->decodeFrame();
646 bool independent;
647 SkAlphaType alphaType;
648 const int index = options().fFrameIndex;
649 if (index == 0) {
650 independent = true;
651 alphaType = to_alpha_type(getEncodedInfo().opaque());
652 } else {
653 const SkWuffsFrame* f = this->frame(index);
654 independent = f->getRequiredFrame() == SkCodec::kNoFrame;
655 alphaType = to_alpha_type(f->reportedAlpha() == SkEncodedInfo::kOpaque_Alpha);
656 }
657 if (status != nullptr) {
658 if (status == wuffs_base__suspension__short_read) {
660 } else {
661 SkCodecPrintf("decodeFrame: %s", status);
663 }
664
665 if (!independent) {
666 // For a dependent frame, we cannot blend the partial result, since
667 // that will overwrite the contribution from prior frames.
668 return result;
669 }
670 }
671
672 uint32_t src_bits_per_pixel = fPixelBuffer.pixcfg.pixel_format().bits_per_pixel();
673 if ((src_bits_per_pixel == 0) || (src_bits_per_pixel % 8 != 0)) {
675 }
676 size_t src_bytes_per_pixel = src_bits_per_pixel / 8;
677
678 wuffs_base__rect_ie_u32 frame_rect = fFrameConfig.bounds();
679 if (fFirstCallToIncrementalDecode) {
680 if (frame_rect.width() > (SIZE_MAX / src_bytes_per_pixel)) {
682 }
683
684 auto bounds = SkIRect::MakeLTRB(frame_rect.min_incl_x, frame_rect.min_incl_y,
685 frame_rect.max_excl_x, frame_rect.max_excl_y);
686
687 // If the frame rect does not fill the output, ensure that those pixels are not
688 // left uninitialized.
689 if (independent && (bounds != this->bounds() || result != kSuccess)) {
690 SkSampler::Fill(dstInfo(), fIncrDecDst, fIncrDecRowBytes, options().fZeroInitialized);
691 }
692 fFirstCallToIncrementalDecode = false;
693 } else {
694 // Existing clients intend to only show frames beyond the first if they
695 // are complete (based on FrameInfo::fFullyReceived), since it might
696 // look jarring to draw a partial frame over an existing frame. If they
697 // changed their behavior and expected to continue decoding a partial
698 // frame after the first one, we'll need to update our blending code.
699 // Otherwise, if the frame were interlaced and not independent, the
700 // second pass may have an overlapping dirty_rect with the first,
701 // resulting in blending with the first pass.
702 SkASSERT(index == 0);
703 }
704
705 // If the frame's dirty rect is empty, no need to swizzle.
706 wuffs_base__rect_ie_u32 dirty_rect = fDecoder->frame_dirty_rect();
707 if (!dirty_rect.is_empty()) {
708 wuffs_base__table_u8 pixels = fPixelBuffer.plane(0);
709
710 // The Wuffs model is that the dst buffer is the image, not the frame.
711 // The expectation is that you allocate the buffer once, but re-use it
712 // for the N frames, regardless of each frame's top-left co-ordinate.
713 //
714 // To get from the start (in the X-direction) of the image to the start
715 // of the dirty_rect, we adjust s by (dirty_rect.min_incl_x * src_bytes_per_pixel).
716 uint8_t* s = pixels.ptr + (dirty_rect.min_incl_y * pixels.stride) +
717 (dirty_rect.min_incl_x * src_bytes_per_pixel);
718
719 // Currently, this is only used for GIF, which will never have an ICC profile. When it is
720 // used for other formats that might have one, we will need to transform from profiles that
721 // do not have corresponding SkColorSpaces.
723
724 auto srcInfo =
725 getInfo().makeWH(dirty_rect.width(), dirty_rect.height()).makeAlphaType(alphaType);
727 src.installPixels(srcInfo, s, pixels.stride);
729 if (independent) {
730 paint.setBlendMode(SkBlendMode::kSrc);
731 }
732
733 SkDraw draw;
734 draw.fDst.reset(dstInfo(), fIncrDecDst, fIncrDecRowBytes);
736 SkRect::Make(this->dstInfo().dimensions()));
737 draw.fCTM = &matrix;
739 draw.fRC = &rc;
740
741 SkMatrix translate = SkMatrix::Translate(dirty_rect.min_incl_x, dirty_rect.min_incl_y);
742 draw.drawBitmap(src, translate, nullptr, SkSamplingOptions(), paint);
743 }
744
745 if (result == SkCodec::kSuccess) {
746 // On success, we are done using the "two pass" pixel buffer for this
747 // frame. We have the option of releasing its memory, but there is a
748 // trade-off. If decoding a subsequent frame will also need "two pass"
749 // decoding, it would have to re-allocate the buffer instead of just
750 // re-using it. On the other hand, if there is no subsequent frame, and
751 // the SkWuffsCodec object isn't deleted soon, then we are holding
752 // megabytes of memory longer than we need to.
753 //
754 // For example, when the Chromium web browser decodes the <img> tags in
755 // a HTML page, the SkCodec object can live until navigating away from
756 // the page, which can be much longer than when the pixels are fully
757 // decoded, especially for a still (non-animated) image. Even for
758 // looping animations, caching the decoded frames (at the higher HTML
759 // renderer layer) may mean that each frame is only decoded once (at
760 // the lower SkCodec layer), in sequence.
761 //
762 // The heuristic we use here is to free the memory if we have decoded
763 // the last frame of the animation (or, for still images, the only
764 // frame). The output of the next decode request (if any) should be the
765 // same either way, but the steady state memory use should hopefully be
766 // lower than always keeping the fTwoPassPixbufPtr buffer up until the
767 // SkWuffsCodec destructor runs.
768 //
769 // This only applies to "two pass" decoding. "One pass" decoding does
770 // not allocate, free or otherwise use fTwoPassPixbufPtr.
771 if (fFramesComplete && (static_cast<size_t>(options().fFrameIndex) == fFrames.size() - 1)) {
772 fTwoPassPixbufPtr.reset(nullptr);
773 fTwoPassPixbufLen = 0;
774 }
775 }
776
777 return result;
778}
779
780int SkWuffsCodec::onGetFrameCount() {
781 if (!fCanSeek) {
782 return 1;
783 }
784
785 // It is valid, in terms of the SkCodec API, to call SkCodec::getFrameCount
786 // while in an incremental decode (after onStartIncrementalDecode returns
787 // and before onIncrementalDecode returns kSuccess).
788 //
789 // We should not advance the SkWuffsCodec' stream while doing so, even
790 // though other SkCodec implementations can return increasing values from
791 // onGetFrameCount when given more data. If we tried to do so, the
792 // subsequent resume of the incremental decode would continue reading from
793 // a different position in the I/O stream, leading to an incorrect error.
794 //
795 // Other SkCodec implementations can move the stream forward during
796 // onGetFrameCount because they assume that the stream is rewindable /
797 // seekable. For example, an alternative GIF implementation may choose to
798 // store, for each frame walked past when merely counting the number of
799 // frames, the I/O position of each of the frame's GIF data blocks. (A GIF
800 // frame's compressed data can have multiple data blocks, each at most 255
801 // bytes in length). Obviously, this can require O(numberOfFrames) extra
802 // memory to store these I/O positions. The constant factor is small, but
803 // it's still O(N), not O(1).
804 //
805 // Wuffs and SkWuffsCodec try to minimize relying on the rewindable /
806 // seekable assumption. By design, Wuffs per se aims for O(1) memory use
807 // (after any pixel buffers are allocated) instead of O(N), and its I/O
808 // type, wuffs_base__io_buffer, is not necessarily rewindable or seekable.
809 //
810 // The Wuffs API provides a limited, optional form of seeking, to the start
811 // of an animation frame's data, but does not provide arbitrary save and
812 // load of its internal state whilst in the middle of an animation frame.
813 bool incrementalDecodeIsInProgress = fIncrDecDst != nullptr;
814
815 if (!fFramesComplete && !incrementalDecodeIsInProgress) {
816 this->onGetFrameCountInternal();
817 this->updateNumFullyReceivedFrames();
818 }
819 return fFrames.size();
820}
821
822void SkWuffsCodec::onGetFrameCountInternal() {
823 size_t n = fFrames.size();
824 int i = n ? n - 1 : 0;
825 if (this->seekFrame(i) != SkCodec::kSuccess) {
826 return;
827 }
828
829 // Iterate through the frames, converting from Wuffs'
830 // wuffs_base__frame_config type to Skia's SkWuffsFrame type.
831 for (; i < INT_MAX; i++) {
832 const char* status = this->decodeFrameConfig();
833 if (status == nullptr) {
834 // No-op.
835 } else if (status == wuffs_base__note__end_of_data) {
836 break;
837 } else {
838 return;
839 }
840
841 if (static_cast<size_t>(i) < fFrames.size()) {
842 continue;
843 }
844 fFrames.emplace_back(&fFrameConfig);
845 SkWuffsFrame* f = &fFrames[fFrames.size() - 1];
846 fFrameHolder.setAlphaAndRequiredFrame(f);
847 }
848
849 fFramesComplete = true;
850}
851
852bool SkWuffsCodec::onGetFrameInfo(int i, SkCodec::FrameInfo* frameInfo) const {
853 if (!fCanSeek) {
854 // We haven't read forward in the stream, so this info isn't available.
855 return false;
856 }
857
858 const SkWuffsFrame* f = this->frame(i);
859 if (!f) {
860 return false;
861 }
862 if (frameInfo) {
863 f->fillIn(frameInfo, static_cast<uint64_t>(i) < this->fNumFullyReceivedFrames);
864 }
865 return true;
866}
867
868int SkWuffsCodec::onGetRepetitionCount() {
869 // Convert from Wuffs's loop count to Skia's repeat count. Wuffs' uint32_t
870 // number is how many times to play the loop. Skia's int number is how many
871 // times to play the loop *after the first play*. Wuffs and Skia use 0 and
872 // kRepetitionCountInfinite respectively to mean loop forever.
873 uint32_t n = fDecoder->num_animation_loops();
874 if (n == 0) {
876 }
877 n--;
878 return n < INT_MAX ? n : INT_MAX;
879}
880
881SkCodec::Result SkWuffsCodec::seekFrame(int frameIndex) {
882 if (fDecoderIsSuspended) {
883 SkCodec::Result res = this->resetDecoder();
884 if (res != SkCodec::kSuccess) {
885 return res;
886 }
887 }
888
889 uint64_t pos = 0;
890 if (frameIndex < 0) {
892 } else if (frameIndex == 0) {
893 pos = fFirstFrameIOPosition;
894 } else if (static_cast<size_t>(frameIndex) < fFrames.size()) {
895 pos = fFrames[frameIndex].ioPosition();
896 } else {
898 }
899
900 if (!seek_buffer(&fIOBuffer, fPrivStream.get(), pos)) {
902 }
903 wuffs_base__status status =
904 fDecoder->restart_frame(frameIndex, fIOBuffer.reader_io_position());
905 if (status.repr != nullptr) {
907 }
908 return SkCodec::kSuccess;
909}
910
911SkCodec::Result SkWuffsCodec::resetDecoder() {
912 if (!fPrivStream->rewind()) {
914 }
915 fIOBuffer.meta = wuffs_base__empty_io_buffer_meta();
916
918 reset_and_decode_image_config(fDecoder.get(), nullptr, &fIOBuffer, fPrivStream.get());
921 } else if (result != SkCodec::kSuccess) {
922 return result;
923 }
924
925 fDecoderIsSuspended = false;
926 return SkCodec::kSuccess;
927}
928
929const char* SkWuffsCodec::decodeFrameConfig() {
930 while (true) {
931 wuffs_base__status status =
932 fDecoder->decode_frame_config(&fFrameConfig, &fIOBuffer);
933 if ((status.repr == wuffs_base__suspension__short_read) &&
934 fill_buffer(&fIOBuffer, fPrivStream.get())) {
935 continue;
936 }
937 fDecoderIsSuspended = !status.is_complete();
938 this->updateNumFullyReceivedFrames();
939 return status.repr;
940 }
941}
942
943const char* SkWuffsCodec::decodeFrame() {
944 while (true) {
945 wuffs_base__status status = fDecoder->decode_frame(
946 &fPixelBuffer, &fIOBuffer, fIncrDecPixelBlend,
947 wuffs_base__make_slice_u8(fWorkbufPtr.get(), fWorkbufLen), nullptr);
948 if ((status.repr == wuffs_base__suspension__short_read) &&
949 fill_buffer(&fIOBuffer, fPrivStream.get())) {
950 continue;
951 }
952 fDecoderIsSuspended = !status.is_complete();
953 this->updateNumFullyReceivedFrames();
954 return status.repr;
955 }
956}
957
958void SkWuffsCodec::updateNumFullyReceivedFrames() {
959 // num_decoded_frames's return value, n, can change over time, both up and
960 // down, as we seek back and forth in the underlying stream.
961 // fNumFullyReceivedFrames is the highest n we've seen.
962 uint64_t n = fDecoder->num_decoded_frames();
963 if (fNumFullyReceivedFrames < n) {
964 fNumFullyReceivedFrames = n;
965 }
966}
967
968// We cannot use the SkCodec implementation since we pass nullptr to the superclass out of
969// an abundance of caution w/r to rewinding the stream.
970std::unique_ptr<SkStream> SkWuffsCodec::getEncodedData() const {
971 SkASSERT(fPrivStream);
972 return fPrivStream->duplicate();
973}
974
975namespace SkGifDecoder {
976
977bool IsGif(const void* buf, size_t bytesRead) {
978 constexpr const char* gif_ptr = "GIF8";
979 constexpr size_t gif_len = 4;
980 return (bytesRead >= gif_len) && (memcmp(buf, gif_ptr, gif_len) == 0);
981}
982
983std::unique_ptr<SkCodec> MakeFromStream(std::unique_ptr<SkStream> stream,
984 SkCodec::SelectionPolicy selectionPolicy,
987 if (!stream) {
989 return nullptr;
990 }
991
992 bool canSeek = stream->hasPosition() && stream->hasLength();
993
994 if (selectionPolicy != SkCodec::SelectionPolicy::kPreferStillImage) {
995 // Some clients (e.g. Android) need to be able to seek the stream, but may
996 // not provide a seekable stream. Copy the stream to one that can seek.
997 if (!canSeek) {
998 auto data = SkCopyStreamToData(stream.get());
999 stream = std::make_unique<SkMemoryStream>(std::move(data));
1000 canSeek = true;
1001 }
1002 }
1003
1005 wuffs_base__io_buffer iobuf =
1006 wuffs_base__make_io_buffer(wuffs_base__make_slice_u8(buffer, SK_WUFFS_CODEC_BUFFER_SIZE),
1007 wuffs_base__empty_io_buffer_meta());
1008 wuffs_base__image_config imgcfg = wuffs_base__null_image_config();
1009
1010 // Wuffs is primarily a C library, not a C++ one. Furthermore, outside of
1011 // the wuffs_base__etc types, the sizeof a file format specific type like
1012 // GIF's wuffs_gif__decoder can vary between Wuffs versions. If p is of
1013 // type wuffs_gif__decoder*, then the supported API treats p as a pointer
1014 // to an opaque type: a private implementation detail. The API is always
1015 // "set_foo(p, etc)" and not "p->foo = etc".
1016 //
1017 // See https://en.wikipedia.org/wiki/Opaque_pointer#C
1018 //
1019 // Thus, we don't use C++'s new operator (which requires knowing the sizeof
1020 // the struct at compile time). Instead, we use sk_malloc_canfail, with
1021 // sizeof__wuffs_gif__decoder returning the appropriate value for the
1022 // (statically or dynamically) linked version of the Wuffs library.
1023 //
1024 // As a C (not C++) library, none of the Wuffs types have constructors or
1025 // destructors.
1026 //
1027 // In RAII style, we can still use std::unique_ptr with these pointers, but
1028 // we pair the pointer with sk_free instead of C++'s delete.
1029 void* decoder_raw = sk_malloc_canfail(sizeof__wuffs_gif__decoder());
1030 if (!decoder_raw) {
1032 return nullptr;
1033 }
1034 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> decoder(
1035 reinterpret_cast<wuffs_gif__decoder*>(decoder_raw), &sk_free);
1036
1037 SkCodec::Result reset_result =
1038 reset_and_decode_image_config(decoder.get(), &imgcfg, &iobuf, stream.get());
1039 if (reset_result != SkCodec::kSuccess) {
1040 *result = reset_result;
1041 return nullptr;
1042 }
1043
1044 uint32_t width = imgcfg.pixcfg.width();
1045 uint32_t height = imgcfg.pixcfg.height();
1046 if ((width == 0) || (width > INT_MAX) || (height == 0) || (height > INT_MAX)) {
1048 return nullptr;
1049 }
1050
1051 uint64_t workbuf_len = decoder->workbuf_len().max_incl;
1052 void* workbuf_ptr_raw = nullptr;
1053 if (workbuf_len) {
1054 workbuf_ptr_raw = workbuf_len <= SIZE_MAX ? sk_malloc_canfail(workbuf_len) : nullptr;
1055 if (!workbuf_ptr_raw) {
1057 return nullptr;
1058 }
1059 }
1060 std::unique_ptr<uint8_t, decltype(&sk_free)> workbuf_ptr(
1061 reinterpret_cast<uint8_t*>(workbuf_ptr_raw), &sk_free);
1062
1064 (imgcfg.pixcfg.pixel_format().repr == WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL)
1067
1068 // In Skia's API, the alpha we calculate here and return is only for the
1069 // first frame.
1070 SkEncodedInfo::Alpha alpha = imgcfg.first_frame_is_opaque() ? SkEncodedInfo::kOpaque_Alpha
1072
1073 SkEncodedInfo encodedInfo = SkEncodedInfo::Make(width, height, color, alpha, 8);
1074
1076 return std::unique_ptr<SkCodec>(new SkWuffsCodec(std::move(encodedInfo), std::move(stream),
1077 canSeek,
1078 std::move(decoder), std::move(workbuf_ptr),
1079 workbuf_len, imgcfg, iobuf));
1080}
1081
1082std::unique_ptr<SkCodec> Decode(std::unique_ptr<SkStream> stream,
1083 SkCodec::Result* outResult,
1085 SkCodec::Result resultStorage;
1086 if (!outResult) {
1087 outResult = &resultStorage;
1088 }
1090 if (ctx) {
1091 policy = *static_cast<SkCodec::SelectionPolicy*>(ctx);
1092 }
1093 return MakeFromStream(std::move(stream), policy, outResult);
1094}
1095
1096std::unique_ptr<SkCodec> Decode(sk_sp<SkData> data,
1097 SkCodec::Result* outResult,
1099 if (!data) {
1100 if (outResult) {
1101 *outResult = SkCodec::kInvalidInput;
1102 }
1103 return nullptr;
1104 }
1105 return Decode(SkMemoryStream::Make(std::move(data)), outResult, ctx);
1106}
1107} // namespace SkGifDecoder
1108
const char * options
sk_bzero(glyphs, sizeof(glyphs))
SkPoint pos
SkAlphaType
Definition: SkAlphaType.h:26
@ kOpaque_SkAlphaType
pixel is opaque
Definition: SkAlphaType.h:28
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define SkCodecPrintf(...)
Definition: SkCodecPriv.h:23
static bool independent(const SkFrame &frame)
Definition: SkCodec.cpp:922
@ kBGRA_8888_SkColorType
pixel with 8 bits for blue, green, red, alpha; in 32-bit word
Definition: SkColorType.h:26
@ kRGB_565_SkColorType
pixel with 5 bits red, 6 bits green, 5 bits blue, in 16-bit word
Definition: SkColorType.h:22
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
SkEncodedImageFormat
SK_API void * sk_malloc_flags(size_t size, unsigned flags)
@ SK_MALLOC_ZERO_INITIALIZE
Definition: SkMalloc.h:34
SK_API void sk_free(void *)
static void * sk_malloc_canfail(size_t size)
Definition: SkMalloc.h:93
sk_sp< SkData > SkCopyStreamToData(SkStream *stream)
Definition: SkStream.cpp:937
constexpr size_t SkToSizeT(S x)
Definition: SkTo.h:31
static SkCodecAnimation::DisposalMethod wuffs_disposal_to_skia_disposal(wuffs_base__animation_disposal w)
static SkCodec::Result reset_and_decode_image_config(wuffs_gif__decoder *decoder, wuffs_base__image_config *imgcfg, wuffs_base__io_buffer *b, SkStream *s)
static SkAlphaType to_alpha_type(bool opaque)
static bool seek_buffer(wuffs_base__io_buffer *b, SkStream *s, uint64_t pos)
#define SK_WUFFS_CODEC_BUFFER_SIZE
#define SK_WUFFS_INITIALIZE_FLAGS
static bool fill_buffer(wuffs_base__io_buffer *b, SkStream *s)
SI F table(const skcms_Curve *curve, F v)
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition: aaclip.cpp:27
SkISize dimensions() const
Definition: SkCodec.h:230
const SkImageInfo & dstInfo() const
Definition: SkCodec.h:878
SkStream * stream()
Definition: SkCodec.h:865
SkIRect bounds() const
Definition: SkCodec.h:231
const SkEncodedInfo & getEncodedInfo() const
Definition: SkCodec.h:788
Result
Definition: SkCodec.h:76
@ kIncompleteInput
Definition: SkCodec.h:84
@ kInvalidInput
Definition: SkCodec.h:109
@ kInvalidParameters
Definition: SkCodec.h:105
@ kInternalError
Definition: SkCodec.h:118
@ kUnimplemented
Definition: SkCodec.h:123
@ kSuccess
Definition: SkCodec.h:80
@ kErrorInInput
Definition: SkCodec.h:91
SelectionPolicy
Definition: SkCodec.h:136
static constexpr int kRepetitionCountInfinite
Definition: SkCodec.h:759
static constexpr int kNoFrame
Definition: SkCodec.h:650
SkImageInfo getInfo() const
Definition: SkCodec.h:228
const Options & options() const
Definition: SkCodec.h:880
const SkRasterClip * fRC
Definition: SkDrawBase.h:156
const SkMatrix * fCTM
Definition: SkDrawBase.h:155
Definition: SkDraw.h:38
void setAlphaAndRequiredFrame(SkFrame *)
Definition: SkCodec.cpp:972
SkFrame(int id)
Definition: SkFrameHolder.h:26
void setBlend(SkCodecAnimation::Blend blend)
void setDuration(int duration)
void setDisposalMethod(SkCodecAnimation::DisposalMethod disposalMethod)
void setXYWH(int x, int y, int width, int height)
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition: SkMatrix.h:157
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.h:91
static std::unique_ptr< SkMemoryStream > Make(sk_sp< SkData > data)
Definition: SkStream.cpp:314
static void Fill(const SkImageInfo &info, void *dst, size_t rowBytes, SkCodec::ZeroInitialized zeroInit)
Definition: SkSampler.cpp:20
SkScalingCodec(SkEncodedInfo &&info, XformFormat srcFormat, std::unique_ptr< SkStream > stream, SkEncodedOrigin origin=kTopLeft_SkEncodedOrigin)
const SkWuffsFrame * frame(int i) const
SkWuffsCodec(SkEncodedInfo &&encodedInfo, std::unique_ptr< SkStream > stream, bool canSeek, std::unique_ptr< wuffs_gif__decoder, decltype(&sk_free)> dec, std::unique_ptr< uint8_t, decltype(&sk_free)> workbuf_ptr, size_t workbuf_len, wuffs_base__image_config imgcfg, wuffs_base__io_buffer iobuf)
std::unique_ptr< SkStream > getEncodedData() const override
const SkFrame * onGetFrame(int i) const override
void init(SkWuffsCodec *codec, int width, int height)
SkWuffsFrame(wuffs_base__frame_config *fc)
SkEncodedInfo::Alpha onReportedAlpha() const override
uint64_t ioPosition() const
const Paint & paint
Definition: color_source.cc:38
DlColor color
static bool b
struct MyStruct s
GAsyncResult * result
double y
void * DecodeContext
Definition: SkCodec.h:1047
std::unique_ptr< SkCodec > MakeFromStream(std::unique_ptr< SkStream > stream, SkCodec::SelectionPolicy selectionPolicy, SkCodec::Result *result)
SK_API std::unique_ptr< SkCodec > Decode(std::unique_ptr< SkStream >, SkCodec::Result *, SkCodecs::DecodeContext=nullptr)
SK_API bool IsGif(const void *, size_t)
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
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 policy
Definition: switches.h:248
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
dst
Definition: cp.py:12
SkSamplingOptions(SkFilterMode::kLinear))
Definition: ref_ptr.h:256
SkScalar w
int32_t height
int32_t width
@ skcms_PixelFormat_RGBA_8888
Definition: skcms_public.h:287
const SkIRect * fSubset
Definition: SkCodec.h:347
ZeroInitialized fZeroInitialized
Definition: SkCodec.h:329
const skcms_ICCProfile * profile() const
static SkEncodedInfo Make(int width, int height, Color color, Alpha alpha, int bitsPerComponent)
static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b)
Definition: SkRect.h:91
static constexpr SkIRect MakeSize(const SkISize &size)
Definition: SkRect.h:66
SkImageInfo makeWH(int newWidth, int newHeight) const
Definition: SkImageInfo.h:444
SkImageInfo makeAlphaType(SkAlphaType newAlphaType) const
Definition: SkImageInfo.h:466
SkISize dimensions() const
Definition: SkImageInfo.h:421
int width() const
Definition: SkImageInfo.h:365
SkColorType colorType() const
Definition: SkImageInfo.h:373
int height() const
Definition: SkImageInfo.h:371
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63