Initial clip-mask-layer support
authorFlorin Malita <fmalita@chromium.org>
Fri, 28 Apr 2017 17:48:37 +0000 (13:48 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Mon, 1 May 2017 14:07:10 +0000 (14:07 +0000)
SkBitmapDevice-only implementation.

Will add A8 fast path specializations in a follow-up.

Change-Id: I2ccb1ffba3689e92ac90a23e94737471dfb121a1
BUG=skia:6005

Change-Id: I2ccb1ffba3689e92ac90a23e94737471dfb121a1
Reviewed-on: https://skia-review.googlesource.com/14183
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>

30 files changed:
gm/imagefilters.cpp
gm/savelayer.cpp
include/core/SkCanvas.h
include/core/SkPicture.h
include/private/SkRecords.h
samplecode/SampleLayers.cpp
src/core/SkBitmapDevice.cpp
src/core/SkBitmapDevice.h
src/core/SkCanvas.cpp
src/core/SkColorSpaceXformCanvas.cpp
src/core/SkDevice.cpp
src/core/SkDevice.h
src/core/SkDraw.h
src/core/SkLiteDL.cpp
src/core/SkLiteDL.h
src/core/SkLiteRecorder.cpp
src/core/SkPictureFlat.h
src/core/SkPicturePlayback.cpp
src/core/SkPictureRecord.cpp
src/core/SkRecordDraw.cpp
src/core/SkRecordOpts.cpp
src/core/SkRecorder.cpp
src/gpu/SkGpuDevice.cpp
src/gpu/SkGpuDevice.h
src/pdf/SkPDFDevice.cpp
src/pdf/SkPDFDevice.h
src/pipe/SkPipeCanvas.cpp
src/pipe/SkPipeFormat.h
src/pipe/SkPipeReader.cpp
tests/RecordOptsTest.cpp

index 8855afd..dd6287b 100644 (file)
@@ -123,7 +123,7 @@ static void draw_set(SkCanvas* canvas, sk_sp<SkImageFilter> filters[], int count
         canvas->save();
         SkRRect rr = SkRRect::MakeRectXY(r.makeOffset(dx, dy), 20, 20);
         canvas->clipRRect(rr, true);
-        canvas->saveLayer({ &rr.getBounds(), nullptr, filters[i].get(), 0 });
+        canvas->saveLayer({ &rr.getBounds(), nullptr, filters[i].get(), nullptr, nullptr, 0 });
         canvas->drawColor(0x40FFFFFF);
         canvas->restore();
         canvas->restore();
index 98a00ca..245785e 100644 (file)
@@ -8,7 +8,6 @@
 #include "gm.h"
 #include "sk_tool_utils.h"
 
-
 static const uint32_t SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag = 1U << 31;
 
 // This GM tests out the deprecated Android-specific unclipped saveLayer "feature".
@@ -17,7 +16,8 @@ static const uint32_t SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag = 1U << 31;
 static void save_layer_unclipped(SkCanvas* canvas,
                                  SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
     SkRect rect = SkRect::MakeLTRB(l, t, r, b);
-    canvas->saveLayer({ &rect, nullptr, nullptr, SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag });
+    canvas->saveLayer({ &rect, nullptr, nullptr, nullptr, nullptr,
+                        SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag });
 }
 
 static void do_draw(SkCanvas* canvas) {
@@ -94,8 +94,8 @@ DEF_SIMPLE_GM(picture_savelayer, canvas, 320, 640) {
     for(int i = 1; i < 2; ++i) {
         canvas->translate(100 * i, 0);
         auto flag = i ? SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag : 0;
-        canvas->saveLayer({ &rect1, &paint1, nullptr, flag});
-        canvas->saveLayer({ &rect2, &paint2, nullptr, flag});
+        canvas->saveLayer({ &rect1, &paint1, nullptr, nullptr, nullptr, flag});
+        canvas->saveLayer({ &rect2, &paint2, nullptr, nullptr, nullptr, flag});
         canvas->drawRect(rect3, paint3);
         canvas->restore();
         canvas->restore();
@@ -122,3 +122,118 @@ DEF_SIMPLE_GM(savelayer_initfromprev, canvas, 256, 256) {
     canvas->restore();
 };
 
+#include "SkBlurImageFilter.h"
+#include "SkGradientShader.h"
+#include "SkPicture.h"
+#include "SkPictureRecorder.h"
+#include "SkSurface.h"
+
+static void draw_mask(SkCanvas* canvas, int size) {
+    const SkScalar cx = size * SK_ScalarHalf,
+                   cy = cx;
+    const SkColor colors[] = { 0x00000000, 0xffff0000, 0x00000000, 0xffff0000, 0x00000000,
+                               0xffff0000, 0x00000000, 0xffff0000, 0x00000000 };
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setShader(SkGradientShader::MakeSweep(cx, cy, colors, nullptr, SK_ARRAY_COUNT(colors)));
+    canvas->drawPaint(paint);
+
+    paint.setShader(SkGradientShader::MakeRadial({cx, cy}, size / 4, colors, nullptr, 2,
+                                                 SkShader::kClamp_TileMode));
+    canvas->drawCircle(cx, cy, size / 4, paint);
+}
+
+DEF_SIMPLE_GM(savelayer_clipmask, canvas, 1200, 1200) {
+    static constexpr int kSize = 100;
+    static constexpr SkRect kLayerBounds = { kSize * 0.25f, kSize * 0.25f,
+                                             kSize * 0.75f, kSize * 0.75f };
+    static constexpr struct {
+        const SkRect* bounds;
+        const SkScalar matrix[9];
+    } kConfigs[] = {
+        { nullptr, { 1     ,  0     ,   0,   0     , 1     ,   0,   0, 0, 1 } },
+        { nullptr, { 2     ,  0     ,   0,   0     , 2     ,   0,   0, 0, 1 } },
+        { nullptr, { 2     ,  0     , -50,   0     , 2     , -50,   0, 0, 1 } },
+        { nullptr, { 0.707f, -0.707f,  50,   0.707f, 0.707f, -20,   0, 0, 1 } },
+        { nullptr, { 0.5f  ,  0     ,  25,   0     , 0.5f  ,  25,   0, 0, 1 } },
+
+        { &kLayerBounds, { 1     ,  0     ,   0,   0     , 1     ,   0,   0, 0, 1 } },
+        { &kLayerBounds, { 2     ,  0     ,   0,   0     , 2     ,   0,   0, 0, 1 } },
+        { &kLayerBounds, { 2     ,  0     , -50,   0     , 2     , -50,   0, 0, 1 } },
+        { &kLayerBounds, { 0.707f, -0.707f,  50,   0.707f, 0.707f, -20,   0, 0, 1 } },
+        { &kLayerBounds, { 0.5f  ,  0     ,  25,   0     , 0.5f  ,  25,   0, 0, 1 } },
+    };
+
+    using MaskMakerFunc = sk_sp<SkImage> (*)(int size);
+    static const MaskMakerFunc kMaskMakers[] = {
+        [](int size) -> sk_sp<SkImage> {
+            auto surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(size, size));
+            draw_mask(surf->getCanvas(), size);
+            return surf->makeImageSnapshot();
+        },
+
+        [](int size) -> sk_sp<SkImage> {
+            auto surf = SkSurface::MakeRasterN32Premul(size, size);
+            draw_mask(surf->getCanvas(), size);
+            return surf->makeImageSnapshot();
+        },
+
+        [](int size) -> sk_sp<SkImage> {
+            SkPictureRecorder recorder;
+            draw_mask(recorder.beginRecording(size, size), size);
+            return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(),
+                                            SkISize::Make(size, size),
+                                            nullptr, nullptr,
+                                            SkImage::BitDepth::kU8,
+                                            SkColorSpace::MakeSRGB());
+        }
+    };
+
+    using PaintMakerFunc = SkPaint (*)();
+    static const PaintMakerFunc kPaintMakers[] = {
+        []() -> SkPaint { return SkPaint(); },
+        []() -> SkPaint {
+            SkPaint p; p.setImageFilter(SkBlurImageFilter::Make(2, 2, nullptr)); return p;
+        },
+        []() -> SkPaint { SkPaint p; p.setBlendMode(SkBlendMode::kSrcOut); return p; },
+    };
+
+    canvas->drawColor(0xffcccccc);
+
+    SkMatrix clipMatrix;
+    SkCanvas::SaveLayerRec rec;
+    rec.fClipMatrix = &clipMatrix;
+
+    for (const auto& paintMaker : kPaintMakers) {
+        auto layerPaint = paintMaker();
+        rec.fPaint = &layerPaint;
+
+        for (const auto& maskMaker : kMaskMakers) {
+            maskMaker(kSize);
+            rec.fClipMask = maskMaker(kSize);
+
+            canvas->save();
+            for (const auto cfg : kConfigs) {
+                rec.fBounds = cfg.bounds;
+                clipMatrix.set9(cfg.matrix);
+                canvas->saveLayer(rec);
+
+                SkPaint paint;
+                paint.setColor(0xff0000ff);
+                canvas->drawRect(SkRect::MakeWH(50, 50), paint);
+                paint.setColor(0xffff0000);
+                canvas->drawRect(SkRect::MakeXYWH(50, 0, 50, 50), paint);
+                paint.setColor(0xff00ff00);
+                canvas->drawRect(SkRect::MakeXYWH(50, 50, 50, 50), paint);
+                paint.setColor(0xffffff00);
+                canvas->drawRect(SkRect::MakeXYWH(0, 50, 50, 50), paint);
+
+                canvas->restore();
+                canvas->translate(120, 0);
+            }
+            canvas->restore();
+            canvas->translate(0, 120);
+        }
+    }
+}
index 43cf4f7..d2f75fe 100644 (file)
@@ -323,27 +323,29 @@ public:
     typedef uint32_t SaveLayerFlags;
 
     struct SaveLayerRec {
-        SaveLayerRec()
-            : fBounds(nullptr), fPaint(nullptr), fBackdrop(nullptr), fSaveLayerFlags(0)
-        {}
+        SaveLayerRec() {}
         SaveLayerRec(const SkRect* bounds, const SkPaint* paint, SaveLayerFlags saveLayerFlags = 0)
             : fBounds(bounds)
             , fPaint(paint)
-            , fBackdrop(nullptr)
             , fSaveLayerFlags(saveLayerFlags)
         {}
         SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop,
+                     sk_sp<SkImage> clipMask, const SkMatrix* clipMatrix,
                      SaveLayerFlags saveLayerFlags)
             : fBounds(bounds)
             , fPaint(paint)
             , fBackdrop(backdrop)
+            , fClipMask(std::move(clipMask))
+            , fClipMatrix(clipMatrix)
             , fSaveLayerFlags(saveLayerFlags)
         {}
 
-        const SkRect*           fBounds;    // optional
-        const SkPaint*          fPaint;     // optional
-        const SkImageFilter*    fBackdrop;  // optional
-        SaveLayerFlags          fSaveLayerFlags;
+        const SkRect*           fBounds = nullptr;      // optional
+        const SkPaint*          fPaint = nullptr;       // optional
+        const SkImageFilter*    fBackdrop = nullptr;    // optional
+        sk_sp<SkImage>          fClipMask;              // optional
+        const SkMatrix*         fClipMatrix = nullptr;  // optional -- only used with fClipMask
+        SaveLayerFlags          fSaveLayerFlags = 0;
     };
 
     int saveLayer(const SaveLayerRec&);
@@ -1489,7 +1491,7 @@ private:
     enum {
         kMCRecSize      = 128,  // most recent measurement
         kMCRecCount     = 32,   // common depth for save/restores
-        kDeviceCMSize   = 184,  // most recent measurement
+        kDeviceCMSize   = 224,  // most recent measurement
     };
     intptr_t fMCRecStorage[kMCRecSize * kMCRecCount / sizeof(intptr_t)];
     intptr_t fDeviceCMStorage[kDeviceCMSize / sizeof(intptr_t)];
@@ -1561,7 +1563,8 @@ private:
                                 SrcRectConstraint);
     void internalDrawPaint(const SkPaint& paint);
     void internalSaveLayer(const SaveLayerRec&, SaveLayerStrategy);
-    void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*);
+    void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*, SkImage* clipImage,
+                            const SkMatrix& clipMatrix);
 
     // shared by save() and saveLayer()
     void internalSave();
index 133d3c9..70f2acb 100644 (file)
@@ -200,10 +200,11 @@ private:
     // V50: SkXfermode -> SkBlendMode
     // V51: more SkXfermode -> SkBlendMode
     // V52: Remove SkTextBlob::fRunCount
+    // V53: SaveLayerRec clip mask
 
     // Only SKPs within the min/current picture version range (inclusive) can be read.
     static const uint32_t     MIN_PICTURE_VERSION = 35;     // Produced by Chrome M39.
-    static const uint32_t CURRENT_PICTURE_VERSION = 52;
+    static const uint32_t CURRENT_PICTURE_VERSION = 53;
 
     static_assert(MIN_PICTURE_VERSION <= 41,
                   "Remove kFontFileName and related code from SkFontDescriptor.cpp.");
index ac492ec..f1886f3 100644 (file)
@@ -180,6 +180,8 @@ RECORD(SaveLayer, kHasPaint_Tag,
        Optional<SkRect> bounds;
        Optional<SkPaint> paint;
        sk_sp<const SkImageFilter> backdrop;
+       sk_sp<SkImage> clipMask;
+       Optional<SkMatrix> clipMatrix;
        SkCanvas::SaveLayerFlags saveLayerFlags);
 
 RECORD(SetMatrix, 0,
index ab75ddb..3ba6d69 100644 (file)
@@ -260,7 +260,7 @@ protected:
 
         SkPaint paint;
         paint.setAlpha(0xCC);
-        canvas->saveLayer({ &bounds, &paint, fFilter.get(), 0 });
+        canvas->saveLayer({ &bounds, &paint, fFilter.get(), nullptr, nullptr, 0 });
 
         canvas->restore();
     }
index 09f25fb..f628621 100644 (file)
@@ -20,6 +20,7 @@
 #include "SkShader.h"
 #include "SkSpecialImage.h"
 #include "SkSurface.h"
+#include "SkTLazy.h"
 #include "SkVertices.h"
 
 class SkColorTable;
@@ -387,35 +388,71 @@ void SkBitmapDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPain
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkBitmapDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y,
-                                 const SkPaint& paint) {
-    SkASSERT(!srcImg->isTextureBacked());
+void SkBitmapDevice::drawSpecial(SkSpecialImage* src, int x, int y, const SkPaint& origPaint,
+                                 SkImage* clipImage, const SkMatrix& clipMatrix) {
+    SkASSERT(!src->isTextureBacked());
 
-    SkBitmap resultBM;
+    sk_sp<SkSpecialImage> filteredImage;
+    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
 
-    SkImageFilter* filter = paint.getImageFilter();
-    if (filter) {
+    if (SkImageFilter* filter = paint->getImageFilter()) {
         SkIPoint offset = SkIPoint::Make(0, 0);
-        SkMatrix matrix = this->ctm();
-        matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+        const SkMatrix matrix = SkMatrix::Concat(
+            SkMatrix::MakeTrans(SkIntToScalar(-x), SkIntToScalar(-y)), this->ctm());
         const SkIRect clipBounds = fRCStack.rc().getBounds().makeOffset(-x, -y);
         sk_sp<SkImageFilterCache> cache(this->getImageFilterCache());
         SkImageFilter::OutputProperties outputProperties(fBitmap.colorSpace());
         SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties);
 
-        sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg, ctx, &offset));
-        if (resultImg) {
-            SkPaint tmpUnfiltered(paint);
-            tmpUnfiltered.setImageFilter(nullptr);
-            if (resultImg->getROPixels(&resultBM)) {
-                this->drawSprite(resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered);
-            }
+        filteredImage = filter->filterImage(src, ctx, &offset);
+        if (!filteredImage) {
+            return;
         }
-    } else {
-        if (srcImg->getROPixels(&resultBM)) {
-            this->drawSprite(resultBM, x, y, paint);
+
+        src = filteredImage.get();
+        paint.writable()->setImageFilter(nullptr);
+        x += offset.x();
+        y += offset.y();
+    }
+
+    if (!clipImage) {
+        SkBitmap resultBM;
+        if (src->getROPixels(&resultBM)) {
+            this->drawSprite(resultBM, x, y, *paint);
         }
+        return;
+    }
+
+    // Clip image case.
+    sk_sp<SkImage> srcImage(src->asImage());
+    if (!srcImage) {
+        return;
     }
+
+    const SkMatrix totalMatrix = SkMatrix::Concat(this->ctm(), clipMatrix);
+
+    SkRect clipBounds;
+    totalMatrix.mapRect(&clipBounds, SkRect::Make(clipImage->bounds()));
+    const SkIRect srcBounds = srcImage->bounds().makeOffset(x, y);
+
+    SkIRect maskBounds = fRCStack.rc().getBounds();
+    if (!maskBounds.intersect(clipBounds.roundOut()) || !maskBounds.intersect(srcBounds)) {
+        return;
+    }
+
+    sk_sp<SkSurface> surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(maskBounds.width(),
+                                                                      maskBounds.height()));
+    SkCanvas* canvas = surf->getCanvas();
+    canvas->translate(-maskBounds.x(), -maskBounds.y());
+    canvas->concat(totalMatrix);
+    canvas->drawImage(clipImage, 0, 0);
+    sk_sp<SkImage> mask = surf->makeImageSnapshot();
+
+    const SkMatrix m = SkMatrix::MakeTrans(x - maskBounds.x(), y - maskBounds.y());
+    paint.writable()->setShader(srcImage->makeShader(&m));
+
+    SkAutoDeviceCTMRestore adctmr(this, SkMatrix::I());
+    this->drawImage(mask.get(), maskBounds.x(), maskBounds.y(), *paint);
 }
 
 sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) {
index 8055b9d..3e9473d 100644 (file)
@@ -112,7 +112,8 @@ protected:
 
     ///////////////////////////////////////////////////////////////////////////
 
-    void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) override;
+    void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
+                     SkImage*, const SkMatrix&) override;
     sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
     sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
     sk_sp<SkSpecialImage> snapSpecial() override;
index a54cf44..c7e1d6e 100644 (file)
@@ -253,12 +253,17 @@ struct DeviceCM {
     SkRasterClip                   fClip;
     std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
     SkMatrix                       fStashedMatrix; // original CTM; used by imagefilter in saveLayer
+    sk_sp<SkImage>                 fClipImage;
+    SkMatrix                       fClipMatrix;
 
-    DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed)
+    DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
+             sk_sp<SkImage> clipImage, const SkMatrix* clipMatrix)
         : fNext(nullptr)
         , fDevice(std::move(device))
         , fPaint(paint ? skstd::make_unique<SkPaint>(*paint) : nullptr)
         , fStashedMatrix(stashed)
+        , fClipImage(std::move(clipImage))
+        , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
     {}
 
     void reset(const SkIRect& bounds) {
@@ -649,7 +654,7 @@ SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
 
     SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
     fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
-    new (fDeviceCMStorage) DeviceCM(sk_ref_sp(device), nullptr, fMCRec->fMatrix);
+    new (fDeviceCMStorage) DeviceCM(sk_ref_sp(device), nullptr, fMCRec->fMatrix, nullptr, nullptr);
 
     fMCRec->fTopLayer = fMCRec->fLayer;
 
@@ -1010,13 +1015,14 @@ int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPai
 }
 
 int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
-    SaveLayerRec rec(origRec);
+    SkTCopyOnFirstWrite<SaveLayerRec> rec(origRec);
     if (gIgnoreSaveLayerBounds) {
-        rec.fBounds = nullptr;
+        rec.writable()->fBounds = nullptr;
     }
-    SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
+
+    SaveLayerStrategy strategy = this->getSaveLayerStrategy(*rec);
     fSaveCount += 1;
-    this->internalSaveLayer(rec, strategy);
+    this->internalSaveLayer(*rec, strategy);
     return this->getSaveCount() - 1;
 }
 
@@ -1041,7 +1047,7 @@ void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filt
     int y = src->getOrigin().y() - dstOrigin.y();
     auto special = src->snapSpecial();
     if (special) {
-        dst->drawSpecial(special.get(), x, y, p);
+        dst->drawSpecial(special.get(), x, y, p, nullptr, SkMatrix::I());
     }
 }
 
@@ -1148,7 +1154,7 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
             return;
         }
     }
-    DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix);
+    DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
 
     // only have a "next" if this new layer doesn't affect the clip (rare)
     layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
@@ -1209,7 +1215,8 @@ void SkCanvas::internalRestore() {
         if (fMCRec) {
             const SkIPoint& origin = layer->fDevice->getOrigin();
             this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
-                                     layer->fPaint.get());
+                                     layer->fPaint.get(),
+                                     layer->fClipImage.get(), layer->fClipMatrix);
             // restore what we smashed in internalSaveLayer
             fMCRec->fMatrix = layer->fStashedMatrix;
             // reset this, since internalDrawDevice will have set it to true
@@ -1302,7 +1309,8 @@ bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
 
 /////////////////////////////////////////////////////////////////////////////
 
-void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
+void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
+                                  SkImage* clipImage, const SkMatrix& clipMatrix) {
     SkPaint tmp;
     if (nullptr == paint) {
         paint = &tmp;
@@ -1315,10 +1323,11 @@ void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPa
         paint = &looper.paint();
         SkImageFilter* filter = paint->getImageFilter();
         SkIPoint pos = { x - iter.getX(), y - iter.getY() };
-        if (filter) {
+        if (filter || clipImage) {
             sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
             if (specialImage) {
-                dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint);
+                dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
+                                    clipImage, clipMatrix);
             }
         } else {
             dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
@@ -2219,7 +2228,8 @@ void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const S
             iter.fDevice->ctm().mapXY(x, y, &pt);
             iter.fDevice->drawSpecial(special.get(),
                                       SkScalarRoundToInt(pt.fX),
-                                      SkScalarRoundToInt(pt.fY), pnt);
+                                      SkScalarRoundToInt(pt.fY), pnt,
+                                      nullptr, SkMatrix::I());
         } else {
             iter.fDevice->drawImage(image, x, y, pnt);
         }
@@ -2300,7 +2310,8 @@ void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, cons
             iter.fDevice->ctm().mapXY(x, y, &pt);
             iter.fDevice->drawSpecial(special.get(),
                                       SkScalarRoundToInt(pt.fX),
-                                      SkScalarRoundToInt(pt.fY), pnt);
+                                      SkScalarRoundToInt(pt.fY), pnt,
+                                      nullptr, SkMatrix::I());
         } else {
             iter.fDevice->drawBitmap(bitmap, matrix, looper.paint());
         }
index f026c6d..b4dd5bb 100644 (file)
@@ -229,10 +229,13 @@ public:
 
     SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
         sk_sp<SkImageFilter> backdrop = rec.fBackdrop ? fXformer->apply(rec.fBackdrop) : nullptr;
+        sk_sp<SkImage> clipMask = rec.fClipMask ? fXformer->apply(rec.fClipMask.get()) : nullptr;
         fTarget->saveLayer({
             rec.fBounds,
             MaybePaint(rec.fPaint, fXformer.get()),
             backdrop.get(),
+            std::move(clipMask),
+            rec.fClipMatrix,
             rec.fSaveLayerFlags,
         });
         return kNoLayer_SaveLayerStrategy;
index f608134..0b72eeb 100644 (file)
@@ -282,7 +282,8 @@ void SkBaseDevice::drawAtlas(const SkImage* atlas, const SkRSXform xform[],
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-void SkBaseDevice::drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) {}
+void SkBaseDevice::drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
+                               SkImage*, const SkMatrix&) {}
 sk_sp<SkSpecialImage> SkBaseDevice::makeSpecial(const SkBitmap&) { return nullptr; }
 sk_sp<SkSpecialImage> SkBaseDevice::makeSpecial(const SkImage*) { return nullptr; }
 sk_sp<SkSpecialImage> SkBaseDevice::snapSpecial() { return nullptr; }
index f1f5488..17ccfdd 100644 (file)
@@ -258,7 +258,8 @@ protected:
     virtual void drawTextRSXform(const void* text, size_t len, const SkRSXform[],
                                  const SkPaint&);
 
-    virtual void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&);
+    virtual void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
+                             SkImage* clipImage, const SkMatrix& clipMatrix);
     virtual sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&);
     virtual sk_sp<SkSpecialImage> makeSpecial(const SkImage*);
     virtual sk_sp<SkSpecialImage> snapSpecial();
index 3168735..b142e7e 100644 (file)
@@ -96,6 +96,8 @@ public:
                            SkMask* mask, SkMask::CreateMode mode,
                            SkStrokeRec::InitStyle style);
 
+    void drawDevMask(const SkMask& mask, const SkPaint&) const;
+
     enum RectType {
         kHair_RectType,
         kFill_RectType,
@@ -122,7 +124,6 @@ public:
                                     const SkPaint&, const SkSurfaceProps*) const;
     static SkScalar ComputeResScaleForStroking(const SkMatrix& );
 private:
-    void    drawDevMask(const SkMask& mask, const SkPaint&) const;
     void    drawBitmapAsMask(const SkBitmap&, const SkPaint&) const;
 
     void    drawPath(const SkPath&, const SkPaint&, const SkMatrix* preMatrix,
index 4a8280d..f840607 100644 (file)
@@ -91,18 +91,24 @@ namespace {
     struct SaveLayer final : Op {
         static const auto kType = Type::SaveLayer;
         SaveLayer(const SkRect* bounds, const SkPaint* paint,
-                  const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) {
+                  const SkImageFilter* backdrop, sk_sp<SkImage> clipMask,
+                  const SkMatrix* clipMatrix, SkCanvas::SaveLayerFlags flags) {
             if (bounds) { this->bounds = *bounds; }
             if (paint)  { this->paint  = *paint;  }
             this->backdrop = sk_ref_sp(backdrop);
+            this->clipMask = std::move(clipMask);
+            this->clipMatrix = clipMatrix ? *clipMatrix : SkMatrix::I();
             this->flags = flags;
         }
         SkRect                     bounds = kUnset;
         SkPaint                    paint;
         sk_sp<const SkImageFilter> backdrop;
+        sk_sp<SkImage>             clipMask;
+        SkMatrix                   clipMatrix;
         SkCanvas::SaveLayerFlags   flags;
         void draw(SkCanvas* c, const SkMatrix&) const {
-            c->saveLayer({ maybe_unset(bounds), &paint, backdrop.get(), flags });
+            c->saveLayer({ maybe_unset(bounds), &paint, backdrop.get(), clipMask,
+                           clipMatrix.isIdentity() ? nullptr : &clipMatrix, flags });
         }
     };
 
@@ -544,8 +550,9 @@ void SkLiteDL::setDrawFilter(SkDrawFilter* df) {
 void SkLiteDL::   save() { this->push   <Save>(0); }
 void SkLiteDL::restore() { this->push<Restore>(0); }
 void SkLiteDL::saveLayer(const SkRect* bounds, const SkPaint* paint,
-                         const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) {
-    this->push<SaveLayer>(0, bounds, paint, backdrop, flags);
+                         const SkImageFilter* backdrop, sk_sp<SkImage> clipMask,
+                         const SkMatrix* clipMatrix, SkCanvas::SaveLayerFlags flags) {
+    this->push<SaveLayer>(0, bounds, paint, backdrop, std::move(clipMask), clipMatrix, flags);
 }
 
 void SkLiteDL::   concat(const SkMatrix& matrix)   { this->push   <Concat>(0, matrix); }
index 1f344cd..5219cd0 100644 (file)
@@ -29,7 +29,8 @@ public:
 #endif
 
     void save();
-    void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, SkCanvas::SaveLayerFlags);
+    void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, sk_sp<SkImage>,
+                   const SkMatrix*, SkCanvas::SaveLayerFlags);
     void restore();
 
     void    concat (const SkMatrix&);
index f899f88..c58355b 100644 (file)
@@ -31,7 +31,8 @@ SkDrawFilter* SkLiteRecorder::setDrawFilter(SkDrawFilter* df) {
 
 void SkLiteRecorder::willSave() { fDL->save(); }
 SkCanvas::SaveLayerStrategy SkLiteRecorder::getSaveLayerStrategy(const SaveLayerRec& rec) {
-    fDL->saveLayer(rec.fBounds, rec.fPaint, rec.fBackdrop, rec.fSaveLayerFlags);
+    fDL->saveLayer(rec.fBounds, rec.fPaint, rec.fBackdrop, rec.fClipMask, rec.fClipMatrix,
+                   rec.fSaveLayerFlags);
     return SkCanvas::kNoLayer_SaveLayerStrategy;
 }
 void SkLiteRecorder::willRestore() { fDL->restore(); }
index a164746..745a7e3 100644 (file)
@@ -122,6 +122,8 @@ enum SaveLayerRecFlatFlags {
     SAVELAYERREC_HAS_PAINT      = 1 << 1,
     SAVELAYERREC_HAS_BACKDROP   = 1 << 2,
     SAVELAYERREC_HAS_FLAGS      = 1 << 3,
+    SAVELAYERREC_HAS_CLIPMASK   = 1 << 4,
+    SAVELAYERREC_HAS_CLIPMATRIX = 1 << 5,
 };
 
 ///////////////////////////////////////////////////////////////////////////////
index 51a3da9..84228d0 100644 (file)
@@ -724,7 +724,8 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader,
             canvas->saveLayer(SkCanvas::SaveLayerRec(boundsPtr, paint, flags));
         } break;
         case SAVE_LAYER_SAVELAYERREC: {
-            SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, 0);
+            SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, nullptr, nullptr, 0);
+            SkMatrix clipMatrix;
             const uint32_t flatFlags = reader->readInt();
             SkRect bounds;
             if (flatFlags & SAVELAYERREC_HAS_BOUNDS) {
@@ -742,6 +743,13 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader,
             if (flatFlags & SAVELAYERREC_HAS_FLAGS) {
                 rec.fSaveLayerFlags = reader->readInt();
             }
+            if (flatFlags & SAVELAYERREC_HAS_CLIPMASK) {
+                rec.fClipMask = sk_ref_sp(const_cast<SkImage*>(fPictureData->getImage(reader)));
+            }
+            if (flatFlags & SAVELAYERREC_HAS_CLIPMATRIX) {
+                reader->readMatrix(&clipMatrix);
+                rec.fClipMatrix = &clipMatrix;
+            }
             BREAK_ON_READ_ERROR(reader);
 
             canvas->saveLayer(rec);
index 9bdb6c5..ea4273f 100644 (file)
@@ -98,6 +98,14 @@ void SkPictureRecord::recordSaveLayer(const SaveLayerRec& rec) {
         flatFlags |= SAVELAYERREC_HAS_FLAGS;
         size += sizeof(uint32_t);
     }
+    if (rec.fClipMask) {
+        flatFlags |= SAVELAYERREC_HAS_CLIPMASK;
+        size += sizeof(uint32_t); // clip image index
+    }
+    if (rec.fClipMatrix) {
+        flatFlags |= SAVELAYERREC_HAS_CLIPMATRIX;
+        size += rec.fClipMatrix->writeToMemory(nullptr);
+    }
 
     const size_t initialOffset = this->addDraw(SAVE_LAYER_SAVELAYERREC, &size);
     this->addInt(flatFlags);
@@ -116,6 +124,12 @@ void SkPictureRecord::recordSaveLayer(const SaveLayerRec& rec) {
     if (flatFlags & SAVELAYERREC_HAS_FLAGS) {
         this->addInt(rec.fSaveLayerFlags);
     }
+    if (flatFlags & SAVELAYERREC_HAS_CLIPMASK) {
+        this->addImage(rec.fClipMask.get());
+    }
+    if (flatFlags & SAVELAYERREC_HAS_CLIPMATRIX) {
+        this->addMatrix(*rec.fClipMatrix);
+    }
     this->validate(initialOffset, size);
 }
 
index 21f9de9..02c8c69 100644 (file)
@@ -77,6 +77,8 @@ DRAW(Save, save());
 DRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds,
                                                  r.paint,
                                                  r.backdrop.get(),
+                                                 r.clipMask,
+                                                 r.clipMatrix,
                                                  r.saveLayerFlags)));
 DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix)));
 DRAW(Concat, concat(r.matrix));
index 07ef19d..8a1ac59 100644 (file)
@@ -188,8 +188,8 @@ struct SaveLayerDrawRestoreNooper {
     typedef Pattern<Is<SaveLayer>, IsDraw, Is<Restore>> Match;
 
     bool onMatch(SkRecord* record, Match* match, int begin, int end) {
-        if (match->first<SaveLayer>()->backdrop) {
-            // can't throw away the layer if we have a backdrop
+        if (match->first<SaveLayer>()->backdrop || match->first<SaveLayer>()->clipMask) {
+            // can't throw away the layer if we have a backdrop or clip mask
             return false;
         }
 
index 0f2891a..c9d88fc 100644 (file)
@@ -366,6 +366,8 @@ SkCanvas::SaveLayerStrategy SkRecorder::getSaveLayerStrategy(const SaveLayerRec&
     APPEND(SaveLayer, this->copy(rec.fBounds)
                     , this->copy(rec.fPaint)
                     , sk_ref_sp(rec.fBackdrop)
+                    , rec.fClipMask
+                    , this->copy(rec.fClipMatrix)
                     , rec.fSaveLayerFlags);
     return SkCanvas::kNoLayer_SaveLayerStrategy;
 }
index 2d3178c..19d9185 100644 (file)
@@ -1125,17 +1125,18 @@ void SkGpuDevice::drawSprite(const SkBitmap& bitmap,
         return;
     }
 
-    this->drawSpecial(srcImg.get(), left, top, paint);
+    this->drawSpecial(srcImg.get(), left, top, paint, nullptr, SkMatrix::I());
 }
 
 
-void SkGpuDevice::drawSpecial(SkSpecialImage* special1,
-                              int left, int top,
-                              const SkPaint& paint) {
+void SkGpuDevice::drawSpecial(SkSpecialImage* special1, int left, int top, const SkPaint& paint,
+                              SkImage* clipImage,const SkMatrix& clipMatrix) {
     ASSERT_SINGLE_OWNER
     CHECK_SHOULD_DRAW();
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawSpecial", fContext.get());
 
+    // TODO: clipImage support.
+
     SkIPoint offset = { 0, 0 };
 
     sk_sp<SkSpecialImage> result;
@@ -1354,7 +1355,7 @@ void SkGpuDevice::drawDevice(SkBaseDevice* device,
         return;
     }
 
-    this->drawSpecial(srcImg.get(), left, top, paint);
+    this->drawSpecial(srcImg.get(), left, top, paint, nullptr, SkMatrix::I());
 }
 
 void SkGpuDevice::drawImage(const SkImage* image, SkScalar x, SkScalar y,
index 4558c1b..340f39a 100644 (file)
@@ -110,8 +110,8 @@ public:
     void drawBitmapLattice(const SkBitmap&, const SkCanvas::Lattice&,
                            const SkRect& dst, const SkPaint&) override;
 
-    void drawSpecial(SkSpecialImage*,
-                     int left, int top, const SkPaint& paint) override;
+    void drawSpecial(SkSpecialImage*, int left, int top, const SkPaint& paint,
+                     SkImage*, const SkMatrix&) override;
     sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
     sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
     sk_sp<SkSpecialImage> snapSpecial() override;
index 768277a..aa8f7ae 100644 (file)
@@ -2202,10 +2202,12 @@ void SkPDFDevice::internalDrawImage(const SkMatrix& origMatrix,
 #include "SkSpecialImage.h"
 #include "SkImageFilter.h"
 
-void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y,
-                              const SkPaint& paint) {
+void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y, const SkPaint& paint,
+                              SkImage* clipImage, const SkMatrix& clipMatrix) {
     SkASSERT(!srcImg->isTextureBacked());
 
+    //TODO: clipImage support
+
     SkBitmap resultBM;
 
     SkImageFilter* filter = paint.getImageFilter();
index 93eb78d..f4318e4 100644 (file)
@@ -170,7 +170,8 @@ protected:
 
     void drawAnnotation(const SkRect&, const char key[], SkData* value) override;
 
-    void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) override;
+    void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
+                     SkImage*, const SkMatrix&) override;
     sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
     sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
     sk_sp<SkSpecialImage> snapSpecial() override;
index 1473b84..91e74ee 100644 (file)
@@ -240,6 +240,12 @@ SkCanvas::SaveLayerStrategy SkPipeCanvas::getSaveLayerStrategy(const SaveLayerRe
     if (rec.fBackdrop) {
         extra |= kHasBackdrop_SaveLayerMask;
     }
+    if (rec.fClipMask) {
+        extra |= kHasClipMask_SaveLayerMask;
+    }
+    if (rec.fClipMatrix) {
+        extra |= kHasClipMatrix_SaveLayerMask;
+    }
 
     writer.write32(pack_verb(SkPipeVerb::kSaveLayer, extra));
     if (rec.fBounds) {
@@ -251,6 +257,13 @@ SkCanvas::SaveLayerStrategy SkPipeCanvas::getSaveLayerStrategy(const SaveLayerRe
     if (rec.fBackdrop) {
         writer.writeFlattenable(rec.fBackdrop);
     }
+    if (rec.fClipMask) {
+        writer.writeImage(rec.fClipMask.get());
+    }
+    if (rec.fClipMatrix) {
+        writer.writeMatrix(*rec.fClipMatrix);
+    }
+
     return kNoLayer_SaveLayerStrategy;
 }
 
index 9a1d30c..a652127 100644 (file)
@@ -107,6 +107,8 @@ enum {
     kHasPaint_SaveLayerMask         = 1 << 9,
     kHasBackdrop_SaveLayerMask      = 1 << 10,
     kDontClipToLayer_SaveLayerMask  = 1 << 11,
+    kHasClipMask_SaveLayerMask      = 1 << 12,
+    kHasClipMatrix_SaveLayerMask    = 1 << 13,
 };
 
 enum {
index 974e6b0..73ef970 100644 (file)
@@ -244,6 +244,14 @@ static void saveLayer_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanva
     if (extra & kHasBackdrop_SaveLayerMask) {
         backdrop = reader.readImageFilter();
     }
+    sk_sp<SkImage> clipMask;
+    if (extra & kHasClipMask_SaveLayerMask) {
+        clipMask = reader.readImage();
+    }
+    SkMatrix clipMatrix;
+    if (extra & kHasClipMatrix_SaveLayerMask) {
+        reader.readMatrix(&clipMatrix);
+    }
     SkCanvas::SaveLayerFlags flags = (SkCanvas::SaveLayerFlags)(extra & kFlags_SaveLayerMask);
 
     // unremap this wacky flag
@@ -251,7 +259,8 @@ static void saveLayer_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanva
         flags |= (1 << 31);//SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag;
     }
 
-    canvas->saveLayer(SkCanvas::SaveLayerRec(bounds, paint, backdrop.get(), flags));
+    canvas->saveLayer(SkCanvas::SaveLayerRec(bounds, paint, backdrop.get(), std::move(clipMask),
+                      (extra & kHasClipMatrix_SaveLayerMask) ? &clipMatrix : nullptr, flags));
 }
 
 static void restore_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanvas* canvas) {
index 09e774e..d34959a 100644 (file)
@@ -190,10 +190,19 @@ DEF_TEST(RecordOpts_NoopSaveLayerDrawRestore, r) {
 
     // saveLayer w/ backdrop should NOT go away
     sk_sp<SkImageFilter> filter(SkBlurImageFilter::Make(3, 3, nullptr));
-    recorder.saveLayer({ nullptr, nullptr, filter.get(), 0});
+    recorder.saveLayer({ nullptr, nullptr, filter.get(), nullptr, nullptr, 0});
         recorder.drawRect(draw, opaqueDrawPaint);
     recorder.restore();
     assert_savelayer_draw_restore(r, &record, 18, false);
+
+    // saveLayer w/ clip mask should also NOT go away
+    {
+        sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(10, 10));
+        recorder.saveLayer({ nullptr, nullptr, nullptr, surface->makeImageSnapshot(), nullptr, 0});
+            recorder.drawRect(draw, opaqueDrawPaint);
+        recorder.restore();
+        assert_savelayer_draw_restore(r, &record, 21, false);
+    }
 }
 #endif
 
@@ -265,10 +274,12 @@ DEF_TEST(RecordOpts_MergeSvgOpacityAndFilterLayers, r) {
                             for (size_t m = 0; m < SK_ARRAY_COUNT(secondPaints); ++m) {
                                 bool innerNoOped = !secondBounds[k] && !secondPaints[m] && !innerF;
 
-                                recorder.saveLayer({firstBounds[i], firstPaints[j], outerF, 0});
+                                recorder.saveLayer({firstBounds[i], firstPaints[j], outerF,
+                                                    nullptr, nullptr, 0});
                                 recorder.save();
                                 recorder.clipRect(clip);
-                                recorder.saveLayer({secondBounds[k], secondPaints[m], innerF, 0});
+                                recorder.saveLayer({secondBounds[k], secondPaints[m], innerF,
+                                                    nullptr, nullptr, 0});
                                 recorder.restore();
                                 recorder.restore();
                                 recorder.restore();