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
8#include "src/codec/SkWuffsCodec.h"
9
10#include "include/core/SkBitmap.h"
11#include "include/core/SkMatrix.h"
12#include "include/core/SkPaint.h"
13#include "include/private/SkMalloc.h"
14#include "src/codec/SkFrameHolder.h"
15#include "src/codec/SkSampler.h"
16#include "src/codec/SkScalingCodec.h"
17#include "src/core/SkDraw.h"
18#include "src/core/SkMatrixProvider.h"
19#include "src/core/SkRasterClip.h"
20#include "src/core/SkUtils.h"
21
22#include <limits.h>
23
24// SK_OPT_OUT_OF_WUFFS_V_0_3 is the step 4 "Flip Skia-uses-Wuffs-v0.3 from
25// opt-in to opt-out" comment in the top-level BUILD.gn file.
26//
27// This SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2 machinery should all be deleted by
28// mid-to-late 2020, as part of step 5, when we end up using only Wuffs version
29// 0.3, not either of v0.3 and v0.2.
30#if !defined(SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2) && !defined(SK_OPT_OUT_OF_WUFFS_V_0_3)
31#define SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
32#endif
33
34// Documentation on the Wuffs language and standard library (in general) and
35// its image decoding API (in particular) is at:
36//
37// - https://github.com/google/wuffs/tree/master/doc
38// - https://github.com/google/wuffs/blob/master/doc/std/image-decoders.md
39
40// Wuffs ships as a "single file C library" or "header file library" as per
41// https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
42//
43// As we have not #define'd WUFFS_IMPLEMENTATION, the #include here is
44// including a header file, even though that file name ends in ".c".
45#if defined(WUFFS_IMPLEMENTATION)
46#error "SkWuffsCodec should not #define WUFFS_IMPLEMENTATION"
47#endif
48#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
49#include "wuffs-v0.3.c"
50#else
51#include "wuffs-v0.2.c"
52#endif
53#if WUFFS_VERSION_BUILD_METADATA_COMMIT_COUNT < 1942
54#error "Wuffs version is too old. Upgrade to the latest version."
55#endif
56
57#define SK_WUFFS_CODEC_BUFFER_SIZE 4096
58
59// Configuring a Skia build with
60// SK_WUFFS_FAVORS_PERFORMANCE_OVER_ADDITIONAL_MEMORY_SAFETY can improve decode
61// performance by some fixed amount (independent of the image size), which can
62// be a noticeable proportional improvement if the input is relatively small.
63//
64// The Wuffs library is still memory-safe either way, in that there are no
65// out-of-bounds reads or writes, and the library endeavours not to read
66// uninitialized memory. There are just fewer compiler-enforced guarantees
67// against reading uninitialized memory. For more detail, see
68// https://github.com/google/wuffs/blob/master/doc/note/initialization.md#partial-zero-initialization
69#if defined(SK_WUFFS_FAVORS_PERFORMANCE_OVER_ADDITIONAL_MEMORY_SAFETY)
70#define SK_WUFFS_INITIALIZE_FLAGS WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED
71#else
72#define SK_WUFFS_INITIALIZE_FLAGS WUFFS_INITIALIZE__DEFAULT_OPTIONS
73#endif
74
75static bool fill_buffer(wuffs_base__io_buffer* b, SkStream* s) {
76 b->compact();
77 size_t num_read = s->read(b->data.ptr + b->meta.wi, b->data.len - b->meta.wi);
78 b->meta.wi += num_read;
79 b->meta.closed = s->isAtEnd();
80 return num_read > 0;
81}
82
83static bool seek_buffer(wuffs_base__io_buffer* b, SkStream* s, uint64_t pos) {
84 // Try to re-position the io_buffer's meta.ri read-index first, which is
85 // cheaper than seeking in the backing SkStream.
86 if ((pos >= b->meta.pos) && (pos - b->meta.pos <= b->meta.wi)) {
87 b->meta.ri = pos - b->meta.pos;
88 return true;
89 }
90 // Seek in the backing SkStream.
91 if ((pos > SIZE_MAX) || (!s->seek(pos))) {
92 return false;
93 }
94 b->meta.wi = 0;
95 b->meta.ri = 0;
96 b->meta.pos = pos;
97 b->meta.closed = false;
98 return true;
99}
100
101#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
102#else
103static SkEncodedInfo::Alpha wuffs_blend_to_skia_alpha(wuffs_base__animation_blend w) {
104 return (w == WUFFS_BASE__ANIMATION_BLEND__OPAQUE) ? SkEncodedInfo::kOpaque_Alpha
105 : SkEncodedInfo::kUnpremul_Alpha;
106}
107
108static SkCodecAnimation::Blend wuffs_blend_to_skia_blend(wuffs_base__animation_blend w) {
109 return (w == WUFFS_BASE__ANIMATION_BLEND__SRC) ? SkCodecAnimation::Blend::kBG
110 : SkCodecAnimation::Blend::kPriorFrame;
111}
112#endif
113
114static SkCodecAnimation::DisposalMethod wuffs_disposal_to_skia_disposal(
115 wuffs_base__animation_disposal w) {
116 switch (w) {
117 case WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_BACKGROUND:
118 return SkCodecAnimation::DisposalMethod::kRestoreBGColor;
119 case WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_PREVIOUS:
120 return SkCodecAnimation::DisposalMethod::kRestorePrevious;
121 default:
122 return SkCodecAnimation::DisposalMethod::kKeep;
123 }
124}
125
126static SkAlphaType to_alpha_type(bool opaque) {
127 return opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
128}
129
130static SkCodec::Result reset_and_decode_image_config(wuffs_gif__decoder* decoder,
131 wuffs_base__image_config* imgcfg,
132 wuffs_base__io_buffer* b,
133 SkStream* s) {
134 // Calling decoder->initialize will memset most or all of it to zero,
135 // depending on SK_WUFFS_INITIALIZE_FLAGS.
136 wuffs_base__status status =
137 decoder->initialize(sizeof__wuffs_gif__decoder(), WUFFS_VERSION, SK_WUFFS_INITIALIZE_FLAGS);
138#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
139 if (status.repr != nullptr) {
140 SkCodecPrintf("initialize: %s", status.message());
141 return SkCodec::kInternalError;
142 }
143 while (true) {
144 status = decoder->decode_image_config(imgcfg, b);
145 if (status.repr == nullptr) {
146 break;
147 } else if (status.repr != wuffs_base__suspension__short_read) {
148 SkCodecPrintf("decode_image_config: %s", status.message());
149 return SkCodec::kErrorInInput;
150 } else if (!fill_buffer(b, s)) {
151 return SkCodec::kIncompleteInput;
152 }
153 }
154#else
155 if (status != nullptr) {
156 SkCodecPrintf("initialize: %s", status);
157 return SkCodec::kInternalError;
158 }
159 while (true) {
160 status = decoder->decode_image_config(imgcfg, b);
161 if (status == nullptr) {
162 break;
163 } else if (status != wuffs_base__suspension__short_read) {
164 SkCodecPrintf("decode_image_config: %s", status);
165 return SkCodec::kErrorInInput;
166 } else if (!fill_buffer(b, s)) {
167 return SkCodec::kIncompleteInput;
168 }
169 }
170#endif
171
172 // A GIF image's natural color model is indexed color: 1 byte per pixel,
173 // indexing a 256-element palette.
174 //
175 // For Skia, we override that to decode to 4 bytes per pixel, BGRA or RGBA.
176 uint32_t pixfmt = WUFFS_BASE__PIXEL_FORMAT__INVALID;
177 switch (kN32_SkColorType) {
178 case kBGRA_8888_SkColorType:
179 pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL;
180 break;
181 case kRGBA_8888_SkColorType:
182 pixfmt = WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL;
183 break;
184 default:
185 return SkCodec::kInternalError;
186 }
187 if (imgcfg) {
188 imgcfg->pixcfg.set(pixfmt, WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, imgcfg->pixcfg.width(),
189 imgcfg->pixcfg.height());
190 }
191
192 return SkCodec::kSuccess;
193}
194
195// -------------------------------- Class definitions
196
197class SkWuffsCodec;
198
199class SkWuffsFrame final : public SkFrame {
200public:
201 SkWuffsFrame(wuffs_base__frame_config* fc);
202
203 SkCodec::FrameInfo frameInfo(bool fullyReceived) const;
204 uint64_t ioPosition() const;
205
206 // SkFrame overrides.
207 SkEncodedInfo::Alpha onReportedAlpha() const override;
208
209private:
210 uint64_t fIOPosition;
211 SkEncodedInfo::Alpha fReportedAlpha;
212
213 typedef SkFrame INHERITED;
214};
215
216// SkWuffsFrameHolder is a trivial indirector that forwards its calls onto a
217// SkWuffsCodec. It is a separate class as SkWuffsCodec would otherwise
218// inherit from both SkCodec and SkFrameHolder, and Skia style discourages
219// multiple inheritance (e.g. with its "typedef Foo INHERITED" convention).
220class SkWuffsFrameHolder final : public SkFrameHolder {
221public:
222 SkWuffsFrameHolder() : INHERITED() {}
223
224 void init(SkWuffsCodec* codec, int width, int height);
225
226 // SkFrameHolder overrides.
227 const SkFrame* onGetFrame(int i) const override;
228
229private:
230 const SkWuffsCodec* fCodec;
231
232 typedef SkFrameHolder INHERITED;
233};
234
235class SkWuffsCodec final : public SkScalingCodec {
236public:
237 SkWuffsCodec(SkEncodedInfo&& encodedInfo,
238 std::unique_ptr<SkStream> stream,
239 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> dec,
240 std::unique_ptr<uint8_t, decltype(&sk_free)> workbuf_ptr,
241 size_t workbuf_len,
242 wuffs_base__image_config imgcfg,
243 wuffs_base__io_buffer iobuf);
244
245 const SkWuffsFrame* frame(int i) const;
246
247private:
248 // It is valid, in terms of the SkCodec API, to call SkCodec::getFrameCount
249 // while in an incremental decode (after onStartIncrementalDecode returns
250 // and before the rest of the image is decoded). Some Skia users expect
251 // getFrameCount to increase, and the SkStream to advance, when given more
252 // data.
253 //
254 // On the other hand, while in an incremental decode, the underlying Wuffs
255 // object is suspended in a coroutine. To keep its internal proof-of-safety
256 // invariants consistent, there's only two things you can safely do with a
257 // suspended Wuffs object: resume the coroutine, or reset all state (memset
258 // to zero and start again).
259 //
260 // The Wuffs API provides a limited, optional form of seeking, to the start
261 // of an animation frame's data, but does not provide arbitrary save and
262 // load of its internal state whilst in the middle of an animation frame.
263 //
264 // SkWuffsCodec therefore uses two Wuffs decoders: a primary decoder
265 // (kIncrDecode) to support startIncrementalDecode / incrementalDecode, and
266 // a secondary decoder (kFrameCount) to support getFrameCount. The two
267 // decoders' states can change independently.
268 //
269 // As of Wuffs version 0.2, both of these decoders have the same type. A
270 // future Wuffs version might let us use a different type for kFrameCount,
271 // one that is much lighter weight (in terms of memory requirements), as it
272 // doesn't have to handle decompressing pixel data.
273 enum WhichDecoder {
274 kIncrDecode,
275 kFrameCount,
276 kNumDecoders,
277 };
278
279 // SkCodec overrides.
280 SkEncodedImageFormat onGetEncodedFormat() const override;
281 Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*) override;
282 const SkFrameHolder* getFrameHolder() const override;
283 Result onStartIncrementalDecode(const SkImageInfo& dstInfo,
284 void* dst,
285 size_t rowBytes,
286 const SkCodec::Options& options) override;
287 Result onIncrementalDecode(int* rowsDecoded) override;
288 int onGetFrameCount() override;
289 bool onGetFrameInfo(int, FrameInfo*) const override;
290 int onGetRepetitionCount() override;
291
292 // Two separate implementations of onStartIncrementalDecode and
293 // onIncrementalDecode, named "one pass" and "two pass" decoding. One pass
294 // decoding writes directly from the Wuffs image decoder to the dst buffer
295 // (the dst argument to onStartIncrementalDecode). Two pass decoding first
296 // writes into an intermediate buffer, and then composites and transforms
297 // the intermediate buffer into the dst buffer.
298 //
299 // In the general case, we need the two pass decoder, because of Skia API
300 // features that Wuffs doesn't support (e.g. color correction, scaling,
301 // RGB565). But as an optimization, we use one pass decoding (it's faster
302 // and uses less memory) if applicable (see the assignment to
303 // fIncrDecOnePass that calculates when we can do so).
304 Result onStartIncrementalDecodeOnePass(const SkImageInfo& dstInfo,
305 uint8_t* dst,
306 size_t rowBytes,
307 const SkCodec::Options& options,
308 uint32_t pixelFormat,
309 size_t bytesPerPixel);
310 Result onStartIncrementalDecodeTwoPass();
311 Result onIncrementalDecodeOnePass();
312 Result onIncrementalDecodeTwoPass();
313
314 void onGetFrameCountInternal();
315 Result seekFrame(WhichDecoder which, int frameIndex);
316 Result resetDecoder(WhichDecoder which);
317 const char* decodeFrameConfig(WhichDecoder which);
318 const char* decodeFrame(WhichDecoder which);
319 void updateNumFullyReceivedFrames(WhichDecoder which);
320
321 SkWuffsFrameHolder fFrameHolder;
322 std::unique_ptr<SkStream> fStream;
323 std::unique_ptr<uint8_t, decltype(&sk_free)> fWorkbufPtr;
324 size_t fWorkbufLen;
325
326 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> fDecoders[WhichDecoder::kNumDecoders];
327
328 const uint64_t fFirstFrameIOPosition;
329 wuffs_base__frame_config fFrameConfigs[WhichDecoder::kNumDecoders];
330 wuffs_base__pixel_config fPixelConfig;
331 wuffs_base__pixel_buffer fPixelBuffer;
332 wuffs_base__io_buffer fIOBuffer;
333
334 // Incremental decoding state.
335 uint8_t* fIncrDecDst;
336 uint64_t fIncrDecReaderIOPosition;
337 size_t fIncrDecRowBytes;
338 bool fIncrDecOnePass;
339 bool fFirstCallToIncrementalDecode;
340
341 // Lazily allocated intermediate pixel buffer, for two pass decoding.
342 std::unique_ptr<uint8_t, decltype(&sk_free)> fTwoPassPixbufPtr;
343 size_t fTwoPassPixbufLen;
344
345 uint64_t fFrameCountReaderIOPosition;
346 uint64_t fNumFullyReceivedFrames;
347 std::vector<SkWuffsFrame> fFrames;
348 bool fFramesComplete;
349
350 // If calling an fDecoders[which] method returns an incomplete status, then
351 // fDecoders[which] is suspended in a coroutine (i.e. waiting on I/O or
352 // halted on a non-recoverable error). To keep its internal proof-of-safety
353 // invariants consistent, there's only two things you can safely do with a
354 // suspended Wuffs object: resume the coroutine, or reset all state (memset
355 // to zero and start again).
356 //
357 // If fDecoderIsSuspended[which], and we aren't sure that we're going to
358 // resume the coroutine, then we will need to call this->resetDecoder
359 // before calling other fDecoders[which] methods.
360 bool fDecoderIsSuspended[WhichDecoder::kNumDecoders];
361
362 uint8_t fBuffer[SK_WUFFS_CODEC_BUFFER_SIZE];
363
364 typedef SkScalingCodec INHERITED;
365};
366
367// -------------------------------- SkWuffsFrame implementation
368
369SkWuffsFrame::SkWuffsFrame(wuffs_base__frame_config* fc)
370 : INHERITED(fc->index()),
371 fIOPosition(fc->io_position()),
372#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
373 fReportedAlpha(fc->opaque_within_bounds() ? SkEncodedInfo::kOpaque_Alpha
374 : SkEncodedInfo::kUnpremul_Alpha)
375#else
376 fReportedAlpha(wuffs_blend_to_skia_alpha(fc->blend()))
377#endif
378{
379 wuffs_base__rect_ie_u32 r = fc->bounds();
380 this->setXYWH(r.min_incl_x, r.min_incl_y, r.width(), r.height());
381 this->setDisposalMethod(wuffs_disposal_to_skia_disposal(fc->disposal()));
382 this->setDuration(fc->duration() / WUFFS_BASE__FLICKS_PER_MILLISECOND);
383#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
384 this->setBlend(fc->overwrite_instead_of_blend() ? SkCodecAnimation::Blend::kBG
385 : SkCodecAnimation::Blend::kPriorFrame);
386#else
387 this->setBlend(wuffs_blend_to_skia_blend(fc->blend()));
388#endif
389}
390
391SkCodec::FrameInfo SkWuffsFrame::frameInfo(bool fullyReceived) const {
392 SkCodec::FrameInfo ret;
393 ret.fRequiredFrame = getRequiredFrame();
394 ret.fDuration = getDuration();
395 ret.fFullyReceived = fullyReceived;
396 ret.fAlphaType = hasAlpha() ? kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
397 ret.fDisposalMethod = getDisposalMethod();
398 return ret;
399}
400
401uint64_t SkWuffsFrame::ioPosition() const {
402 return fIOPosition;
403}
404
405SkEncodedInfo::Alpha SkWuffsFrame::onReportedAlpha() const {
406 return fReportedAlpha;
407}
408
409// -------------------------------- SkWuffsFrameHolder implementation
410
411void SkWuffsFrameHolder::init(SkWuffsCodec* codec, int width, int height) {
412 fCodec = codec;
413 // Initialize SkFrameHolder's (the superclass) fields.
414 fScreenWidth = width;
415 fScreenHeight = height;
416}
417
418const SkFrame* SkWuffsFrameHolder::onGetFrame(int i) const {
419 return fCodec->frame(i);
420};
421
422// -------------------------------- SkWuffsCodec implementation
423
424SkWuffsCodec::SkWuffsCodec(SkEncodedInfo&& encodedInfo,
425 std::unique_ptr<SkStream> stream,
426 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> dec,
427 std::unique_ptr<uint8_t, decltype(&sk_free)> workbuf_ptr,
428 size_t workbuf_len,
429 wuffs_base__image_config imgcfg,
430 wuffs_base__io_buffer iobuf)
431 : INHERITED(std::move(encodedInfo),
432 skcms_PixelFormat_RGBA_8888,
433 // Pass a nullptr SkStream to the SkCodec constructor. We
434 // manage the stream ourselves, as the default SkCodec behavior
435 // is too trigger-happy on rewinding the stream.
436 nullptr),
437 fFrameHolder(),
438 fStream(std::move(stream)),
439 fWorkbufPtr(std::move(workbuf_ptr)),
440 fWorkbufLen(workbuf_len),
441 fDecoders{
442 std::move(dec),
443 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)>(nullptr, sk_free),
444 },
445 fFirstFrameIOPosition(imgcfg.first_frame_io_position()),
446 fFrameConfigs{
447 wuffs_base__null_frame_config(),
448 wuffs_base__null_frame_config(),
449 },
450 fPixelConfig(imgcfg.pixcfg),
451 fPixelBuffer(wuffs_base__null_pixel_buffer()),
452 fIOBuffer(wuffs_base__empty_io_buffer()),
453 fIncrDecDst(nullptr),
454 fIncrDecReaderIOPosition(0),
455 fIncrDecRowBytes(0),
456 fIncrDecOnePass(false),
457 fFirstCallToIncrementalDecode(false),
458 fTwoPassPixbufPtr(nullptr, &sk_free),
459 fTwoPassPixbufLen(0),
460 fFrameCountReaderIOPosition(0),
461 fNumFullyReceivedFrames(0),
462 fFramesComplete(false),
463 fDecoderIsSuspended{
464 false,
465 false,
466 } {
467 fFrameHolder.init(this, imgcfg.pixcfg.width(), imgcfg.pixcfg.height());
468
469 // Initialize fIOBuffer's fields, copying any outstanding data from iobuf to
470 // fIOBuffer, as iobuf's backing array may not be valid for the lifetime of
471 // this SkWuffsCodec object, but fIOBuffer's backing array (fBuffer) is.
472 SkASSERT(iobuf.data.len == SK_WUFFS_CODEC_BUFFER_SIZE);
473 memmove(fBuffer, iobuf.data.ptr, iobuf.meta.wi);
474 fIOBuffer.data = wuffs_base__make_slice_u8(fBuffer, SK_WUFFS_CODEC_BUFFER_SIZE);
475 fIOBuffer.meta = iobuf.meta;
476}
477
478const SkWuffsFrame* SkWuffsCodec::frame(int i) const {
479 if ((0 <= i) && (static_cast<size_t>(i) < fFrames.size())) {
480 return &fFrames[i];
481 }
482 return nullptr;
483}
484
485SkEncodedImageFormat SkWuffsCodec::onGetEncodedFormat() const {
486 return SkEncodedImageFormat::kGIF;
487}
488
489SkCodec::Result SkWuffsCodec::onGetPixels(const SkImageInfo& dstInfo,
490 void* dst,
491 size_t rowBytes,
492 const Options& options,
493 int* rowsDecoded) {
494 SkCodec::Result result = this->onStartIncrementalDecode(dstInfo, dst, rowBytes, options);
495 if (result != kSuccess) {
496 return result;
497 }
498 return this->onIncrementalDecode(rowsDecoded);
499}
500
501const SkFrameHolder* SkWuffsCodec::getFrameHolder() const {
502 return &fFrameHolder;
503}
504
505SkCodec::Result SkWuffsCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
506 void* dst,
507 size_t rowBytes,
508 const SkCodec::Options& options) {
509 if (!dst) {
510 return SkCodec::kInvalidParameters;
511 }
512 if (options.fSubset) {
513 return SkCodec::kUnimplemented;
514 }
515 if (options.fFrameIndex > 0 && SkColorTypeIsAlwaysOpaque(dstInfo.colorType())) {
516 return SkCodec::kInvalidConversion;
517 }
518 SkCodec::Result result = this->seekFrame(WhichDecoder::kIncrDecode, options.fFrameIndex);
519 if (result != SkCodec::kSuccess) {
520 return result;
521 }
522
523 const char* status = this->decodeFrameConfig(WhichDecoder::kIncrDecode);
524 if (status == wuffs_base__suspension__short_read) {
525 return SkCodec::kIncompleteInput;
526 } else if (status != nullptr) {
527 SkCodecPrintf("decodeFrameConfig: %s", status);
528 return SkCodec::kErrorInInput;
529 }
530
531 uint32_t pixelFormat = WUFFS_BASE__PIXEL_FORMAT__INVALID;
532 size_t bytesPerPixel = 0;
533
534 switch (dstInfo.colorType()) {
535 case kBGRA_8888_SkColorType:
536 pixelFormat = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL;
537 bytesPerPixel = 4;
538 break;
539 case kRGBA_8888_SkColorType:
540 pixelFormat = WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL;
541 bytesPerPixel = 4;
542 break;
543 default:
544 break;
545 }
546
547 // We can use "one pass" decoding if we have a Skia pixel format that Wuffs
548 // supports...
549 fIncrDecOnePass =
550 (pixelFormat != WUFFS_BASE__PIXEL_FORMAT__INVALID) &&
551 // ...and no color profile (as Wuffs does not support them)...
552 (!getEncodedInfo().profile()) &&
553 // ...and we have an independent frame (as Wuffs does not support the
554 // equivalent of SkBlendMode::kSrcOver)...
555 ((options.fFrameIndex == 0) ||
556 (this->frame(options.fFrameIndex)->getRequiredFrame() == SkCodec::kNoFrame)) &&
557 // ...and we use the identity transform (as Wuffs does not support
558 // scaling).
559 (this->dimensions() == dstInfo.dimensions());
560
561 result = fIncrDecOnePass ? this->onStartIncrementalDecodeOnePass(
562 dstInfo, static_cast<uint8_t*>(dst), rowBytes, options,
563 pixelFormat, bytesPerPixel)
564 : this->onStartIncrementalDecodeTwoPass();
565 if (result != SkCodec::kSuccess) {
566 return result;
567 }
568
569 fIncrDecDst = static_cast<uint8_t*>(dst);
570 fIncrDecReaderIOPosition = fIOBuffer.reader_io_position();
571 fIncrDecRowBytes = rowBytes;
572 fFirstCallToIncrementalDecode = true;
573 return SkCodec::kSuccess;
574}
575
576SkCodec::Result SkWuffsCodec::onStartIncrementalDecodeOnePass(const SkImageInfo& dstInfo,
577 uint8_t* dst,
578 size_t rowBytes,
579 const SkCodec::Options& options,
580 uint32_t pixelFormat,
581 size_t bytesPerPixel) {
582 wuffs_base__pixel_config pixelConfig;
583 pixelConfig.set(pixelFormat, WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, dstInfo.width(),
584 dstInfo.height());
585
586 wuffs_base__table_u8 table;
587 table.ptr = dst;
588 table.width = static_cast<size_t>(dstInfo.width()) * bytesPerPixel;
589 table.height = dstInfo.height();
590 table.stride = rowBytes;
591
592 wuffs_base__status status = fPixelBuffer.set_from_table(&pixelConfig, table);
593#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
594 if (status.repr != nullptr) {
595 SkCodecPrintf("set_from_table: %s", status.message());
596 return SkCodec::kInternalError;
597 }
598#else
599 if (status != nullptr) {
600 SkCodecPrintf("set_from_table: %s", status);
601 return SkCodec::kInternalError;
602 }
603#endif
604
605 SkSampler::Fill(dstInfo, dst, rowBytes, options.fZeroInitialized);
606 return SkCodec::kSuccess;
607}
608
609SkCodec::Result SkWuffsCodec::onStartIncrementalDecodeTwoPass() {
610 // Either re-use the previously allocated "two pass" pixel buffer (and
611 // memset to zero), or allocate (and zero initialize) a new one.
612 bool already_zeroed = false;
613
614 if (!fTwoPassPixbufPtr) {
615 uint64_t pixbuf_len = fPixelConfig.pixbuf_len();
616 void* pixbuf_ptr_raw = (pixbuf_len <= SIZE_MAX)
617 ? sk_malloc_flags(pixbuf_len, SK_MALLOC_ZERO_INITIALIZE)
618 : nullptr;
619 if (!pixbuf_ptr_raw) {
620 return SkCodec::kInternalError;
621 }
622 fTwoPassPixbufPtr.reset(reinterpret_cast<uint8_t*>(pixbuf_ptr_raw));
623 fTwoPassPixbufLen = SkToSizeT(pixbuf_len);
624 already_zeroed = true;
625 }
626
627 wuffs_base__status status = fPixelBuffer.set_from_slice(
628 &fPixelConfig, wuffs_base__make_slice_u8(fTwoPassPixbufPtr.get(), fTwoPassPixbufLen));
629#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
630 if (status.repr != nullptr) {
631 SkCodecPrintf("set_from_slice: %s", status.message());
632 return SkCodec::kInternalError;
633 }
634#else
635 if (status != nullptr) {
636 SkCodecPrintf("set_from_slice: %s", status);
637 return SkCodec::kInternalError;
638 }
639#endif
640
641 if (!already_zeroed) {
642#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
643 uint32_t src_bits_per_pixel = fPixelConfig.pixel_format().bits_per_pixel();
644#else
645 uint32_t src_bits_per_pixel =
646 wuffs_base__pixel_format__bits_per_pixel(fPixelConfig.pixel_format());
647#endif
648 if ((src_bits_per_pixel == 0) || (src_bits_per_pixel % 8 != 0)) {
649 return SkCodec::kInternalError;
650 }
651 size_t src_bytes_per_pixel = src_bits_per_pixel / 8;
652
653 wuffs_base__rect_ie_u32 frame_rect = fFrameConfigs[WhichDecoder::kIncrDecode].bounds();
654 wuffs_base__table_u8 pixels = fPixelBuffer.plane(0);
655
656 uint8_t* ptr = pixels.ptr + (frame_rect.min_incl_y * pixels.stride) +
657 (frame_rect.min_incl_x * src_bytes_per_pixel);
658 size_t len = frame_rect.width() * src_bytes_per_pixel;
659
660 // As an optimization, issue a single sk_bzero call, if possible.
661 // Otherwise, zero out each row separately.
662 if ((len == pixels.stride) && (frame_rect.min_incl_y < frame_rect.max_excl_y)) {
663 sk_bzero(ptr, len * (frame_rect.max_excl_y - frame_rect.min_incl_y));
664 } else {
665 for (uint32_t y = frame_rect.min_incl_y; y < frame_rect.max_excl_y; y++) {
666 sk_bzero(ptr, len);
667 ptr += pixels.stride;
668 }
669 }
670 }
671
672 return SkCodec::kSuccess;
673}
674
675SkCodec::Result SkWuffsCodec::onIncrementalDecode(int* rowsDecoded) {
676 if (!fIncrDecDst) {
677 return SkCodec::kInternalError;
678 }
679
680 // If multiple SkCodec::incrementalDecode calls are made consecutively (or
681 // if SkCodec::incrementalDecode is called immediately after
682 // SkCodec::startIncrementalDecode), then this seek should be a no-op.
683 // However, it is possible to interleave SkCodec::getFrameCount calls in
684 // between SkCodec::incrementalDecode calls, and those other calls may
685 // advance the stream. This seek restores the stream to where the last
686 // SkCodec::startIncrementalDecode or SkCodec::incrementalDecode stopped.
687 if (!seek_buffer(&fIOBuffer, fStream.get(), fIncrDecReaderIOPosition)) {
688 return SkCodec::kInternalError;
689 }
690
691 if (rowsDecoded) {
692 *rowsDecoded = dstInfo().height();
693 }
694
695 SkCodec::Result result =
696 fIncrDecOnePass ? this->onIncrementalDecodeOnePass() : this->onIncrementalDecodeTwoPass();
697 if (result == SkCodec::kSuccess) {
698 fIncrDecDst = nullptr;
699 fIncrDecReaderIOPosition = 0;
700 fIncrDecRowBytes = 0;
701 fIncrDecOnePass = false;
702 } else {
703 fIncrDecReaderIOPosition = fIOBuffer.reader_io_position();
704 }
705 return result;
706}
707
708SkCodec::Result SkWuffsCodec::onIncrementalDecodeOnePass() {
709 const char* status = this->decodeFrame(WhichDecoder::kIncrDecode);
710 if (status != nullptr) {
711 if (status == wuffs_base__suspension__short_read) {
712 return SkCodec::kIncompleteInput;
713 } else {
714 SkCodecPrintf("decodeFrame: %s", status);
715 return SkCodec::kErrorInInput;
716 }
717 }
718 return SkCodec::kSuccess;
719}
720
721SkCodec::Result SkWuffsCodec::onIncrementalDecodeTwoPass() {
722 SkCodec::Result result = SkCodec::kSuccess;
723 const char* status = this->decodeFrame(WhichDecoder::kIncrDecode);
724 bool independent;
725 SkAlphaType alphaType;
726 const int index = options().fFrameIndex;
727 if (index == 0) {
728 independent = true;
729 alphaType = to_alpha_type(getEncodedInfo().opaque());
730 } else {
731 const SkWuffsFrame* f = this->frame(index);
732 independent = f->getRequiredFrame() == SkCodec::kNoFrame;
733 alphaType = to_alpha_type(f->reportedAlpha() == SkEncodedInfo::kOpaque_Alpha);
734 }
735 if (status != nullptr) {
736 if (status == wuffs_base__suspension__short_read) {
737 result = SkCodec::kIncompleteInput;
738 } else {
739 SkCodecPrintf("decodeFrame: %s", status);
740 result = SkCodec::kErrorInInput;
741 }
742
743 if (!independent) {
744 // For a dependent frame, we cannot blend the partial result, since
745 // that will overwrite the contribution from prior frames.
746 return result;
747 }
748 }
749
750#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
751 uint32_t src_bits_per_pixel = fPixelBuffer.pixcfg.pixel_format().bits_per_pixel();
752#else
753 uint32_t src_bits_per_pixel =
754 wuffs_base__pixel_format__bits_per_pixel(fPixelBuffer.pixcfg.pixel_format());
755#endif
756 if ((src_bits_per_pixel == 0) || (src_bits_per_pixel % 8 != 0)) {
757 return SkCodec::kInternalError;
758 }
759 size_t src_bytes_per_pixel = src_bits_per_pixel / 8;
760
761 wuffs_base__rect_ie_u32 frame_rect = fFrameConfigs[WhichDecoder::kIncrDecode].bounds();
762 if (fFirstCallToIncrementalDecode) {
763 if (frame_rect.width() > (SIZE_MAX / src_bytes_per_pixel)) {
764 return SkCodec::kInternalError;
765 }
766
767 auto bounds = SkIRect::MakeLTRB(frame_rect.min_incl_x, frame_rect.min_incl_y,
768 frame_rect.max_excl_x, frame_rect.max_excl_y);
769
770 // If the frame rect does not fill the output, ensure that those pixels are not
771 // left uninitialized.
772 if (independent && (bounds != this->bounds() || result != kSuccess)) {
773 SkSampler::Fill(dstInfo(), fIncrDecDst, fIncrDecRowBytes, options().fZeroInitialized);
774 }
775 fFirstCallToIncrementalDecode = false;
776 } else {
777 // Existing clients intend to only show frames beyond the first if they
778 // are complete (based on FrameInfo::fFullyReceived), since it might
779 // look jarring to draw a partial frame over an existing frame. If they
780 // changed their behavior and expected to continue decoding a partial
781 // frame after the first one, we'll need to update our blending code.
782 // Otherwise, if the frame were interlaced and not independent, the
783 // second pass may have an overlapping dirty_rect with the first,
784 // resulting in blending with the first pass.
785 SkASSERT(index == 0);
786 }
787
788 // If the frame's dirty rect is empty, no need to swizzle.
789 wuffs_base__rect_ie_u32 dirty_rect = fDecoders[WhichDecoder::kIncrDecode]->frame_dirty_rect();
790 if (!dirty_rect.is_empty()) {
791 wuffs_base__table_u8 pixels = fPixelBuffer.plane(0);
792
793 // The Wuffs model is that the dst buffer is the image, not the frame.
794 // The expectation is that you allocate the buffer once, but re-use it
795 // for the N frames, regardless of each frame's top-left co-ordinate.
796 //
797 // To get from the start (in the X-direction) of the image to the start
798 // of the dirty_rect, we adjust s by (dirty_rect.min_incl_x * src_bytes_per_pixel).
799 uint8_t* s = pixels.ptr + (dirty_rect.min_incl_y * pixels.stride) +
800 (dirty_rect.min_incl_x * src_bytes_per_pixel);
801
802 // Currently, this is only used for GIF, which will never have an ICC profile. When it is
803 // used for other formats that might have one, we will need to transform from profiles that
804 // do not have corresponding SkColorSpaces.
805 SkASSERT(!getEncodedInfo().profile());
806
807 auto srcInfo =
808 getInfo().makeWH(dirty_rect.width(), dirty_rect.height()).makeAlphaType(alphaType);
809 SkBitmap src;
810 src.installPixels(srcInfo, s, pixels.stride);
811 SkPaint paint;
812 if (independent) {
813 paint.setBlendMode(SkBlendMode::kSrc);
814 }
815
816 SkDraw draw;
817 draw.fDst.reset(dstInfo(), fIncrDecDst, fIncrDecRowBytes);
818 SkMatrix matrix = SkMatrix::MakeRectToRect(SkRect::Make(this->dimensions()),
819 SkRect::Make(this->dstInfo().dimensions()),
820 SkMatrix::kFill_ScaleToFit);
821 SkSimpleMatrixProvider matrixProvider(matrix);
822 draw.fMatrixProvider = &matrixProvider;
823 SkRasterClip rc(SkIRect::MakeSize(this->dstInfo().dimensions()));
824 draw.fRC = &rc;
825
826 SkMatrix translate = SkMatrix::Translate(dirty_rect.min_incl_x, dirty_rect.min_incl_y);
827 draw.drawBitmap(src, translate, nullptr, paint);
828 }
829
830 if (result == SkCodec::kSuccess) {
831 // On success, we are done using the "two pass" pixel buffer for this
832 // frame. We have the option of releasing its memory, but there is a
833 // trade-off. If decoding a subsequent frame will also need "two pass"
834 // decoding, it would have to re-allocate the buffer instead of just
835 // re-using it. On the other hand, if there is no subsequent frame, and
836 // the SkWuffsCodec object isn't deleted soon, then we are holding
837 // megabytes of memory longer than we need to.
838 //
839 // For example, when the Chromium web browser decodes the <img> tags in
840 // a HTML page, the SkCodec object can live until navigating away from
841 // the page, which can be much longer than when the pixels are fully
842 // decoded, especially for a still (non-animated) image. Even for
843 // looping animations, caching the decoded frames (at the higher HTML
844 // renderer layer) may mean that each frame is only decoded once (at
845 // the lower SkCodec layer), in sequence.
846 //
847 // The heuristic we use here is to free the memory if we have decoded
848 // the last frame of the animation (or, for still images, the only
849 // frame). The output of the next decode request (if any) should be the
850 // same either way, but the steady state memory use should hopefully be
851 // lower than always keeping the fTwoPassPixbufPtr buffer up until the
852 // SkWuffsCodec destructor runs.
853 //
854 // This only applies to "two pass" decoding. "One pass" decoding does
855 // not allocate, free or otherwise use fTwoPassPixbufPtr.
856 if (fFramesComplete && (static_cast<size_t>(options().fFrameIndex) == fFrames.size() - 1)) {
857 fTwoPassPixbufPtr.reset(nullptr);
858 fTwoPassPixbufLen = 0;
859 }
860 }
861
862 return result;
863}
864
865int SkWuffsCodec::onGetFrameCount() {
866 if (!fFramesComplete && seek_buffer(&fIOBuffer, fStream.get(), fFrameCountReaderIOPosition)) {
867 this->onGetFrameCountInternal();
868 fFrameCountReaderIOPosition =
869 fDecoders[WhichDecoder::kFrameCount] ? fIOBuffer.reader_io_position() : 0;
870 }
871 return fFrames.size();
872}
873
874void SkWuffsCodec::onGetFrameCountInternal() {
875 if (!fDecoders[WhichDecoder::kFrameCount]) {
876 void* decoder_raw = sk_malloc_canfail(sizeof__wuffs_gif__decoder());
877 if (!decoder_raw) {
878 return;
879 }
880 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> decoder(
881 reinterpret_cast<wuffs_gif__decoder*>(decoder_raw), &sk_free);
882 reset_and_decode_image_config(decoder.get(), nullptr, &fIOBuffer, fStream.get());
883 fDecoders[WhichDecoder::kFrameCount] = std::move(decoder);
884 }
885
886 // Iterate through the frames, converting from Wuffs'
887 // wuffs_base__frame_config type to Skia's SkWuffsFrame type.
888 while (true) {
889 const char* status = this->decodeFrameConfig(WhichDecoder::kFrameCount);
890 if (status == nullptr) {
891 // No-op.
892#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
893 } else if (status == wuffs_base__note__end_of_data) {
894#else
895 } else if (status == wuffs_base__warning__end_of_data) {
896#endif
897 break;
898 } else {
899 return;
900 }
901
902 uint64_t i = fDecoders[WhichDecoder::kFrameCount]->num_decoded_frame_configs();
903 if (i > INT_MAX) {
904 break;
905 }
906 if ((i == 0) || (static_cast<size_t>(i - 1) != fFrames.size())) {
907 continue;
908 }
909 fFrames.emplace_back(&fFrameConfigs[WhichDecoder::kFrameCount]);
910 SkWuffsFrame* f = &fFrames[fFrames.size() - 1];
911 fFrameHolder.setAlphaAndRequiredFrame(f);
912 }
913
914 fFramesComplete = true;
915
916 // We've seen the end of the animation. There'll be no more frames, so we
917 // no longer need the kFrameCount decoder. Releasing it earlier than the
918 // SkWuffsCodec destructor might help peak memory use.
919 fDecoders[WhichDecoder::kFrameCount].reset(nullptr);
920}
921
922bool SkWuffsCodec::onGetFrameInfo(int i, SkCodec::FrameInfo* frameInfo) const {
923 const SkWuffsFrame* f = this->frame(i);
924 if (!f) {
925 return false;
926 }
927 if (frameInfo) {
928 *frameInfo = f->frameInfo(static_cast<uint64_t>(i) < this->fNumFullyReceivedFrames);
929 }
930 return true;
931}
932
933int SkWuffsCodec::onGetRepetitionCount() {
934 // Convert from Wuffs's loop count to Skia's repeat count. Wuffs' uint32_t
935 // number is how many times to play the loop. Skia's int number is how many
936 // times to play the loop *after the first play*. Wuffs and Skia use 0 and
937 // kRepetitionCountInfinite respectively to mean loop forever.
938 uint32_t n = fDecoders[WhichDecoder::kIncrDecode]->num_animation_loops();
939 if (n == 0) {
940 return SkCodec::kRepetitionCountInfinite;
941 }
942 n--;
943 return n < INT_MAX ? n : INT_MAX;
944}
945
946SkCodec::Result SkWuffsCodec::seekFrame(WhichDecoder which, int frameIndex) {
947 if (fDecoderIsSuspended[which]) {
948 SkCodec::Result res = this->resetDecoder(which);
949 if (res != SkCodec::kSuccess) {
950 return res;
951 }
952 }
953
954 uint64_t pos = 0;
955 if (frameIndex < 0) {
956 return SkCodec::kInternalError;
957 } else if (frameIndex == 0) {
958 pos = fFirstFrameIOPosition;
959 } else if (static_cast<size_t>(frameIndex) < fFrames.size()) {
960 pos = fFrames[frameIndex].ioPosition();
961 } else {
962 return SkCodec::kInternalError;
963 }
964
965 if (!seek_buffer(&fIOBuffer, fStream.get(), pos)) {
966 return SkCodec::kInternalError;
967 }
968 wuffs_base__status status =
969 fDecoders[which]->restart_frame(frameIndex, fIOBuffer.reader_io_position());
970#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
971 if (status.repr != nullptr) {
972 return SkCodec::kInternalError;
973 }
974#else
975 if (status != nullptr) {
976 return SkCodec::kInternalError;
977 }
978#endif
979 return SkCodec::kSuccess;
980}
981
982SkCodec::Result SkWuffsCodec::resetDecoder(WhichDecoder which) {
983 if (!fStream->rewind()) {
984 return SkCodec::kInternalError;
985 }
986 fIOBuffer.meta = wuffs_base__empty_io_buffer_meta();
987
988 SkCodec::Result result =
989 reset_and_decode_image_config(fDecoders[which].get(), nullptr, &fIOBuffer, fStream.get());
990 if (result == SkCodec::kIncompleteInput) {
991 return SkCodec::kInternalError;
992 } else if (result != SkCodec::kSuccess) {
993 return result;
994 }
995
996 fDecoderIsSuspended[which] = false;
997 return SkCodec::kSuccess;
998}
999
1000const char* SkWuffsCodec::decodeFrameConfig(WhichDecoder which) {
1001 while (true) {
1002 wuffs_base__status status =
1003 fDecoders[which]->decode_frame_config(&fFrameConfigs[which], &fIOBuffer);
1004#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
1005 if ((status.repr == wuffs_base__suspension__short_read) &&
1006 fill_buffer(&fIOBuffer, fStream.get())) {
1007 continue;
1008 }
1009 fDecoderIsSuspended[which] = !status.is_complete();
1010 this->updateNumFullyReceivedFrames(which);
1011 return status.repr;
1012#else
1013 if ((status == wuffs_base__suspension__short_read) &&
1014 fill_buffer(&fIOBuffer, fStream.get())) {
1015 continue;
1016 }
1017 fDecoderIsSuspended[which] = !wuffs_base__status__is_complete(status);
1018 this->updateNumFullyReceivedFrames(which);
1019 return status;
1020#endif
1021 }
1022}
1023
1024const char* SkWuffsCodec::decodeFrame(WhichDecoder which) {
1025 while (true) {
1026#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
1027 wuffs_base__status status = fDecoders[which]->decode_frame(
1028 &fPixelBuffer, &fIOBuffer, WUFFS_BASE__PIXEL_BLEND__SRC,
1029 wuffs_base__make_slice_u8(fWorkbufPtr.get(), fWorkbufLen), NULL);
1030 if ((status.repr == wuffs_base__suspension__short_read) &&
1031 fill_buffer(&fIOBuffer, fStream.get())) {
1032 continue;
1033 }
1034 fDecoderIsSuspended[which] = !status.is_complete();
1035 this->updateNumFullyReceivedFrames(which);
1036 return status.repr;
1037#else
1038 const char* status = fDecoders[which]->decode_frame(
1039 &fPixelBuffer, &fIOBuffer, wuffs_base__make_slice_u8(fWorkbufPtr.get(), fWorkbufLen),
1040 NULL);
1041 if ((status == wuffs_base__suspension__short_read) &&
1042 fill_buffer(&fIOBuffer, fStream.get())) {
1043 continue;
1044 }
1045 fDecoderIsSuspended[which] = !wuffs_base__status__is_complete(status);
1046 this->updateNumFullyReceivedFrames(which);
1047 return status;
1048#endif
1049 }
1050}
1051
1052void SkWuffsCodec::updateNumFullyReceivedFrames(WhichDecoder which) {
1053 // num_decoded_frames's return value, n, can change over time, both up and
1054 // down, as we seek back and forth in the underlying stream.
1055 // fNumFullyReceivedFrames is the highest n we've seen.
1056 uint64_t n = fDecoders[which]->num_decoded_frames();
1057 if (fNumFullyReceivedFrames < n) {
1058 fNumFullyReceivedFrames = n;
1059 }
1060}
1061
1062// -------------------------------- SkWuffsCodec.h functions
1063
1064bool SkWuffsCodec_IsFormat(const void* buf, size_t bytesRead) {
1065 constexpr const char* gif_ptr = "GIF8";
1066 constexpr size_t gif_len = 4;
1067 return (bytesRead >= gif_len) && (memcmp(buf, gif_ptr, gif_len) == 0);
1068}
1069
1070std::unique_ptr<SkCodec> SkWuffsCodec_MakeFromStream(std::unique_ptr<SkStream> stream,
1071 SkCodec::Result* result) {
1072 uint8_t buffer[SK_WUFFS_CODEC_BUFFER_SIZE];
1073 wuffs_base__io_buffer iobuf =
1074 wuffs_base__make_io_buffer(wuffs_base__make_slice_u8(buffer, SK_WUFFS_CODEC_BUFFER_SIZE),
1075 wuffs_base__empty_io_buffer_meta());
1076 wuffs_base__image_config imgcfg = wuffs_base__null_image_config();
1077
1078 // Wuffs is primarily a C library, not a C++ one. Furthermore, outside of
1079 // the wuffs_base__etc types, the sizeof a file format specific type like
1080 // GIF's wuffs_gif__decoder can vary between Wuffs versions. If p is of
1081 // type wuffs_gif__decoder*, then the supported API treats p as a pointer
1082 // to an opaque type: a private implementation detail. The API is always
1083 // "set_foo(p, etc)" and not "p->foo = etc".
1084 //
1085 // See https://en.wikipedia.org/wiki/Opaque_pointer#C
1086 //
1087 // Thus, we don't use C++'s new operator (which requires knowing the sizeof
1088 // the struct at compile time). Instead, we use sk_malloc_canfail, with
1089 // sizeof__wuffs_gif__decoder returning the appropriate value for the
1090 // (statically or dynamically) linked version of the Wuffs library.
1091 //
1092 // As a C (not C++) library, none of the Wuffs types have constructors or
1093 // destructors.
1094 //
1095 // In RAII style, we can still use std::unique_ptr with these pointers, but
1096 // we pair the pointer with sk_free instead of C++'s delete.
1097 void* decoder_raw = sk_malloc_canfail(sizeof__wuffs_gif__decoder());
1098 if (!decoder_raw) {
1099 *result = SkCodec::kInternalError;
1100 return nullptr;
1101 }
1102 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> decoder(
1103 reinterpret_cast<wuffs_gif__decoder*>(decoder_raw), &sk_free);
1104
1105 SkCodec::Result reset_result =
1106 reset_and_decode_image_config(decoder.get(), &imgcfg, &iobuf, stream.get());
1107 if (reset_result != SkCodec::kSuccess) {
1108 *result = reset_result;
1109 return nullptr;
1110 }
1111
1112 uint32_t width = imgcfg.pixcfg.width();
1113 uint32_t height = imgcfg.pixcfg.height();
1114 if ((width == 0) || (width > INT_MAX) || (height == 0) || (height > INT_MAX)) {
1115 *result = SkCodec::kInvalidInput;
1116 return nullptr;
1117 }
1118
1119 uint64_t workbuf_len = decoder->workbuf_len().max_incl;
1120 void* workbuf_ptr_raw = nullptr;
1121 if (workbuf_len) {
1122 workbuf_ptr_raw = workbuf_len <= SIZE_MAX ? sk_malloc_canfail(workbuf_len) : nullptr;
1123 if (!workbuf_ptr_raw) {
1124 *result = SkCodec::kInternalError;
1125 return nullptr;
1126 }
1127 }
1128 std::unique_ptr<uint8_t, decltype(&sk_free)> workbuf_ptr(
1129 reinterpret_cast<uint8_t*>(workbuf_ptr_raw), &sk_free);
1130
1131 SkEncodedInfo::Color color =
1132#ifdef SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2
1133 (imgcfg.pixcfg.pixel_format().repr == WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL)
1134#else
1135 (imgcfg.pixcfg.pixel_format() == WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL)
1136#endif
1137 ? SkEncodedInfo::kBGRA_Color
1138 : SkEncodedInfo::kRGBA_Color;
1139
1140 // In Skia's API, the alpha we calculate here and return is only for the
1141 // first frame.
1142 SkEncodedInfo::Alpha alpha = imgcfg.first_frame_is_opaque() ? SkEncodedInfo::kOpaque_Alpha
1143 : SkEncodedInfo::kBinary_Alpha;
1144
1145 SkEncodedInfo encodedInfo = SkEncodedInfo::Make(width, height, color, alpha, 8);
1146
1147 *result = SkCodec::kSuccess;
1148 return std::unique_ptr<SkCodec>(new SkWuffsCodec(std::move(encodedInfo), std::move(stream),
1149 std::move(decoder), std::move(workbuf_ptr),
1150 workbuf_len, imgcfg, iobuf));
1151}
1152