SkWuffsCodec: swizzle only the dirty rect
This should result in a little less work, since the dirty rect should be
a subset of the frame rect. More importantly, the dirty rect will be
empty until we've fully read the color table. For incomplete data, we
therefore shouldn't read an uninitialized color table.
This also lets us convert from the Wuffs palette to the Skia palette
(fColorTable) once (per frame), not once per onIncrementalDecode call.
Again, this means we do a little less work.
Bug: oss-fuzz:11780
Change-Id: I1991778ca19f0525a0fefa57448e2dd015ea38fb
Reviewed-on: https://skia-review.googlesource.com/c/175900
Commit-Queue: Leon Scroggins <scroggo@google.com>
Reviewed-by: Leon Scroggins <scroggo@google.com>
diff --git a/src/codec/SkWuffsCodec.cpp b/src/codec/SkWuffsCodec.cpp
index fe859b7..4cdbf63 100644
--- a/src/codec/SkWuffsCodec.cpp
+++ b/src/codec/SkWuffsCodec.cpp
@@ -202,6 +202,7 @@
std::unique_ptr<SkSwizzler> fSwizzler;
SkPMColor fColorTable[256];
+ bool fColorTableFilled;
uint64_t fNumFullyReceivedFrames;
std::vector<SkWuffsFrame> fFrames;
@@ -320,6 +321,7 @@
fIncrDecDst(nullptr),
fIncrDecRowBytes(0),
fSwizzler(nullptr),
+ fColorTableFilled(false),
fNumFullyReceivedFrames(0),
fFramesComplete(false),
fDecoderIsSuspended(false) {
@@ -381,6 +383,7 @@
fSpySampler.reset();
fSwizzler = nullptr;
+ fColorTableFilled = false;
const char* status = this->decodeFrameConfig();
if (status == nullptr) {
@@ -425,9 +428,10 @@
wuffs_base__table_u8 pixels = fPixelBuffer.plane(0);
int scaledHeight = dstInfo().height();
const bool independent = independent_frame(this, options().fFrameIndex);
- wuffs_base__rect_ie_u32 r = fFrameConfig.bounds();
+ wuffs_base__rect_ie_u32 frame_rect = fFrameConfig.bounds();
if (!fSwizzler) {
- auto bounds = SkIRect::MakeLTRB(r.min_incl_x, r.min_incl_y, r.max_excl_x, r.max_excl_y);
+ auto bounds = SkIRect::MakeLTRB(frame_rect.min_incl_x, frame_rect.min_incl_y,
+ frame_rect.max_excl_x, frame_rect.max_excl_y);
fSwizzler = SkSwizzler::Make(this->getEncodedInfo(), fColorTable, dstInfo(),
this->options(), &bounds);
fSwizzler->setSampleX(fSpySampler.sampleX());
@@ -437,9 +441,9 @@
// Zero-initialize wuffs' buffer covering the frame rect. This will later be used to
// determine how we write to the output, even if the image was incomplete. This ensures
// that we do not swizzle uninitialized memory.
- for (uint32_t y = r.min_incl_y; y < r.max_excl_y; y++) {
- uint8_t* s = pixels.ptr + (y * pixels.stride) + (r.min_incl_x * src_bpp);
- sk_bzero(s, r.width() * src_bpp);
+ for (uint32_t y = frame_rect.min_incl_y; y < frame_rect.max_excl_y; y++) {
+ uint8_t* s = pixels.ptr + (y * pixels.stride) + (frame_rect.min_incl_x * src_bpp);
+ sk_bzero(s, frame_rect.width() * src_bpp);
}
// If the frame rect does not fill the output, ensure that those pixels are not
@@ -489,14 +493,18 @@
}
}
- // If the frame rect is empty, no need to swizzle.
- if (!r.is_empty()) {
- wuffs_base__slice_u8 palette = fPixelBuffer.palette();
- SkASSERT(palette.len == 4 * 256);
- auto proc = choose_pack_color_proc(false, dstInfo().colorType());
- for (int i = 0; i < 256; i++) {
- uint8_t* p = palette.ptr + 4 * i;
- fColorTable[i] = proc(p[3], p[2], p[1], p[0]);
+ // If the frame's dirty rect is empty, no need to swizzle.
+ wuffs_base__rect_ie_u32 dirty_rect = wuffs_gif__decoder__frame_dirty_rect(fDecoder.get());
+ if (!dirty_rect.is_empty()) {
+ if (!fColorTableFilled) {
+ fColorTableFilled = true;
+ wuffs_base__slice_u8 palette = fPixelBuffer.palette();
+ SkASSERT(palette.len == 4 * 256);
+ auto proc = choose_pack_color_proc(false, dstInfo().colorType());
+ for (int i = 0; i < 256; i++) {
+ uint8_t* p = palette.ptr + 4 * i;
+ fColorTable[i] = proc(p[3], p[2], p[1], p[0]);
+ }
}
std::unique_ptr<uint8_t[]> tmpBuffer;
@@ -504,7 +512,7 @@
tmpBuffer.reset(new uint8_t[dstInfo().minRowBytes()]);
}
const int sampleY = fSwizzler->sampleY();
- for (uint32_t y = r.min_incl_y; y < r.max_excl_y; y++) {
+ for (uint32_t y = dirty_rect.min_incl_y; y < dirty_rect.max_excl_y; y++) {
int dstY = y;
if (sampleY != 1) {
if (!fSwizzler->rowNeeded(y)) {
@@ -516,8 +524,8 @@
}
}
- // We don't adjust d by (r.min_incl_x * dst_bpp) as we have already
- // accounted for that in swizzleRect, above.
+ // We don't adjust d by (frame_rect.min_incl_x * dst_bpp) as we
+ // have already accounted for that in swizzleRect, above.
uint8_t* d = fIncrDecDst + (dstY * fIncrDecRowBytes);
// The Wuffs model is that the dst buffer is the image, not the frame.
@@ -525,8 +533,12 @@
// for the N frames, regardless of each frame's top-left co-ordinate.
//
// To get from the start (in the X-direction) of the image to the start
- // of the frame, we adjust s by (r.min_incl_x * src_bpp).
- uint8_t* s = pixels.ptr + (y * pixels.stride) + (r.min_incl_x * src_bpp);
+ // of the frame, we adjust s by (frame_rect.min_incl_x * src_bpp).
+ //
+ // We adjust (in the X-direction) by the frame rect, not the dirty
+ // rect, because the swizzler (which operates on rows) was
+ // configured with the frame rect's X range.
+ uint8_t* s = pixels.ptr + (y * pixels.stride) + (frame_rect.min_incl_x * src_bpp);
if (independent) {
fSwizzler->swizzle(d, s);
} else {
@@ -545,6 +557,7 @@
fIncrDecDst = nullptr;
fIncrDecRowBytes = 0;
fSwizzler = nullptr;
+ fColorTableFilled = false;
} else {
// Make fSpySampler return whatever fSwizzler would have for fillWidth.
fSpySampler.fFillWidth = fSwizzler->fillWidth();