Apply clip in device space for onDrawBehind().

The back image for saveBehind was in the device's space, but clipping
when drawing to that same region used clipRegion(), which is affected
by the global-to-device matrix. This change switches to using clipRect()
and a temporary whack of the local-to-device matrix to identity so that
we can exactly specify the same rectangle in the device space.

This is important once image filters start changing the global-to-device
matrix to no longer be integer-translations.

Change-Id: I40b37700b9cbf787e81931fdfe109968452533e1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/341158
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Derek Sollenberger <djsollen@google.com>
This commit is contained in:
Michael Ludwig 2020-12-07 19:55:04 -05:00 committed by Skia Commit-Bot
parent e5d9bbfa5b
commit 8685c31dd1

View File

@ -2274,6 +2274,11 @@ void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
}
void SkCanvas::onDrawBehind(const SkPaint& paint) {
SkBaseDevice* dev = this->getTopDevice();
if (!dev) {
return;
}
SkIRect bounds;
SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart);
for (;;) {
@ -2282,6 +2287,9 @@ void SkCanvas::onDrawBehind(const SkPaint& paint) {
return; // no backimages, so nothing to draw
}
if (rec->fBackImage) {
// drawBehind should only have been called when the saveBehind record is active;
// if this fails, it means a real saveLayer was made w/o being restored first.
SkASSERT(dev == rec->fTopLayer->fDevice.get());
bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY,
rec->fBackImage->fImage->width(),
rec->fBackImage->fImage->height());
@ -2289,22 +2297,27 @@ void SkCanvas::onDrawBehind(const SkPaint& paint) {
}
}
// The backimage location (and thus bounds) were defined in the device's space, so mark it
// as a clip. We use a clip instead of just drawing a rect in case the paint has an image
// filter on it (which is applied before any auto-layer so the filter is clipped).
dev->save();
{
// We also have to temporarily whack the device matrix since clipRegion is affected by the
// global-to-device matrix and clipRect is affected by the local-to-device.
SkAutoDeviceTransformRestore adtr(dev, SkMatrix::I());
dev->clipRect(SkRect::Make(bounds), SkClipOp::kIntersect, /* aa */ false);
// ~adtr will reset the local-to-device matrix so that drawPaint() shades correctly.
}
DRAW_BEGIN(paint, nullptr)
while (iter.next()) {
SkBaseDevice* dev = iter.fDevice;
dev->save();
// We use clipRegion because it is already defined to operate in dev-space
// (i.e. ignores the ctm). However, it is going to first translate by -origin,
// but we don't want that, so we undo that before calling in.
SkRegion rgn(bounds.makeOffset(dev->getOrigin()));
dev->clipRegion(rgn, SkClipOp::kIntersect);
dev->drawPaint(draw.paint());
dev->restore(fMCRec->fMatrix);
iter.fDevice->drawPaint(draw.paint());
}
DRAW_END
dev->restore(fMCRec->fMatrix);
}
void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {