"Ensure frame rect is on screen or empty" - take 2
Revert commit e13b82fac077383d9f93631a177573509be44f38
(I215b312b421a30bf985f681424a4c4198f069862), and fix a different way.
The prior fix resulted in incorrectly decoding a frame whose bounds
extended beyond the canvas. Instead of changing the internal frame rect,
just change the one passed to the client.
Change-Id: I82568cb672f1027a9dc17ec0a15d355d561c356d
Reviewed-on: https://skia-review.googlesource.com/c/libgifcodec/+/353445
Reviewed-by: Derek Sollenberger <djsollen@google.com>
Commit-Queue: Leon Scroggins <scroggo@google.com>
diff --git a/SkGifImageReader.cpp b/SkGifImageReader.cpp
index 6d37d59..f0184cb 100644
--- a/SkGifImageReader.cpp
+++ b/SkGifImageReader.cpp
@@ -767,12 +767,6 @@
if (currentFrameIsFirstFrame()) {
fScreenHeight = std::max(fScreenHeight, yOffset + height);
fScreenWidth = std::max(fScreenWidth, xOffset + width);
- } else {
- // If a non-first frame is offscreen, it will have no effect on
- // the output image. Modify its offsets to be consistent with
- // the Wuffs implementation.
- if (xOffset > fScreenWidth) xOffset = fScreenWidth;
- if (yOffset > fScreenHeight) yOffset = fScreenHeight;
}
// NOTE: Chromium placed this block after setHeaderDefined, down
@@ -820,8 +814,7 @@
return SkCodec::kSuccess;
}
- width = std::min(width, fScreenWidth - xOffset);
- height = std::min(height, fScreenHeight - yOffset);
+
currentFrame->setXYWH(xOffset, yOffset, width, height);
currentFrame->setInterlaced(SkToBool(currentComponent[8] & 0x40));
diff --git a/SkLibGifCodec.cpp b/SkLibGifCodec.cpp
index b76c18f..95f5bf1 100644
--- a/SkLibGifCodec.cpp
+++ b/SkLibGifCodec.cpp
@@ -130,6 +130,18 @@
if (frameInfo) {
frameContext->fillIn(frameInfo, frameContext->isComplete());
+
+ auto* rect = &frameInfo->fFrameRect;
+ auto bounds = this->bounds();
+ if (!rect->intersect(bounds)) {
+ // If a frame is offscreen, it will have no effect on the output
+ // image. Modify its bounds to be consistent with the Wuffs
+ // implementation.
+ rect->setLTRB(std::min(rect->left(), bounds.right()),
+ std::min(rect->top(), bounds.bottom()),
+ std::min(rect->right(), bounds.right()),
+ std::min(rect->bottom(), bounds.bottom()));
+ }
}
return true;
}
@@ -225,9 +237,12 @@
// This is only called by prepareToDecode, which ensures frameIndex is in range.
SkASSERT(frame);
- // Swizzler::Make only reads left and right of this rect.
- SkIRect swizzleRect = frame->frameRect();
- SkASSERT(swizzleRect.right() <= fReader->screenWidth());
+ const int xBegin = frame->xOffset();
+ const int xEnd = std::min(frame->frameRect().right(), fReader->screenWidth());
+
+ // CreateSwizzler only reads left and right of the frame. We cannot use the frame's raw
+ // frameRect, since it might extend beyond the edge of the frame.
+ SkIRect swizzleRect = SkIRect::MakeLTRB(xBegin, 0, xEnd, 0);
SkImageInfo swizzlerInfo = dstInfo;
if (this->colorXform()) {
@@ -404,15 +419,15 @@
const SkGIFFrameContext* frameContext = fReader->frameContext(frameIndex);
// The pixel data and coordinates supplied to us are relative to the frame's
// origin within the entire image size, i.e.
- // (frameContext->xOffset, frameContext->yOffset).
+ // (frameContext->xOffset, frameContext->yOffset). There is no guarantee
+ // that width == (size().width() - frameContext->xOffset), so
+ // we must ensure we don't run off the end of either the source data or the
+ // row's X-coordinates.
const int width = frameContext->width();
const int xBegin = frameContext->xOffset();
const int yBegin = frameContext->yOffset() + rowNumber;
- const int xEnd = xBegin + width;
+ const int xEnd = std::min(xBegin + width, this->dimensions().width());
const int yEnd = std::min(yBegin + repeatCount, this->dimensions().height());
-
- SkASSERT(xEnd <= this->dimensions().width());
-
// FIXME: No need to make the checks on width/xBegin/xEnd for every row. We could instead do
// this once in prepareToDecode.
if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= yBegin))