Experiment to track coverage in a layer

Bug: skia:
Change-Id: I5ed334f63e64991944394dc8103092a2c6280546
Reviewed-on: https://skia-review.googlesource.com/122000
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
diff --git a/docs/SkCanvas_Reference.bmh b/docs/SkCanvas_Reference.bmh
index de33770..0bce15e 100644
--- a/docs/SkCanvas_Reference.bmh
+++ b/docs/SkCanvas_Reference.bmh
@@ -1684,6 +1684,7 @@
     enum {
         kPreserveLCDText_SaveLayerFlag = 1 << 1,
         kInitWithPrevious_SaveLayerFlag = 1 << 2,
+        kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag = 1 << 3,
         kDontClipToLayer_Legacy_SaveLayerFlag = kDontClipToLayer_PrivateSaveLayerFlag,
     };
 ##
@@ -1700,6 +1701,10 @@
   Initializes Layer with the contents of the previous Layer.
 ##
 
+#Const kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag 8
+  Experimental -- don't use
+##
+
 #Const kDontClipToLayer_Legacy_SaveLayerFlag 0x80000000
 #Deprecated soon
 ##
diff --git a/gm/savelayer.cpp b/gm/savelayer.cpp
index 32f95bf..7721820 100644
--- a/gm/savelayer.cpp
+++ b/gm/savelayer.cpp
@@ -238,3 +238,42 @@
         }
     }
 }
+
+DEF_SIMPLE_GM(savelayer_coverage, canvas, 500, 500) {
+    canvas->saveLayer(nullptr, nullptr);
+
+    SkRect r = { 0, 0, 200, 200 };
+    SkPaint layerPaint;
+    layerPaint.setBlendMode(SkBlendMode::kModulate);
+
+    auto image = GetResourceAsImage("images/mandrill_128.png");
+
+    auto proc = [layerPaint](SkCanvas* canvas, SkCanvas::SaveLayerRec& rec) {
+        SkPaint paint;
+        paint.setColor(SK_ColorRED);
+
+        canvas->saveLayer(rec);
+        canvas->drawCircle(100, 100, 50, paint);
+        paint.setColor(0x8800FF00);
+        canvas->drawRect({10, 90, 190, 110}, paint);
+        canvas->restore();
+    };
+
+    const int yflags[] = { 0, SkCanvas::kInitWithPrevious_SaveLayerFlag };
+    for (int y = 0; y <= 1; ++y) {
+        const int xflags[] = { 0, SkCanvas::kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag };
+        for (int x = 0; x <= 1; ++x) {
+            canvas->save();
+            canvas->translate(x * 200.f, y * 200.f);
+
+            SkCanvas::SaveLayerRec rec(&r, &layerPaint, yflags[y] | xflags[x]);
+            canvas->drawImageRect(image, r, nullptr);
+            proc(canvas, rec);
+
+            canvas->restore();
+        }
+    }
+
+    canvas->restore();
+}
+
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index a819cda..73f7a3e 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -609,6 +609,8 @@
         /** Initializes layer with the contents of the previous layer. */
         kInitWithPrevious_SaveLayerFlag       = 1 << 2,
 
+        kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag  = 1 << 3,
+
 #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
         /** To be deprecated soon. */
         kDontClipToLayer_Legacy_SaveLayerFlag = kDontClipToLayer_PrivateSaveLayerFlag,
diff --git a/src/core/SkAutoBlitterChoose.h b/src/core/SkAutoBlitterChoose.h
index 3c2e8af..b6c0433 100644
--- a/src/core/SkAutoBlitterChoose.h
+++ b/src/core/SkAutoBlitterChoose.h
@@ -10,6 +10,7 @@
 
 #include "SkArenaAlloc.h"
 #include "SkBlitter.h"
+#include "SkDraw.h"
 
 class SkMatrix;
 class SkPaint;
@@ -17,27 +18,35 @@
 
 class SkAutoBlitterChoose : SkNoncopyable {
 public:
-    SkAutoBlitterChoose() {
-        fBlitter = nullptr;
-    }
-    SkAutoBlitterChoose(const SkPixmap& dst, const SkMatrix& matrix,
-                        const SkPaint& paint, bool drawCoverage = false) {
-        fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage);
+    SkAutoBlitterChoose() {}
+    SkAutoBlitterChoose(const SkDraw& draw, const SkMatrix* matrix, const SkPaint& paint,
+                        bool drawCoverage = false) {
+        this->choose(draw, matrix, paint, drawCoverage);
     }
 
     SkBlitter*  operator->() { return fBlitter; }
     SkBlitter*  get() const { return fBlitter; }
 
-    SkBlitter* choose(const SkPixmap& dst, const SkMatrix& matrix,
-                const SkPaint& paint, bool drawCoverage = false) {
+    SkBlitter* choose(const SkDraw& draw, const SkMatrix* matrix, const SkPaint& paint,
+                      bool drawCoverage = false) {
         SkASSERT(!fBlitter);
-        fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage);
+        if (!matrix) {
+            matrix = draw.fMatrix;
+        }
+        fBlitter = SkBlitter::Choose(draw.fDst, *matrix, paint, &fAlloc, drawCoverage);
+
+        if (draw.fCoverage) {
+            // hmm, why can't choose ignore the paint if drawCoverage is true?
+            SkBlitter* coverageBlitter = SkBlitter::Choose(*draw.fCoverage, *matrix, SkPaint(),
+                                                           &fAlloc, true);
+            fBlitter = fAlloc.make<SkPairBlitter>(fBlitter, coverageBlitter);
+        }
         return fBlitter;
     }
 
 private:
     // Owned by fAlloc, which will handle the delete.
-    SkBlitter*          fBlitter;
+    SkBlitter* fBlitter = nullptr;
 
     SkSTArenaAlloc<kSkBlitterContextSize> fAlloc;
 };
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 507dd35..b49818c 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -10,6 +10,7 @@
 #include "SkImageFilter.h"
 #include "SkImageFilterCache.h"
 #include "SkMallocPixelRef.h"
+#include "SkMakeUnique.h"
 #include "SkMatrix.h"
 #include "SkPaint.h"
 #include "SkPath.h"
@@ -104,6 +105,8 @@
             fDraw.fMatrix = &dev->ctm();
             fDraw.fRC = &dev->fRCStack.rc();
             fOrigin.set(0, 0);
+
+            fDraw.fCoverage = dev->accessCoverage();
         }
     }
 
@@ -180,6 +183,7 @@
         }
         fMatrix = &dev->ctm();
         fRC = &dev->fRCStack.rc();
+        fCoverage = dev->accessCoverage();
     }
 };
 
@@ -238,17 +242,24 @@
 }
 
 SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps,
-                               SkRasterHandleAllocator::Handle hndl)
+                               SkRasterHandleAllocator::Handle hndl, const SkBitmap* coverage)
     : INHERITED(bitmap.info(), surfaceProps)
     , fBitmap(bitmap)
     , fRasterHandle(hndl)
     , fRCStack(bitmap.width(), bitmap.height())
 {
     SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
+
+    if (coverage) {
+        SkASSERT(coverage->width() == bitmap.width());
+        SkASSERT(coverage->height() == bitmap.height());
+        fCoverage = skstd::make_unique<SkBitmap>(*coverage);
+    }
 }
 
 SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo,
                                        const SkSurfaceProps& surfaceProps,
+                                       bool trackCoverage,
                                        SkRasterHandleAllocator* allocator) {
     SkAlphaType newAT = origInfo.alphaType();
     if (!valid_for_bitmap_device(origInfo, &newAT)) {
@@ -282,7 +293,16 @@
         }
     }
 
-    return new SkBitmapDevice(bitmap, surfaceProps, hndl);
+    SkBitmap coverage;
+    if (trackCoverage) {
+        SkImageInfo ci = SkImageInfo::Make(info.width(), info.height(), kAlpha_8_SkColorType,
+                                           kPremul_SkAlphaType);
+        if (!coverage.tryAllocPixelsFlags(ci, SkBitmap::kZeroPixels_AllocFlag)) {
+            return nullptr;
+        }
+    }
+
+    return new SkBitmapDevice(bitmap, surfaceProps, hndl, trackCoverage ? &coverage : nullptr);
 }
 
 void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
@@ -294,7 +314,8 @@
 
 SkBaseDevice* SkBitmapDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) {
     const SkSurfaceProps surfaceProps(this->surfaceProps().flags(), cinfo.fPixelGeometry);
-    return SkBitmapDevice::Create(cinfo.fInfo, surfaceProps, cinfo.fAllocator);
+    return SkBitmapDevice::Create(cinfo.fInfo, surfaceProps, cinfo.fTrackCoverage,
+                                  cinfo.fAllocator);
 }
 
 bool SkBitmapDevice::onAccessPixels(SkPixmap* pmap) {
@@ -593,7 +614,22 @@
     if (paint->getMaskFilter()) {
         paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithMatrix(this->ctm()));
     }
-    this->drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap, x, y, *paint);
+
+    // hack to test coverage
+    SkBitmapDevice* src = static_cast<SkBitmapDevice*>(device);
+    if (src->fCoverage) {
+        SkDraw draw;
+        draw.fDst = fBitmap.pixmap();
+        draw.fMatrix = &SkMatrix::I();
+        draw.fRC = &fRCStack.rc();
+        SkPaint paint(origPaint);
+        paint.setShader(SkShader::MakeBitmapShader(src->fBitmap, SkShader::kClamp_TileMode,
+                                                   SkShader::kClamp_TileMode, nullptr));
+        draw.drawBitmap(*src->fCoverage.get(),
+                        SkMatrix::MakeTrans(SkIntToScalar(x),SkIntToScalar(y)), nullptr, paint);
+    } else {
+        this->drawSprite(src->fBitmap, x, y, *paint);
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkBitmapDevice.h b/src/core/SkBitmapDevice.h
index e040b6b..f80cba6 100644
--- a/src/core/SkBitmapDevice.h
+++ b/src/core/SkBitmapDevice.h
@@ -55,10 +55,19 @@
      *  any drawing to this device will have no effect.
      */
     SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps,
-                   void* externalHandle = nullptr);
+                   void* externalHandle, const SkBitmap* coverage);
 
     static SkBitmapDevice* Create(const SkImageInfo&, const SkSurfaceProps&,
-                                  SkRasterHandleAllocator* = nullptr);
+                                  bool trackCoverage,
+                                  SkRasterHandleAllocator*);
+
+    static SkBitmapDevice* Create(const SkImageInfo& info, const SkSurfaceProps& props) {
+        return Create(info, props, false, nullptr);
+    }
+
+    const SkPixmap* accessCoverage() const {
+        return fCoverage ? &fCoverage->pixmap() : nullptr;
+    }
 
 protected:
     void* getRasterHandle() const override { return fRasterHandle; }
@@ -165,6 +174,7 @@
     SkBitmap    fBitmap;
     void*       fRasterHandle = nullptr;
     SkRasterClipStack  fRCStack;
+    std::unique_ptr<SkBitmap> fCoverage;    // if non-null, will have the same dimensions as fBitmap
 
     typedef SkBaseDevice INHERITED;
 };
diff --git a/src/core/SkBlitter.h b/src/core/SkBlitter.h
index c280ac3..cf3edd3 100644
--- a/src/core/SkBlitter.h
+++ b/src/core/SkBlitter.h
@@ -297,4 +297,33 @@
     SkRgnClipBlitter    fRgnBlitter;
 };
 
+#define SHARD(code)   fA->code; fB->code;
+
+class SkPairBlitter : public SkBlitter {
+    SkBlitter*  fA = nullptr;
+    SkBlitter*  fB = nullptr;
+public:
+    SkPairBlitter(SkBlitter* a, SkBlitter* b) : fA(a), fB(b) {}
+
+    void blitH(int x, int y, int width) override { SHARD(blitH(x, y, width)) }
+    void blitAntiH(int x, int y, const SkAlpha alphas[], const int16_t runs[]) override {
+         SHARD(blitAntiH(x, y, alphas, runs))
+    }
+    void blitV(int x, int y, int height, SkAlpha alpha) override {
+        SHARD(blitV(x, y, height, alpha))
+    }
+    void blitRect(int x, int y, int width, int height) override {
+        SHARD(blitRect(x, y, width, height))
+    }
+    void blitAntiRect(int x, int y, int width, int height,
+                      SkAlpha leftAlpha, SkAlpha rightAlpha) override {
+        SHARD(blitAntiRect(x, y, width, height, leftAlpha, rightAlpha))
+    }
+    void blitMask(const SkMask& mask, const SkIRect& clip) override { SHARD(blitMask(mask, clip)) }
+    const SkPixmap* justAnOpaqueColor(uint32_t* value) override { return nullptr; }
+    void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) override { SHARD(blitAntiH2(x, y, a0, a1)) }
+    void blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) override { SHARD(blitAntiV2(x, y, a0, a1)) }
+};
+#undef SHARD
+
 #endif
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 3f2439d..5264472 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -644,7 +644,7 @@
 {
     inc_canvas();
 
-    sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
+    sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr));
     this->init(device.get(), kDefault_InitFlags);
 }
 
@@ -656,7 +656,7 @@
 {
     inc_canvas();
 
-    sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl));
+    sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr));
     this->init(device.get(), kDefault_InitFlags);
 }
 
@@ -1058,8 +1058,10 @@
         const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
                                      (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
         const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
+        const bool trackCoverage = SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag);
         const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
                                                                              preserveLCDText,
+                                                                             trackCoverage,
                                                                              fAllocator.get());
         newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
         if (!newDevice) {
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index fb91c8a..40b9589 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -305,16 +305,19 @@
                    TileUsage tileUsage,
                    SkPixelGeometry geo,
                    bool preserveLCDText,
+                   bool trackCoverage,
                    SkRasterHandleAllocator* allocator)
             : fInfo(info)
             , fTileUsage(tileUsage)
             , fPixelGeometry(AdjustGeometry(info, tileUsage, geo, preserveLCDText))
+            , fTrackCoverage(trackCoverage)
             , fAllocator(allocator)
         {}
 
         const SkImageInfo       fInfo;
         const TileUsage         fTileUsage;
         const SkPixelGeometry   fPixelGeometry;
+        const bool              fTrackCoverage = false;
         SkRasterHandleAllocator* fAllocator = nullptr;
     };
 
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 71d0d13..362c8b5 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -220,7 +220,7 @@
     }
 
     // normal case: use a blitter
-    SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
+    SkAutoBlitterChoose blitter(*this, nullptr, paint);
     SkScan::FillIRect(devRect, *fRC, blitter.get());
 }
 
@@ -497,7 +497,7 @@
 
     PtProcRec rec;
     if (!device && rec.init(mode, paint, fMatrix, fRC)) {
-        SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
+        SkAutoBlitterChoose blitter(*this, nullptr, paint);
 
         SkPoint             devPts[MAX_DEV_PTS];
         const SkMatrix*     matrix = fMatrix;
@@ -798,7 +798,7 @@
         return;
     }
 
-    SkAutoBlitterChoose blitterStorage(fDst, *matrix, paint);
+    SkAutoBlitterChoose blitterStorage(*this, matrix, paint);
     const SkRasterClip& clip = *fRC;
     SkBlitter*          blitter = blitterStorage.get();
 
@@ -846,7 +846,7 @@
     }
     SkAutoMaskFreeImage ami(dstM.fImage);
 
-    SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint);
+    SkAutoBlitterChoose blitterChooser(*this, nullptr, paint);
     SkBlitter* blitter = blitterChooser.get();
 
     SkAAClipBlitterWrapper wrapper;
@@ -920,7 +920,7 @@
         // Transform the rrect into device space.
         SkRRect devRRect;
         if (rrect.transform(*fMatrix, &devRRect)) {
-            SkAutoBlitterChoose blitter(fDst, *fMatrix, paint);
+            SkAutoBlitterChoose blitter(*this, nullptr, paint);
             if (as_MFB(paint.getMaskFilter())->filterRRect(devRRect, *fMatrix,
                                                            *fRC, blitter.get())) {
                 return; // filterRRect() called the blitter, so we're done
@@ -954,7 +954,7 @@
     SkBlitter* blitter = nullptr;
     SkAutoBlitterChoose blitterStorage;
     if (nullptr == customBlitter) {
-        blitter = blitterStorage.choose(fDst, *fMatrix, paint, drawCoverage);
+        blitter = blitterStorage.choose(*this, nullptr, paint, drawCoverage);
     } else {
         blitter = customBlitter;
     }
@@ -1022,9 +1022,8 @@
         iData->fElement->setDrawFn([proc, devPath, paint, drawCoverage](SkArenaAlloc* alloc,
                 const SkThreadedBMPDevice::DrawState& ds, const SkIRect& tileBounds) {
             SkThreadedBMPDevice::TileDraw tileDraw(ds, tileBounds);
-            SkAutoBlitterChoose blitterStorage;
-            proc(devPath, *tileDraw.fRC, blitterStorage.choose(tileDraw.fDst, *tileDraw.fMatrix,
-                                                               paint, drawCoverage));
+            SkAutoBlitterChoose blitterStorage(tileDraw, nullptr, paint, drawCoverage);
+            proc(devPath, *tileDraw.fRC, blitterStorage.get());
         });
     } else {
         // We can use DAA to do scan conversion in the init-once phase.
@@ -1036,10 +1035,8 @@
                     const SkThreadedBMPDevice::DrawState& ds, const SkIRect& tileBounds) {
             SkASSERT(record->fType != SkDAARecord::Type::kToBeComputed);
             SkThreadedBMPDevice::TileDraw tileDraw(ds, tileBounds);
-            SkAutoBlitterChoose blitterStorage;
-            SkBlitter* blitter = blitterStorage.choose(tileDraw.fDst, *tileDraw.fMatrix,
-                                                       paint, drawCoverage);
-            SkScan::AntiFillPath(devPath, *tileDraw.fRC, blitter, record);
+            SkAutoBlitterChoose blitterStorage(tileDraw, nullptr, paint, drawCoverage);
+            SkScan::AntiFillPath(devPath, *tileDraw.fRC, blitterStorage.get(), record);
         });
     }
 }
@@ -1547,7 +1544,7 @@
             paint, props, this->scalerContextFlags(), fMatrix);
 
     // The Blitter Choose needs to be live while using the blitter below.
-    SkAutoBlitterChoose    blitterChooser(fDst, *fMatrix, paint);
+    SkAutoBlitterChoose    blitterChooser(*this, nullptr, paint);
     SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
     DrawOneGlyph           drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
 
@@ -1627,7 +1624,7 @@
             paint, props, this->scalerContextFlags(), fMatrix);
 
     // The Blitter Choose needs to be live while using the blitter below.
-    SkAutoBlitterChoose    blitterChooser(fDst, *fMatrix, paint);
+    SkAutoBlitterChoose    blitterChooser(*this, nullptr, paint);
     SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get());
     DrawOneGlyph           drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter());
     SkPaint::Align         textAlignment = paint.getTextAlign();
diff --git a/src/core/SkDraw.h b/src/core/SkDraw.h
index 8598475..eabccf0 100644
--- a/src/core/SkDraw.h
+++ b/src/core/SkDraw.h
@@ -152,6 +152,9 @@
     const SkMatrix* fMatrix;        // required
     const SkRasterClip* fRC;        // required
 
+    // optional, will be same dimensions as fDst if present
+    const SkPixmap* fCoverage = nullptr;
+
 #ifdef SK_DEBUG
     void validate() const;
 #else
diff --git a/src/core/SkDraw_vertices.cpp b/src/core/SkDraw_vertices.cpp
index 810ded1..8c1ad1f 100644
--- a/src/core/SkDraw_vertices.cpp
+++ b/src/core/SkDraw_vertices.cpp
@@ -294,7 +294,7 @@
         // no colors[] and no texture, stroke hairlines with paint's color.
         SkPaint p;
         p.setStyle(SkPaint::kStroke_Style);
-        SkAutoBlitterChoose blitter(fDst, *fMatrix, p);
+        SkAutoBlitterChoose blitter(*this, nullptr, p);
         // Abort early if we failed to create a shader context.
         if (blitter->isNullBlitter()) {
             return;
diff --git a/tests/LayerDrawLooperTest.cpp b/tests/LayerDrawLooperTest.cpp
index f3705b7..1a1e68b 100644
--- a/tests/LayerDrawLooperTest.cpp
+++ b/tests/LayerDrawLooperTest.cpp
@@ -27,7 +27,8 @@
 // TODO: can this be derived from SkBaseDevice?
 class FakeDevice : public SkBitmapDevice {
 public:
-    FakeDevice() : INHERITED(make_bm(100, 100), SkSurfaceProps(0, kUnknown_SkPixelGeometry)) {
+    FakeDevice() : INHERITED(make_bm(100, 100), SkSurfaceProps(0, kUnknown_SkPixelGeometry),
+                             nullptr, nullptr) {
     }
 
     void drawRect(const SkRect& r, const SkPaint& paint) override {