post apply non-scale transforms after imagefilters have run
may choose to eliminate the final matrix-filter buffer before the sprite blit, but at the moment want to defer that change to a 2nd CL.
heavily inspired by https://codereview.chromium.org/1140943004
Need these CLs to land first:
https://codereview.chromium.org/1898193005/#
https://codereview.chromium.org/1902253003/
BUG=skia:3288
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1899263002
Review URL: https://codereview.chromium.org/1899263002
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index ba5cd5c..e7b7571 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -1408,7 +1408,7 @@
enum {
kMCRecSize = 128, // most recent measurement
kMCRecCount = 32, // common depth for save/restores
- kDeviceCMSize = 136, // most recent measurement
+ kDeviceCMSize = 176, // most recent measurement
};
intptr_t fMCRecStorage[kMCRecSize * kMCRecCount / sizeof(intptr_t)];
intptr_t fDeviceCMStorage[kDeviceCMSize / sizeof(intptr_t)];
@@ -1432,6 +1432,7 @@
void doSave();
void checkForDeferredSave();
+ void internalSetMatrix(const SkMatrix&);
friend class CanvasTestingAccess; // for testing
friend class SkDrawIter; // needs setupDrawForLayerDevice()
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index 8cab6ef..43369d3 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -222,11 +222,12 @@
#endif
/**
- * Create an SkMatrixImageFilter, which transforms its input by the given matrix.
+ * Return an imagefilter which transforms its input by the given matrix.
*/
static sk_sp<SkImageFilter> MakeMatrixFilter(const SkMatrix& matrix,
- SkFilterQuality,
+ SkFilterQuality quality,
sk_sp<SkImageFilter> input);
+
#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR
static SkImageFilter* CreateMatrixFilter(const SkMatrix& matrix,
SkFilterQuality filterQuality,
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 3732a35..89c3c9e 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -195,12 +195,14 @@
SkPaint* fPaint; // may be null (in the future)
const SkMatrix* fMatrix;
SkMatrix fMatrixStorage;
+ SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
const bool fDeviceIsBitmapDevice;
DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
- bool conservativeRasterClip, bool deviceIsBitmapDevice)
+ bool conservativeRasterClip, bool deviceIsBitmapDevice, const SkMatrix& stashed)
: fNext(nullptr)
, fClip(conservativeRasterClip)
+ , fStashedMatrix(stashed)
, fDeviceIsBitmapDevice(deviceIsBitmapDevice)
{
if (nullptr != device) {
@@ -663,7 +665,8 @@
SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
- new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false);
+ new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false,
+ fMCRec->fMatrix);
fMCRec->fTopLayer = fMCRec->fLayer;
@@ -1192,6 +1195,43 @@
saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
#endif
+ SkLazyPaint lazyP;
+ SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
+ SkMatrix stashedMatrix = fMCRec->fMatrix;
+#ifndef SK_SUPPORT_LEGACY_IMAGEFILTER_CTM
+ SkMatrix remainder;
+ SkSize scale;
+ /*
+ * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
+ * but they do handle scaling. To accommodate this, we do the following:
+ *
+ * 1. Stash off the current CTM
+ * 2. Decompose the CTM into SCALE and REMAINDER
+ * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
+ * contains the REMAINDER
+ * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
+ * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
+ * of the original imagefilter, and draw that (via drawSprite)
+ * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
+ *
+ * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
+ * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
+ */
+ if (imageFilter &&
+ !stashedMatrix.isScaleTranslate() &&
+ stashedMatrix.decomposeScale(&scale, &remainder))
+ {
+ // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
+ this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
+ SkPaint* p = lazyP.set(*paint);
+ p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
+ SkFilterQuality::kLow_SkFilterQuality,
+ sk_ref_sp(imageFilter)));
+ imageFilter = p->getImageFilter();
+ paint = p;
+ }
+#endif
+
// do this before we create the layer. We don't call the public save() since
// that would invoke a possibly overridden virtual
this->internalSave();
@@ -1199,7 +1239,7 @@
fDeviceCMDirty = true;
SkIRect ir;
- if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, paint ? paint->getImageFilter() : nullptr)) {
+ if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
return;
}
@@ -1255,8 +1295,8 @@
draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
}
- DeviceCM* layer =
- new DeviceCM(device, paint, this, fConservativeRasterClip, forceSpriteOnRestore);
+ DeviceCM* layer = new DeviceCM(device, paint, this, fConservativeRasterClip,
+ forceSpriteOnRestore, stashedMatrix);
device->unref();
layer->fNext = fMCRec->fTopLayer;
@@ -1301,6 +1341,8 @@
const SkIPoint& origin = layer->fDevice->getOrigin();
this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
layer->fPaint, layer->fDeviceIsBitmapDevice);
+ // restore what we smashed in internalSaveLayer
+ fMCRec->fMatrix = layer->fStashedMatrix;
// reset this, since internalDrawDevice will have set it to true
fDeviceCMDirty = true;
delete layer;
@@ -1308,6 +1350,7 @@
// we're at the root
SkASSERT(layer == (void*)fDeviceCMStorage);
layer->~DeviceCM();
+ // no need to update fMCRec, 'cause we're killing the canvas
}
}
}
@@ -1482,19 +1525,20 @@
this->didConcat(matrix);
}
-void SkCanvas::setMatrix(const SkMatrix& matrix) {
- this->checkForDeferredSave();
+void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
fDeviceCMDirty = true;
fCachedLocalClipBoundsDirty = true;
fMCRec->fMatrix = matrix;
+}
+
+void SkCanvas::setMatrix(const SkMatrix& matrix) {
+ this->checkForDeferredSave();
+ this->internalSetMatrix(matrix);
this->didSetMatrix(matrix);
}
void SkCanvas::resetMatrix() {
- SkMatrix matrix;
-
- matrix.reset();
- this->setMatrix(matrix);
+ this->setMatrix(SkMatrix::I());
}
//////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkMatrixImageFilter.cpp b/src/core/SkMatrixImageFilter.cpp
index 6cd79f3..2e827d8 100644
--- a/src/core/SkMatrixImageFilter.cpp
+++ b/src/core/SkMatrixImageFilter.cpp
@@ -86,6 +86,7 @@
canvas->concat(matrix);
SkPaint paint;
+ paint.setAntiAlias(true);
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
paint.setFilterQuality(fFilterQuality);