blob: cb647487bfce8f0905e32d3170f2442dd1522e78 [file] [log] [blame]
Leon Scroggins IIIe93ec682018-10-26 09:25:51 -04001/*
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 "SkWuffsCodec.h"
9
10#include "../private/SkMalloc.h"
11#include "SkFrameHolder.h"
12#include "SkSampler.h"
13#include "wuffs-v0.2.h"
14
15#define SK_WUFFS_CODEC_BUFFER_SIZE 4096
16
17// TODO(nigeltao): use a swizzler instead of load_u32le and store_etc.
18
19static inline uint32_t load_u32le(uint8_t* p) {
20 return ((uint32_t)(p[0]) << 0) | ((uint32_t)(p[1]) << 8) | ((uint32_t)(p[2]) << 16) |
21 ((uint32_t)(p[3]) << 24);
22}
23
24static inline void store_u32le(uint8_t* p, uint32_t x) {
25 p[0] = x >> 0;
26 p[1] = x >> 8;
27 p[2] = x >> 16;
28 p[3] = x >> 24;
29}
30
31static inline void store_u32le_switched(uint8_t* p, uint32_t x) {
32 // This could probably be optimized, but in any case, we should use a
33 // swizzler.
34 p[0] = x >> 16;
35 p[1] = x >> 8;
36 p[2] = x >> 0;
37 p[3] = x >> 24;
38}
39
40static inline void store_565(uint8_t* p, uint32_t argb) {
41 uint32_t r5 = 0x1F & (argb >> ((8 - 5) + 16));
42 uint32_t g6 = 0x3F & (argb >> ((8 - 6) + 8));
43 uint32_t b5 = 0x1F & (argb >> ((8 - 5) + 0));
44 p[0] = (b5 << 0) | (g6 << 5);
45 p[1] = (g6 >> 3) | (r5 << 3);
46}
47
48static bool fill_buffer(wuffs_base__io_buffer* b, SkStream* s) {
49 b->compact();
50 size_t num_read = s->read(b->data.ptr + b->meta.wi, b->data.len - b->meta.wi);
51 b->meta.wi += num_read;
52 b->meta.closed = s->isAtEnd();
53 return num_read > 0;
54}
55
56static bool seek_buffer(wuffs_base__io_buffer* b, SkStream* s, uint64_t pos) {
57 // Try to re-position the io_buffer's meta.ri read-index first, which is
58 // cheaper than seeking in the backing SkStream.
59 if ((pos >= b->meta.pos) && (pos - b->meta.pos <= b->meta.wi)) {
60 b->meta.ri = pos - b->meta.pos;
61 return true;
62 }
63 // Seek in the backing SkStream.
64 if ((pos > SIZE_MAX) || (!s->seek(pos))) {
65 return false;
66 }
67 b->meta.wi = 0;
68 b->meta.ri = 0;
69 b->meta.pos = pos;
70 b->meta.closed = false;
71 return true;
72}
73
74static SkEncodedInfo::Alpha wuffs_blend_to_skia_alpha(wuffs_base__animation_blend w) {
75 return (w == WUFFS_BASE__ANIMATION_BLEND__OPAQUE) ? SkEncodedInfo::kOpaque_Alpha
76 : SkEncodedInfo::kUnpremul_Alpha;
77}
78
79static SkCodecAnimation::Blend wuffs_blend_to_skia_blend(wuffs_base__animation_blend w) {
80 return (w == WUFFS_BASE__ANIMATION_BLEND__SRC) ? SkCodecAnimation::Blend::kBG
81 : SkCodecAnimation::Blend::kPriorFrame;
82}
83
84static SkCodecAnimation::DisposalMethod wuffs_disposal_to_skia_disposal(
85 wuffs_base__animation_disposal w) {
86 switch (w) {
87 case WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_BACKGROUND:
88 return SkCodecAnimation::DisposalMethod::kRestoreBGColor;
89 case WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_PREVIOUS:
90 return SkCodecAnimation::DisposalMethod::kRestorePrevious;
91 default:
92 return SkCodecAnimation::DisposalMethod::kKeep;
93 }
94}
95
96// -------------------------------- Class definitions
97
98class SkWuffsCodec;
99
100class SkWuffsFrame final : public SkFrame {
101public:
102 SkWuffsFrame(wuffs_base__frame_config* fc);
103
104 SkCodec::FrameInfo frameInfo(bool fullyReceived) const;
105 uint64_t ioPosition() const;
106
107 // SkFrame overrides.
108 SkEncodedInfo::Alpha onReportedAlpha() const override;
109
110private:
111 uint64_t fIOPosition;
112 SkEncodedInfo::Alpha fReportedAlpha;
113
114 typedef SkFrame INHERITED;
115};
116
117// SkWuffsFrameHolder is a trivial indirector that forwards its calls onto a
118// SkWuffsCodec. It is a separate class as SkWuffsCodec would otherwise
119// inherit from both SkCodec and SkFrameHolder, and Skia style discourages
120// multiple inheritance (e.g. with its "typedef Foo INHERITED" convention).
121class SkWuffsFrameHolder final : public SkFrameHolder {
122public:
123 SkWuffsFrameHolder() : INHERITED() {}
124
125 void init(SkWuffsCodec* codec, int width, int height);
126
127 // SkFrameHolder overrides.
128 const SkFrame* onGetFrame(int i) const override;
129
130private:
131 const SkWuffsCodec* fCodec;
132
133 typedef SkFrameHolder INHERITED;
134};
135
136class SkWuffsCodec final : public SkCodec {
137public:
138 SkWuffsCodec(SkEncodedInfo&& encodedInfo,
139 std::unique_ptr<SkStream> stream,
140 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> dec,
141 std::unique_ptr<uint8_t, decltype(&sk_free)> pixbuf_ptr,
142 std::unique_ptr<uint8_t, decltype(&sk_free)> workbuf_ptr,
143 size_t workbuf_len,
144 wuffs_base__image_config imgcfg,
145 wuffs_base__pixel_buffer pixbuf,
146 wuffs_base__io_buffer iobuf);
147
148 const SkWuffsFrame* frame(int i) const;
149
150private:
151 // SkCodec overrides.
152 SkEncodedImageFormat onGetEncodedFormat() const override;
153 Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*) override;
154 const SkFrameHolder* getFrameHolder() const override;
155 Result onStartIncrementalDecode(const SkImageInfo& dstInfo,
156 void* dst,
157 size_t rowBytes,
158 const SkCodec::Options& options) override;
159 Result onIncrementalDecode(int* rowsDecoded) override;
160 int onGetFrameCount() override;
161 bool onGetFrameInfo(int, FrameInfo*) const override;
162 int onGetRepetitionCount() override;
163
164 void readFrames();
165 Result seekFrame(int frameIndex);
166
167 Result resetDecoder();
168 const char* decodeFrameConfig();
169 const char* decodeFrame();
170 void updateNumFullyReceivedFrames();
171
172 SkWuffsFrameHolder fFrameHolder;
173 std::unique_ptr<SkStream> fStream;
174 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> fDecoder;
175 std::unique_ptr<uint8_t, decltype(&sk_free)> fPixbufPtr;
176 std::unique_ptr<uint8_t, decltype(&sk_free)> fWorkbufPtr;
177 size_t fWorkbufLen;
178
179 const uint64_t fFirstFrameIOPosition;
180 wuffs_base__frame_config fFrameConfig;
181 wuffs_base__pixel_buffer fPixelBuffer;
182 wuffs_base__io_buffer fIOBuffer;
183
184 // Incremental decoding state.
185 SkColorType fIncrDecColorType;
186 uint8_t* fIncrDecDst;
187 bool fIncrDecHaveFrameConfig;
188 size_t fIncrDecRowBytes;
189
190 uint64_t fNumFullyReceivedFrames;
191 std::vector<SkWuffsFrame> fFrames;
192 bool fFramesComplete;
193
194 // If calling an fDecoder method returns an incomplete status, then
195 // fDecoder is suspended in a coroutine (i.e. waiting on I/O or halted on a
196 // non-recoverable error). To keep its internal proof-of-safety invariants
197 // consistent, there's only two things you can safely do with a suspended
198 // Wuffs object: resume the coroutine, or reset all state (memset to zero
199 // and start again).
200 //
201 // If fDecoderIsSuspended, and we aren't sure that we're going to resume
202 // the coroutine, then we will need to call this->resetDecoder before
203 // calling other fDecoder methods.
204 bool fDecoderIsSuspended;
205
206 uint8_t fBuffer[SK_WUFFS_CODEC_BUFFER_SIZE];
207
208 typedef SkCodec INHERITED;
209};
210
211// -------------------------------- SkWuffsFrame implementation
212
213SkWuffsFrame::SkWuffsFrame(wuffs_base__frame_config* fc)
214 : INHERITED(fc->index()),
215 fIOPosition(fc->io_position()),
216 fReportedAlpha(wuffs_blend_to_skia_alpha(fc->blend())) {
217 wuffs_base__rect_ie_u32 r = fc->bounds();
218 this->setXYWH(r.min_incl_x, r.min_incl_y, r.width(), r.height());
219 this->setDisposalMethod(wuffs_disposal_to_skia_disposal(fc->disposal()));
220 this->setDuration(fc->duration() / WUFFS_BASE__FLICKS_PER_MILLISECOND);
221 this->setBlend(wuffs_blend_to_skia_blend(fc->blend()));
222}
223
224SkCodec::FrameInfo SkWuffsFrame::frameInfo(bool fullyReceived) const {
225 return ((SkCodec::FrameInfo){
226 .fRequiredFrame = getRequiredFrame(),
227 .fDuration = getDuration(),
228 .fFullyReceived = fullyReceived,
229 .fAlphaType = hasAlpha() ? kUnpremul_SkAlphaType : kOpaque_SkAlphaType,
230 .fDisposalMethod = getDisposalMethod(),
231 });
232}
233
234uint64_t SkWuffsFrame::ioPosition() const {
235 return fIOPosition;
236}
237
238SkEncodedInfo::Alpha SkWuffsFrame::onReportedAlpha() const {
239 return fReportedAlpha;
240}
241
242// -------------------------------- SkWuffsFrameHolder implementation
243
244void SkWuffsFrameHolder::init(SkWuffsCodec* codec, int width, int height) {
245 fCodec = codec;
246 // Initialize SkFrameHolder's (the superclass) fields.
247 fScreenWidth = width;
248 fScreenHeight = height;
249}
250
251const SkFrame* SkWuffsFrameHolder::onGetFrame(int i) const {
252 return fCodec->frame(i);
253};
254
255// -------------------------------- SkWuffsCodec implementation
256
257SkWuffsCodec::SkWuffsCodec(SkEncodedInfo&& encodedInfo,
258 std::unique_ptr<SkStream> stream,
259 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> dec,
260 std::unique_ptr<uint8_t, decltype(&sk_free)> pixbuf_ptr,
261 std::unique_ptr<uint8_t, decltype(&sk_free)> workbuf_ptr,
262 size_t workbuf_len,
263 wuffs_base__image_config imgcfg,
264 wuffs_base__pixel_buffer pixbuf,
265 wuffs_base__io_buffer iobuf)
266 : INHERITED(std::move(encodedInfo),
267 skcms_PixelFormat_RGBA_8888,
268 // Pass a nullptr SkStream to the SkCodec constructor. We
269 // manage the stream ourselves, as the default SkCodec behavior
270 // is too trigger-happy on rewinding the stream.
271 nullptr),
272 fStream(std::move(stream)),
273 fDecoder(std::move(dec)),
274 fPixbufPtr(std::move(pixbuf_ptr)),
275 fWorkbufPtr(std::move(workbuf_ptr)),
276 fWorkbufLen(workbuf_len),
277 fFirstFrameIOPosition(imgcfg.first_frame_io_position()),
278 fFrameConfig((wuffs_base__frame_config){}),
279 fPixelBuffer(pixbuf),
280 fIOBuffer((wuffs_base__io_buffer){}),
281 fIncrDecColorType(kUnknown_SkColorType),
282 fIncrDecDst(nullptr),
283 fIncrDecHaveFrameConfig(false),
284 fIncrDecRowBytes(0),
285 fNumFullyReceivedFrames(0),
286 fFramesComplete(false),
287 fDecoderIsSuspended(false) {
288 fFrameHolder.init(this, imgcfg.pixcfg.width(), imgcfg.pixcfg.height());
289
290 // Initialize fIOBuffer's fields, copying any outstanding data from iobuf to
291 // fIOBuffer, as iobuf's backing array may not be valid for the lifetime of
292 // this SkWuffsCodec object, but fIOBuffer's backing array (fBuffer) is.
293 SkASSERT(iobuf.data.len == SK_WUFFS_CODEC_BUFFER_SIZE);
294 memmove(fBuffer, iobuf.data.ptr, iobuf.meta.wi);
295 fIOBuffer = ((wuffs_base__io_buffer){
296 .data = ((wuffs_base__slice_u8){
297 .ptr = fBuffer,
298 .len = SK_WUFFS_CODEC_BUFFER_SIZE,
299 }),
300 .meta = iobuf.meta,
301 });
302}
303
304const SkWuffsFrame* SkWuffsCodec::frame(int i) const {
305 if ((0 <= i) && (static_cast<size_t>(i) < fFrames.size())) {
306 return &fFrames[i];
307 }
308 return nullptr;
309}
310
311SkEncodedImageFormat SkWuffsCodec::onGetEncodedFormat() const {
312 return SkEncodedImageFormat::kGIF;
313}
314
315SkCodec::Result SkWuffsCodec::onGetPixels(const SkImageInfo& dstInfo,
316 void* dst,
317 size_t rowBytes,
318 const Options& options,
319 int* rowsDecoded) {
320 SkCodec::Result result = this->onStartIncrementalDecode(dstInfo, dst, rowBytes, options);
321 if (result != kSuccess) {
322 return result;
323 }
324 return this->onIncrementalDecode(rowsDecoded);
325}
326
327const SkFrameHolder* SkWuffsCodec::getFrameHolder() const {
328 return &fFrameHolder;
329}
330
331SkCodec::Result SkWuffsCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
332 void* dst,
333 size_t rowBytes,
334 const SkCodec::Options& options) {
335 if (options.fSubset) {
336 return SkCodec::kUnimplemented;
337 }
338 SkCodec::Result result = this->seekFrame(options.fFrameIndex);
339 if (result != SkCodec::kSuccess) {
340 return result;
341 }
342
343 SkSampler::Fill(dstInfo, dst, rowBytes, options.fZeroInitialized);
344
345 fIncrDecColorType = dstInfo.colorType();
346 fIncrDecDst = static_cast<uint8_t*>(dst);
347 fIncrDecHaveFrameConfig = false;
348 fIncrDecRowBytes = rowBytes;
349
350 return SkCodec::kSuccess;
351}
352
353SkCodec::Result SkWuffsCodec::onIncrementalDecode(int* rowsDecoded) {
354 if (!fIncrDecDst) {
355 return SkCodec::kInternalError;
356 }
357
358 if (!fIncrDecHaveFrameConfig) {
359 const char* status = this->decodeFrameConfig();
360 if (status == nullptr) {
361 // No-op.
362 } else if (status == wuffs_base__suspension__short_read) {
363 return SkCodec::kIncompleteInput;
364 } else {
365 SkCodecPrintf("decodeFrameConfig: %s", status);
366 return SkCodec::kErrorInInput;
367 }
368 fIncrDecHaveFrameConfig = true;
369 }
370
371 SkCodec::Result result = SkCodec::kSuccess;
372 const char* status = this->decodeFrame();
373 if (status == nullptr) {
374 // No-op.
375 } else if (status == wuffs_base__suspension__short_read) {
376 result = SkCodec::kIncompleteInput;
377 } else {
378 SkCodecPrintf("decodeFrame: %s", status);
379 return SkCodec::kErrorInInput;
380 }
381
382 // TODO(nigeltao): use a swizzler, once I figure out how it works. For
383 // now, a C style load/store loop gets the job done.
384 wuffs_base__rect_ie_u32 r = fFrameConfig.bounds();
385 wuffs_base__table_u8 pixels = fPixelBuffer.plane(0);
386 wuffs_base__slice_u8 palette = fPixelBuffer.palette();
387 SkASSERT(palette.len == 4 * 256);
388 switch (fIncrDecColorType) {
389 case kRGB_565_SkColorType:
390 for (uint32_t y = r.min_incl_y; y < r.max_excl_y; y++) {
391 uint8_t* d = fIncrDecDst + (y * fIncrDecRowBytes) + (r.min_incl_x * 2);
392 uint8_t* s = pixels.ptr + (y * pixels.stride) + (r.min_incl_x * 1);
393 for (uint32_t x = r.min_incl_x; x < r.max_excl_x; x++) {
394 uint8_t index = *s++;
395 uint32_t argb = load_u32le(palette.ptr + 4 * static_cast<size_t>(index));
396 store_565(d, argb);
397 d += 2;
398 }
399 }
400 break;
401 case kBGRA_8888_SkColorType:
402 for (uint32_t y = r.min_incl_y; y < r.max_excl_y; y++) {
403 uint8_t* d = fIncrDecDst + (y * fIncrDecRowBytes) + (r.min_incl_x * 4);
404 uint8_t* s = pixels.ptr + (y * pixels.stride) + (r.min_incl_x * 1);
405 for (uint32_t x = r.min_incl_x; x < r.max_excl_x; x++) {
406 uint8_t index = *s++;
407 uint32_t argb = load_u32le(palette.ptr + 4 * static_cast<size_t>(index));
408 store_u32le(d, argb);
409 d += 4;
410 }
411 }
412 break;
413 case kRGBA_8888_SkColorType:
414 for (uint32_t y = r.min_incl_y; y < r.max_excl_y; y++) {
415 uint8_t* d = fIncrDecDst + (y * fIncrDecRowBytes) + (r.min_incl_x * 4);
416 uint8_t* s = pixels.ptr + (y * pixels.stride) + (r.min_incl_x * 1);
417 for (uint32_t x = r.min_incl_x; x < r.max_excl_x; x++) {
418 uint8_t index = *s++;
419 uint32_t argb = load_u32le(palette.ptr + 4 * static_cast<size_t>(index));
420 store_u32le_switched(d, argb);
421 d += 4;
422 }
423 }
424 break;
425 default:
426 return SkCodec::kUnimplemented;
427 }
428
429 // The semantics of *rowsDecoded is: say you have a 10 pixel high image
430 // (both the frame and the image). If you only decoded the first 3 rows,
431 // set this to 3, and then SkCodec (or the caller of incrementalDecode)
432 // would zero-initialize the remaining 7 (unless the memory was already
433 // zero-initialized).
434 //
435 // Now let's say that the image is still 10 pixels high, but the frame is
436 // from row 5 to 9. If you only decoded 3 rows, but you initialized the
437 // first 5, you could return 8, and the caller would zero-initialize the
438 // final 2. For GIF (where a frame can be smaller than the image and can be
439 // interlaced), we just zero-initialize all 10 rows ahead of time and
440 // return the height of the image, so the caller knows it doesn't need to
441 // do anything.
442 if (rowsDecoded) {
443 *rowsDecoded = static_cast<int>(fPixelBuffer.pixcfg.height());
444 }
445
446 if (result == SkCodec::kSuccess) {
447 fIncrDecColorType = kUnknown_SkColorType;
448 fIncrDecDst = nullptr;
449 fIncrDecHaveFrameConfig = false;
450 fIncrDecRowBytes = 0;
451 }
452 return result;
453}
454
455int SkWuffsCodec::onGetFrameCount() {
456 if (!fFramesComplete) {
457 this->readFrames();
458 this->updateNumFullyReceivedFrames();
459 }
460 return fFrames.size();
461}
462
463bool SkWuffsCodec::onGetFrameInfo(int i, SkCodec::FrameInfo* frameInfo) const {
464 const SkWuffsFrame* f = this->frame(i);
465 if (!f) {
466 return false;
467 }
468 if (frameInfo) {
469 *frameInfo = f->frameInfo(static_cast<uint64_t>(i) < this->fNumFullyReceivedFrames);
470 }
471 return true;
472}
473
474int SkWuffsCodec::onGetRepetitionCount() {
475 // Convert from Wuffs's loop count to Skia's repeat count. Wuffs' uint32_t
476 // number is how many times to play the loop. Skia's int number is how many
477 // times to play the loop *after the first play*. Wuffs and Skia use 0 and
478 // kRepetitionCountInfinite respectively to mean loop forever.
479 uint32_t n = wuffs_gif__decoder__num_animation_loops(fDecoder.get());
480 if (n == 0) {
481 return SkCodec::kRepetitionCountInfinite;
482 }
483 n--;
484 return n < INT_MAX ? n : INT_MAX;
485}
486
487void SkWuffsCodec::readFrames() {
488 size_t n = fFrames.size();
489 int i = n ? n - 1 : 0;
490 if (this->seekFrame(i) != SkCodec::kSuccess) {
491 return;
492 }
493
494 // Iterate through the frames, converting from Wuffs'
495 // wuffs_base__frame_config type to Skia's SkWuffsFrame type.
496 for (; i < INT_MAX; i++) {
497 const char* status = this->decodeFrameConfig();
498 if (status == nullptr) {
499 // No-op.
500 } else if (status == wuffs_base__warning__end_of_data) {
501 break;
502 } else {
503 return;
504 }
505
506 if (static_cast<size_t>(i) < fFrames.size()) {
507 continue;
508 }
509 fFrames.emplace_back(&fFrameConfig);
510 SkWuffsFrame* f = &fFrames[fFrames.size() - 1];
511 fFrameHolder.setAlphaAndRequiredFrame(f);
512 }
513
514 fFramesComplete = true;
515}
516
517SkCodec::Result SkWuffsCodec::seekFrame(int frameIndex) {
518 if (fDecoderIsSuspended) {
519 SkCodec::Result res = this->resetDecoder();
520 if (res != SkCodec::kSuccess) {
521 return res;
522 }
523 }
524
525 uint64_t pos = 0;
526 if (frameIndex < 0) {
527 return SkCodec::kInternalError;
528 } else if (frameIndex == 0) {
529 pos = fFirstFrameIOPosition;
530 } else if (static_cast<size_t>(frameIndex) < fFrames.size()) {
531 pos = fFrames[frameIndex].ioPosition();
532 } else {
533 return SkCodec::kInternalError;
534 }
535
536 if (!seek_buffer(&fIOBuffer, fStream.get(), pos)) {
537 return SkCodec::kInternalError;
538 }
539 const char* status = wuffs_gif__decoder__restart_frame(fDecoder.get(), frameIndex,
540 fIOBuffer.reader_io_position());
541 if (status != nullptr) {
542 return SkCodec::kInternalError;
543 }
544 return SkCodec::kSuccess;
545}
546
547// An overview of the Wuffs decoding API:
548//
549// An animated image (such as GIF) has an image header and then N frames. The
550// image header gives e.g. the overall image's width and height. Each frame
551// consists of a frame header (e.g. frame rectangle bounds, display duration)
552// and a payload (the pixels).
553//
554// In Wuffs terminology, there is one image config and then N pairs of
555// (frame_config, frame). To decode everything (without knowing N in advance)
556// sequentially:
557// - call wuffs_gif__decoder::decode_image_config
558// - while (true) {
559// - call wuffs_gif__decoder::decode_frame_config
560// - if that returned wuffs_base__warning__end_of_data, break
561// - call wuffs_gif__decoder::decode_frame
562// - }
563//
564// The first argument to each decode_foo method is the destination struct to
565// store the decoded information.
566//
567// For random (instead of sequential) access to an image's frames, call
568// wuffs_gif__decoder::restart_frame to prepare to decode the i'th frame.
569// Essentially, it restores the state to be at the top of the while loop above.
570// The wuffs_base__io_buffer's reader position will also need to be set at the
571// right point in the source data stream. The position for the i'th frame is
572// calculated by the i'th decode_frame_config call. You can only call
573// restart_frame after decode_image_config is called, explicitly or implicitly
574// (see below), as decoding a single frame might require for-all-frames
575// information like the overall image dimensions and the global palette.
576//
577// All of those decode_xxx calls are optional. For example, if
578// decode_image_config is not called, then the first decode_frame_config call
579// will implicitly parse and verify the image header, before parsing the first
580// frame's header. Similarly, you can call only decode_frame N times, without
581// calling decode_image_config or decode_frame_config, if you already know
582// metadata like N and each frame's rectangle bounds by some other means (e.g.
583// this is a first party, statically known image).
584//
585// Specifically, starting with an unknown (but re-windable) GIF image, if you
586// want to just find N (i.e. count the number of frames), you can loop calling
587// only the decode_frame_config method and avoid calling the more expensive
588// decode_frame method. In terms of the underlying GIF image format, this will
589// skip over the LZW-encoded pixel data, avoiding the costly LZW decompression.
590//
591// Those decode_xxx methods are also suspendible. They will return early (with
592// a status code that is_suspendible and therefore isn't is_complete) if there
593// isn't enough source data to complete the operation: an incremental decode.
594// Calling decode_xxx again with additional source data will resume the
595// previous operation, instead of starting a new operation. Calling decode_yyy
596// whilst decode_xxx is suspended will result in an error.
597//
598// Once an error is encountered, whether from invalid source data or from a
599// programming error such as calling decode_yyy while suspended in decode_xxx,
600// all subsequent calls will be no-ops that return an error. To reset the
601// decoder into something that does productive work, memset the entire struct
602// to zero, check the Wuffs version and then, in order to be able to call
603// restart_frame, call decode_image_config. The io_buffer and its associated
604// stream will also need to be rewound.
605
606static SkCodec::Result reset_and_decode_image_config(wuffs_gif__decoder* decoder,
607 wuffs_base__image_config* imgcfg,
608 wuffs_base__io_buffer* b,
609 SkStream* s) {
610 memset(decoder, 0, sizeof__wuffs_gif__decoder());
611 const char* status = wuffs_gif__decoder__check_wuffs_version(
612 decoder, sizeof__wuffs_gif__decoder(), WUFFS_VERSION);
613 if (status != nullptr) {
614 SkCodecPrintf("check_wuffs_version: %s", status);
615 return SkCodec::kInternalError;
616 }
617 while (true) {
618 status = wuffs_gif__decoder__decode_image_config(decoder, imgcfg, b->reader());
619 if (status == nullptr) {
620 return SkCodec::kSuccess;
621 } else if (status != wuffs_base__suspension__short_read) {
622 SkCodecPrintf("decode_image_config: %s", status);
623 return SkCodec::kErrorInInput;
624 } else if (!fill_buffer(b, s)) {
625 return SkCodec::kIncompleteInput;
626 }
627 }
628}
629
630SkCodec::Result SkWuffsCodec::resetDecoder() {
631 if (!fStream->rewind()) {
632 return SkCodec::kInternalError;
633 }
634 fIOBuffer.meta = ((wuffs_base__io_buffer_meta){});
635
636 SkCodec::Result result =
637 reset_and_decode_image_config(fDecoder.get(), nullptr, &fIOBuffer, fStream.get());
638 if (result == SkCodec::kIncompleteInput) {
639 return SkCodec::kInternalError;
640 } else if (result != SkCodec::kSuccess) {
641 return result;
642 }
643
644 fDecoderIsSuspended = false;
645 return SkCodec::kSuccess;
646}
647
648const char* SkWuffsCodec::decodeFrameConfig() {
649 while (true) {
650 const char* status = wuffs_gif__decoder__decode_frame_config(fDecoder.get(), &fFrameConfig,
651 fIOBuffer.reader());
652 if ((status == wuffs_base__suspension__short_read) &&
653 fill_buffer(&fIOBuffer, fStream.get())) {
654 continue;
655 }
656 fDecoderIsSuspended = !wuffs_base__status__is_complete(status);
657 this->updateNumFullyReceivedFrames();
658 return status;
659 }
660}
661
662const char* SkWuffsCodec::decodeFrame() {
663 while (true) {
664 const char* status =
665 wuffs_gif__decoder__decode_frame(fDecoder.get(), &fPixelBuffer, fIOBuffer.reader(),
666 ((wuffs_base__slice_u8){
667 .ptr = fWorkbufPtr.get(),
668 .len = fWorkbufLen,
669 }),
670 NULL);
671 if ((status == wuffs_base__suspension__short_read) &&
672 fill_buffer(&fIOBuffer, fStream.get())) {
673 continue;
674 }
675 fDecoderIsSuspended = !wuffs_base__status__is_complete(status);
676 this->updateNumFullyReceivedFrames();
677 return status;
678 }
679}
680
681void SkWuffsCodec::updateNumFullyReceivedFrames() {
682 // wuffs_gif__decoder__num_decoded_frames's return value, n, can change
683 // over time, both up and down, as we seek back and forth in the underlying
684 // stream. fNumFullyReceivedFrames is the highest n we've seen.
685 uint64_t n = wuffs_gif__decoder__num_decoded_frames(fDecoder.get());
686 if (fNumFullyReceivedFrames < n) {
687 fNumFullyReceivedFrames = n;
688 }
689}
690
691// -------------------------------- SkWuffsCodec.h functions
692
693bool SkWuffsCodec_IsFormat(const void* buf, size_t bytesRead) {
694 constexpr const char* gif_ptr = "GIF8";
695 constexpr size_t gif_len = 4;
696 return (bytesRead >= gif_len) && (memcmp(buf, gif_ptr, gif_len) == 0);
697}
698
699std::unique_ptr<SkCodec> SkWuffsCodec_MakeFromStream(std::unique_ptr<SkStream> stream,
700 SkCodec::Result* result) {
701 uint8_t buffer[SK_WUFFS_CODEC_BUFFER_SIZE];
702 wuffs_base__io_buffer iobuf = ((wuffs_base__io_buffer){
703 .data = ((wuffs_base__slice_u8){
704 .ptr = buffer,
705 .len = SK_WUFFS_CODEC_BUFFER_SIZE,
706 }),
707 .meta = ((wuffs_base__io_buffer_meta){}),
708 });
709 wuffs_base__image_config imgcfg = ((wuffs_base__image_config){});
710
711 // Wuffs is primarily a C library, not a C++ one. Furthermore, outside of
712 // the wuffs_base__etc types, the sizeof a file format specific type like
713 // GIF's wuffs_gif__decoder can vary between Wuffs versions. If p is of
714 // type wuffs_gif__decoder*, then the supported API treats p as a pointer
715 // to an opaque type: a private implementation detail. The API is always
716 // "set_foo(p, etc)" and not "p->foo = etc".
717 //
718 // See https://en.wikipedia.org/wiki/Opaque_pointer#C
719 //
720 // Thus, we don't use C++'s new operator (which requires knowing the sizeof
721 // the struct at compile time). Instead, we use sk_malloc_canfail, with
722 // sizeof__wuffs_gif__decoder returning the appropriate value for the
723 // (statically or dynamically) linked version of the Wuffs library.
724 //
725 // As a C (not C++) library, none of the Wuffs types have constructors or
726 // destructors.
727 //
728 // In RAII style, we can still use std::unique_ptr with these pointers, but
729 // we pair the pointer with sk_free instead of C++'s delete.
730 void* decoder_raw = sk_malloc_canfail(sizeof__wuffs_gif__decoder());
731 if (!decoder_raw) {
732 *result = SkCodec::kInternalError;
733 return nullptr;
734 }
735 std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> decoder(
736 reinterpret_cast<wuffs_gif__decoder*>(decoder_raw), &sk_free);
737
738 SkCodec::Result reset_result =
739 reset_and_decode_image_config(decoder.get(), &imgcfg, &iobuf, stream.get());
740 if (reset_result != SkCodec::kSuccess) {
741 *result = reset_result;
742 return nullptr;
743 }
744
745 uint32_t width = imgcfg.pixcfg.width();
746 uint32_t height = imgcfg.pixcfg.height();
747 if ((width == 0) || (width > INT_MAX) || (height == 0) || (height > INT_MAX)) {
748 *result = SkCodec::kInvalidInput;
749 return nullptr;
750 }
751
752 uint64_t workbuf_len = imgcfg.workbuf_len().max_incl;
753 void* workbuf_ptr_raw = workbuf_len <= SIZE_MAX ? sk_malloc_canfail(workbuf_len) : nullptr;
754 if (!workbuf_ptr_raw) {
755 *result = SkCodec::kInternalError;
756 return nullptr;
757 }
758 std::unique_ptr<uint8_t, decltype(&sk_free)> workbuf_ptr(
759 reinterpret_cast<uint8_t*>(workbuf_ptr_raw), &sk_free);
760
761 uint64_t pixbuf_len = imgcfg.pixcfg.pixbuf_len();
762 void* pixbuf_ptr_raw = pixbuf_len <= SIZE_MAX ? sk_malloc_canfail(pixbuf_len) : nullptr;
763 if (!pixbuf_ptr_raw) {
764 *result = SkCodec::kInternalError;
765 return nullptr;
766 }
767 std::unique_ptr<uint8_t, decltype(&sk_free)> pixbuf_ptr(
768 reinterpret_cast<uint8_t*>(pixbuf_ptr_raw), &sk_free);
769 wuffs_base__pixel_buffer pixbuf = ((wuffs_base__pixel_buffer){});
770
771 const char* status = pixbuf.set_from_slice(&imgcfg.pixcfg, ((wuffs_base__slice_u8){
772 .ptr = pixbuf_ptr.get(),
773 .len = pixbuf_len,
774 }));
775 if (status != nullptr) {
776 SkCodecPrintf("set_from_slice: %s", status);
777 *result = SkCodec::kInternalError;
778 return nullptr;
779 }
780
781 // In Skia's API, the alpha we calculate here and return is only for the
782 // first frame.
783 SkEncodedInfo::Alpha alpha = imgcfg.first_frame_is_opaque() ? SkEncodedInfo::kOpaque_Alpha
784 : SkEncodedInfo::kBinary_Alpha;
785
786 SkEncodedInfo encodedInfo =
787 SkEncodedInfo::Make(width, height, SkEncodedInfo::kPalette_Color, alpha, 8);
788
789 *result = SkCodec::kSuccess;
790 return std::unique_ptr<SkCodec>(new SkWuffsCodec(
791 std::move(encodedInfo), std::move(stream), std::move(decoder), std::move(pixbuf_ptr),
792 std::move(workbuf_ptr), workbuf_len, imgcfg, pixbuf, iobuf));
793}