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
This commit is contained in:
reed 2016-04-20 16:36:51 -07:00 committed by Commit bot
parent a814000cbb
commit 8c30a8196d
4 changed files with 61 additions and 14 deletions

View File

@ -1408,7 +1408,7 @@ private:
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 @@ private:
void doSave();
void checkForDeferredSave();
void internalSetMatrix(const SkMatrix&);
friend class CanvasTestingAccess; // for testing
friend class SkDrawIter; // needs setupDrawForLayerDevice()

View File

@ -222,11 +222,12 @@ public:
#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,

View File

@ -195,12 +195,14 @@ struct DeviceCM {
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 @@ SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
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 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
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 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
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 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
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 @@ void SkCanvas::internalRestore() {
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 @@ void SkCanvas::internalRestore() {
// 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 @@ void SkCanvas::concat(const SkMatrix& matrix) {
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());
}
//////////////////////////////////////////////////////////////////////////////

View File

@ -86,6 +86,7 @@ sk_sp<SkSpecialImage> SkMatrixImageFilter::onFilterImage(SkSpecialImage* source,
canvas->concat(matrix);
SkPaint paint;
paint.setAntiAlias(true);
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
paint.setFilterQuality(fFilterQuality);