"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))