Revert "In the CPU backend, stop treating alpha images as coverage"

Restore the old behavior behind a flag, so it can be maintained in the
Android framework.

This reverts commit f1660bf1baecb787f5d541c1973f2c7f2ded13dd.

Bug: b/231400686
Test: GM in Skia
Change-Id: I01fadaed52eecc42416deb2307d03499bca85a4c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/557391
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Leon Scroggins <scroggo@google.com>
(cherry picked from commit fa93c4addd3cfba3054d34f30d5d4b61af2821be)
Merged-In: I01fadaed52eecc42416deb2307d03499bca85a4c
diff --git a/gm/alpha_image.cpp b/gm/alpha_image.cpp
index 95e23f8..6edafe1 100644
--- a/gm/alpha_image.cpp
+++ b/gm/alpha_image.cpp
@@ -17,6 +17,7 @@
 #include "include/core/SkPaint.h"
 #include "include/core/SkRefCnt.h"
 #include "include/core/SkShader.h"
+#include "tools/Resources.h"
 
 static SkBitmap make_alpha_image(int w, int h) {
     SkBitmap bm;
@@ -84,3 +85,41 @@
     paint.setShader(image->makeShader(SkSamplingOptions()));
     canvas->drawRect({ 0, 0, 64, 64 }, paint);
 }
+
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+// For a long time, the CPU backend treated A8 bitmaps as coverage, rather than alpha. This was
+// inconsistent with the GPU backend (skbug.com/9692). When this was fixed, it altered behavior
+// for some Android apps (b/231400686). This GM verifies that our Android framework workaround
+// produces the old result (mandrill with a round-rect border).
+DEF_SIMPLE_GM(alpha_bitmap_is_coverage_ANDROID, canvas, 128, 128) {
+    SkBitmap maskBitmap;
+    maskBitmap.allocPixels(SkImageInfo::MakeA8(128, 128));
+    {
+        SkCanvas maskCanvas(maskBitmap);
+        maskCanvas.clear(SK_ColorWHITE);
+
+        SkPaint maskPaint;
+        maskPaint.setAntiAlias(true);
+        maskPaint.setColor(SK_ColorWHITE);
+        maskPaint.setBlendMode(SkBlendMode::kClear);
+        maskCanvas.drawRoundRect({0, 0, 128, 128}, 16, 16, maskPaint);
+    }
+
+    SkBitmap offscreenBitmap;
+    offscreenBitmap.allocN32Pixels(128, 128);
+    {
+        SkCanvas offscreenCanvas(offscreenBitmap);
+        offscreenCanvas.drawImage(GetResourceAsImage("images/mandrill_128.png"), 0, 0);
+
+        SkPaint clearPaint;
+        clearPaint.setAntiAlias(true);
+        clearPaint.setBlendMode(SkBlendMode::kClear);
+        // At tip-of-tree (or at any time on the GPU backend), this draw produces full coverage,
+        // completely erasing the mandrill. With the workaround enabled, the alpha border is treated
+        // as coverage, so we only apply kClear to those pixels, just erasing the outer border.
+        offscreenCanvas.drawImage(maskBitmap.asImage(), 0, 0, SkSamplingOptions{}, &clearPaint);
+    }
+
+    canvas->drawImage(offscreenBitmap.asImage(), 0, 0);
+}
+#endif
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
index f68f8ee..b910d06 100644
--- a/src/core/SkBlitter.cpp
+++ b/src/core/SkBlitter.cpp
@@ -273,6 +273,22 @@
 
 /////////////////////// these are not virtual, just helpers
 
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) {
+    if (clip.quickReject(mask.fBounds)) {
+        return;
+    }
+
+    SkRegion::Cliperator clipper(clip, mask.fBounds);
+
+    while (!clipper.done()) {
+        const SkIRect& cr = clipper.rect();
+        this->blitMask(mask, cr);
+        clipper.next();
+    }
+}
+#endif
+
 void SkBlitter::blitRectRegion(const SkIRect& rect, const SkRegion& clip) {
     SkRegion::Cliperator clipper(clip, rect);
 
diff --git a/src/core/SkBlitter.h b/src/core/SkBlitter.h
index 0ed7cd6..ec58c5a 100644
--- a/src/core/SkBlitter.h
+++ b/src/core/SkBlitter.h
@@ -132,6 +132,9 @@
     }
 
     ///@name non-virtual helpers
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+    void blitMaskRegion(const SkMask& mask, const SkRegion& clip);
+#endif
     void blitRectRegion(const SkIRect& rect, const SkRegion& clip);
     void blitRegion(const SkRegion& clip);
     ///@}
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 60d18e9..30cbaf2 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -705,6 +705,39 @@
     }
 }
 
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
+    if (srcM.fBounds.isEmpty()) {
+        return;
+    }
+
+    const SkMask* mask = &srcM;
+
+    SkMask dstM;
+    if (paint.getMaskFilter() &&
+        as_MFB(paint.getMaskFilter())
+                ->filterMask(&dstM, srcM, fMatrixProvider->localToDevice(), nullptr)) {
+        mask = &dstM;
+    }
+    SkAutoMaskFreeImage ami(dstM.fImage);
+
+    SkAutoBlitterChoose blitterChooser(*this, nullptr, paint);
+    SkBlitter* blitter = blitterChooser.get();
+
+    SkAAClipBlitterWrapper wrapper;
+    const SkRegion* clipRgn;
+
+    if (fRC->isBW()) {
+        clipRgn = &fRC->bwRgn();
+    } else {
+        wrapper.init(*fRC, blitter);
+        clipRgn = &wrapper.getRgn();
+        blitter = wrapper.getBlitter();
+    }
+    blitter->blitMaskRegion(*mask, *clipRgn);
+}
+#endif
+
 static SkScalar fast_len(const SkVector& vec) {
     SkScalar x = SkScalarAbs(vec.fX);
     SkScalar y = SkScalarAbs(vec.fY);
@@ -925,6 +958,94 @@
     this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
 }
 
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkSamplingOptions& sampling,
+                              const SkPaint& paint) const {
+    SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType);
+
+    // nothing to draw
+    if (fRC->isEmpty()) {
+        return;
+    }
+
+    SkMatrix ctm = fMatrixProvider->localToDevice();
+    if (SkTreatAsSprite(ctm, bitmap.dimensions(), sampling, paint))
+    {
+        int ix = SkScalarRoundToInt(ctm.getTranslateX());
+        int iy = SkScalarRoundToInt(ctm.getTranslateY());
+
+        SkPixmap pmap;
+        if (!bitmap.peekPixels(&pmap)) {
+            return;
+        }
+        SkMask  mask;
+        mask.fBounds.setXYWH(ix, iy, pmap.width(), pmap.height());
+        mask.fFormat = SkMask::kA8_Format;
+        mask.fRowBytes = SkToU32(pmap.rowBytes());
+        // fImage is typed as writable, but in this case it is used read-only
+        mask.fImage = (uint8_t*)pmap.addr8(0, 0);
+
+        this->drawDevMask(mask, paint);
+    } else {    // need to xform the bitmap first
+        SkRect  r;
+        SkMask  mask;
+
+        r.setIWH(bitmap.width(), bitmap.height());
+        ctm.mapRect(&r);
+        r.round(&mask.fBounds);
+
+        // set the mask's bounds to the transformed bitmap-bounds,
+        // clipped to the actual device and further limited by the clip bounds
+        {
+            SkASSERT(fDst.bounds().contains(fRC->getBounds()));
+            SkIRect devBounds = fDst.bounds();
+            devBounds.intersect(fRC->getBounds().makeOutset(1, 1));
+            // need intersect(l, t, r, b) on irect
+            if (!mask.fBounds.intersect(devBounds)) {
+                return;
+            }
+        }
+
+        mask.fFormat = SkMask::kA8_Format;
+        mask.fRowBytes = SkAlign4(mask.fBounds.width());
+        size_t size = mask.computeImageSize();
+        if (0 == size) {
+            // the mask is too big to allocated, draw nothing
+            return;
+        }
+
+        // allocate (and clear) our temp buffer to hold the transformed bitmap
+        SkAutoTMalloc<uint8_t> storage(size);
+        mask.fImage = storage.get();
+        memset(mask.fImage, 0, size);
+
+        // now draw our bitmap(src) into mask(dst), transformed by the matrix
+        {
+            SkBitmap    device;
+            device.installPixels(SkImageInfo::MakeA8(mask.fBounds.width(), mask.fBounds.height()),
+                                 mask.fImage, mask.fRowBytes);
+
+            SkCanvas c(device);
+            // need the unclipped top/left for the translate
+            c.translate(-SkIntToScalar(mask.fBounds.fLeft),
+                        -SkIntToScalar(mask.fBounds.fTop));
+            c.concat(ctm);
+
+            // We can't call drawBitmap, or we'll infinitely recurse. Instead
+            // we manually build a shader and draw that into our new mask
+            SkPaint tmpPaint;
+            tmpPaint.setAntiAlias(paint.isAntiAlias());
+            tmpPaint.setDither(paint.isDither());
+            SkPaint paintWithShader = make_paint_with_image(tmpPaint, bitmap, sampling);
+            SkRect rr;
+            rr.setIWH(bitmap.width(), bitmap.height());
+            c.drawRect(rr, paintWithShader);
+        }
+        this->drawDevMask(mask, paint);
+    }
+}
+#endif
+
 static bool clipped_out(const SkMatrix& m, const SkRasterClip& c,
                         const SkRect& srcR) {
     SkRect  dstR;
@@ -998,6 +1119,16 @@
     SkDraw draw(*this);
     draw.fMatrixProvider = &matrixProvider;
 
+    // For a long time, the CPU backend treated A8 bitmaps as coverage, rather than alpha. This was
+    // inconsistent with the GPU backend (skbug.com/9692). When this was fixed, it altered behavior
+    // for some Android apps (b/231400686). Thus: keep the old behavior in the framework.
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+    if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
+        draw.drawBitmapAsMask(bitmap, sampling, *paint);
+        return;
+    }
+#endif
+
     SkPaint paintWithShader = make_paint_with_image(*paint, bitmap, sampling);
     const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
     if (dstBounds) {
diff --git a/src/core/SkDraw.h b/src/core/SkDraw.h
index dc5fe8e..c87948c 100644
--- a/src/core/SkDraw.h
+++ b/src/core/SkDraw.h
@@ -98,6 +98,10 @@
                            SkMask* mask, SkMask::CreateMode mode,
                            SkStrokeRec::InitStyle style);
 
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+    void drawDevMask(const SkMask& mask, const SkPaint&) const;
+#endif
+
     enum RectType {
         kHair_RectType,
         kFill_RectType,
@@ -117,6 +121,9 @@
                                     SkPoint* strokeSize);
 
 private:
+#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
+    void drawBitmapAsMask(const SkBitmap&, const SkSamplingOptions&, const SkPaint&) const;
+#endif
     void drawFixedVertices(const SkVertices* vertices,
                            sk_sp<SkBlender> blender,
                            const SkPaint& paint,