Fix saveLayer() with a pixel-moving filter vs SkBBoxHierarchyRecord / SkRecordDraw
In SkBBoxHierarchyRecord:
Since the bounds we pass to saveLayer are in the pre-filtering
coordinate space, they aren't correct for determining the actual
device pixels touched by the saveLayer in this case.
The easiest fix for now is to pass the clip bounds, since the final
draw done in restore() will never draw outside the clip.
In SkRecordDraw:
We do adjust the bounds passed to saveLayer, so we just need to make
sure that when we're using a paint that may affect transparent black,
we ignore the calculated bounds of draw ops and use the clip intersected
with those adjusted bounds.
See originally crrev.com/497773002
BUG=skia:
R=reed@google.com, senorblanco@chromium.org, junov@chromium.org, mtklein@google.com
Author: mtklein@chromium.org
Review URL: https://codereview.chromium.org/496963003
diff --git a/src/core/SkBBoxHierarchyRecord.cpp b/src/core/SkBBoxHierarchyRecord.cpp
index 1868e65..8cdd1d9 100644
--- a/src/core/SkBBoxHierarchyRecord.cpp
+++ b/src/core/SkBBoxHierarchyRecord.cpp
@@ -41,14 +41,9 @@
(NULL != paint->getColorFilter()));
SkRect drawBounds;
if (paintAffectsTransparentBlack) {
- if (bounds) {
- drawBounds = *bounds;
- this->getTotalMatrix().mapRect(&drawBounds);
- } else {
- SkIRect deviceBounds;
- this->getClipDeviceBounds(&deviceBounds);
- drawBounds.set(deviceBounds);
- }
+ SkIRect deviceBounds;
+ this->getClipDeviceBounds(&deviceBounds);
+ drawBounds.set(deviceBounds);
}
fStateTree->appendSaveLayer(this->writeStream().bytesWritten());
SkCanvas::SaveLayerStrategy strategy = this->INHERITED::willSaveLayer(bounds, paint, flags);
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp
index a14b031..5dd2ba3 100644
--- a/src/core/SkRecordDraw.cpp
+++ b/src/core/SkRecordDraw.cpp
@@ -204,19 +204,29 @@
this->pushControl();
}
+ static bool PaintMayAffectTransparentBlack(const SkPaint* paint) {
+ // FIXME: this is very conservative
+ return paint && (paint->getImageFilter() || paint->getColorFilter());
+ }
+
SkIRect popSaveBlock() {
// We're done the Save block. Apply the block's bounds to all control ops inside it.
SaveBounds sb;
fSaveStack.pop(&sb);
+
+ // If the paint affects transparent black, we can't trust any of our calculated bounds.
+ const SkIRect& bounds =
+ PaintMayAffectTransparentBlack(sb.paint) ? fCurrentClipBounds : sb.bounds;
+
while (sb.controlOps --> 0) {
- this->popControl(sb.bounds);
+ this->popControl(bounds);
}
// This whole Save block may be part another Save block.
- this->updateSaveBounds(sb.bounds);
+ this->updateSaveBounds(bounds);
// If called from a real Restore (not a phony one for balance), it'll need the bounds.
- return sb.bounds;
+ return bounds;
}
void pushControl() {