Simplify paint filtering

Removes complication of copy-on-write intermediate class, and
allows for shaing a single applyLooper() function.

Test: make

Bug: 178700363
Change-Id: Ia74cb8e7c650e469b8429de1d7cf9204821d8f11
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 1fddac4..28d2b4c 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -188,7 +188,7 @@
     }
 
     if (mCanvas->getSaveCount() == restoreCount + 1) {
-        SkCanvasPriv::DrawBehind(mCanvas, *filterPaint(paint));
+        SkCanvasPriv::DrawBehind(mCanvas, filterPaint(paint));
         this->restore();
     }
 }
@@ -431,15 +431,14 @@
     mCanvas->drawColor(color, mode);
 }
 
-SkiaCanvas::PaintCoW&& SkiaCanvas::filterPaint(PaintCoW&& paint) const {
+void SkiaCanvas::onFilterPaint(SkPaint& paint) {
     if (mPaintFilter) {
-        mPaintFilter->filter(&paint.writeable());
+        mPaintFilter->filter(&paint);
     }
-    return std::move(paint);
 }
 
 void SkiaCanvas::drawPaint(const SkPaint& paint) {
-    mCanvas->drawPaint(*filterPaint(paint));
+    mCanvas->drawPaint(filterPaint(paint));
 }
 
 // ----------------------------------------------------------------------------
@@ -457,13 +456,11 @@
         points += 2;
     }
 
-    apply_looper(&paint, [&](const SkPaint& p) {
-        mCanvas->drawPoints(mode, count, pts.get(), p);
-    });
+    applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPoints(mode, count, pts.get(), p); });
 }
 
 void SkiaCanvas::drawPoint(float x, float y, const Paint& paint) {
-    apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawPoint(x, y, p); });
+    applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPoint(x, y, p); });
 }
 
 void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint) {
@@ -472,9 +469,8 @@
 
 void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
                           const Paint& paint) {
-    apply_looper(&paint, [&](const SkPaint& p) {
-        mCanvas->drawLine(startX, startY, stopX, stopY, p);
-    });
+    applyLooper(&paint,
+                [&](const SkPaint& p) { mCanvas->drawLine(startX, startY, stopX, stopY, p); });
 }
 
 void SkiaCanvas::drawLines(const float* points, int count, const Paint& paint) {
@@ -484,46 +480,44 @@
 
 void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const Paint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
-    apply_looper(&paint, [&](const SkPaint& p) {
+    applyLooper(&paint, [&](const SkPaint& p) {
         mCanvas->drawRect({left, top, right, bottom}, p);
     });
 }
 
 void SkiaCanvas::drawRegion(const SkRegion& region, const Paint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
-    apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawRegion(region, p); });
+    applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawRegion(region, p); });
 }
 
 void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
                                const Paint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
-    apply_looper(&paint, [&](const SkPaint& p) {
-        mCanvas->drawRoundRect(rect, rx, ry, p);
-    });
+    applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawRoundRect(rect, rx, ry, p); });
 }
 
 void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
                                 const Paint& paint) {
-    apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawDRRect(outer, inner, p); });
+    applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawDRRect(outer, inner, p); });
 }
 
 void SkiaCanvas::drawCircle(float x, float y, float radius, const Paint& paint) {
     if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
-    apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawCircle(x, y, radius, p); });
+    applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawCircle(x, y, radius, p); });
 }
 
 void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const Paint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
-    apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawOval(oval, p); });
+    applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawOval(oval, p); });
 }
 
 void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle,
                          float sweepAngle, bool useCenter, const Paint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
-    apply_looper(&paint, [&](const SkPaint& p) {
+    applyLooper(&paint, [&](const SkPaint& p) {
         if (fabs(sweepAngle) >= 360.0f) {
             mCanvas->drawOval(arc, p);
         } else {
@@ -537,13 +531,11 @@
     if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) {
         return;
     }
-    apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawPath(path, p); });
+    applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPath(path, p); });
 }
 
 void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const Paint& paint) {
-    apply_looper(&paint, [&](const SkPaint& p) {
-        mCanvas->drawVertices(vertices, mode, p);
-    });
+    applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawVertices(vertices, mode, p); });
 }
 
 // ----------------------------------------------------------------------------
@@ -552,7 +544,7 @@
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
     auto image = bitmap.makeImage();
-    apply_looper(paint, [&](const SkPaint& p) {
+    applyLooper(paint, [&](const SkPaint& p) {
         auto sampling = SkSamplingOptions(p.getFilterQuality());
         mCanvas->drawImage(image, left, top, sampling, &p);
     });
@@ -562,7 +554,7 @@
     auto image = bitmap.makeImage();
     SkAutoCanvasRestore acr(mCanvas, true);
     mCanvas->concat(matrix);
-    apply_looper(paint, [&](const SkPaint& p) {
+    applyLooper(paint, [&](const SkPaint& p) {
         auto sampling = SkSamplingOptions(p.getFilterQuality());
         mCanvas->drawImage(image, 0, 0, sampling, &p);
     });
@@ -575,7 +567,7 @@
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    apply_looper(paint, [&](const SkPaint& p) {
+    applyLooper(paint, [&](const SkPaint& p) {
         auto sampling = SkSamplingOptions(p.getFilterQuality());
         mCanvas->drawImageRect(image, srcRect, dstRect, sampling, &p,
                                SkCanvas::kFast_SrcRectConstraint);
@@ -672,11 +664,11 @@
     pnt.setShader(image->makeShader(sampling));
 
     auto v = builder.detach();
-    apply_looper(&pnt, [&](const SkPaint& p) {
+    applyLooper(&pnt, [&](const SkPaint& p) {
         SkPaint copy(p);
         auto s = SkSamplingOptions(p.getFilterQuality());
         if (s != sampling) {
-            // apply_looper changed the quality?
+            // applyLooper changed the quality?
             copy.setShader(image->makeShader(s));
         }
         mCanvas->drawVertices(v, SkBlendMode::kModulate, copy);
@@ -707,7 +699,7 @@
     lattice.fBounds = nullptr;
     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
     auto image = bitmap.makeImage();
-    apply_looper(paint, [&](const SkPaint& p) {
+    applyLooper(paint, [&](const SkPaint& p) {
         auto filter = SkSamplingOptions(p.getFilterQuality()).filter;
         mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p);
     });
@@ -746,9 +738,7 @@
 
     sk_sp<SkTextBlob> textBlob(builder.make());
 
-    apply_looper(&paintCopy, [&](const SkPaint& p) {
-        mCanvas->drawTextBlob(textBlob, 0, 0, p);
-    });
+    applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); });
     drawTextDecorations(x, y, totalAdvance, paintCopy);
 }
 
@@ -788,9 +778,7 @@
 
     sk_sp<SkTextBlob> textBlob(builder.make());
 
-    apply_looper(&paintCopy, [&](const SkPaint& p) {
-        mCanvas->drawTextBlob(textBlob, 0, 0, p);
-    });
+    applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); });
 }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index eac3f22..9ab2b10 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -169,53 +169,24 @@
                                   const Paint& paint, const SkPath& path, size_t start,
                                   size_t end) override;
 
-    /** This class acts as a copy on write SkPaint.
-     *
-     *  Initially this will be the SkPaint passed to the contructor.
-     *  The first time writable() is called this will become a copy of the
-     *  initial SkPaint (or a default SkPaint if nullptr).
-     */
-    struct PaintCoW {
-        PaintCoW(const SkPaint& that) : mPtr(&that) {}
-        PaintCoW(const SkPaint* ptr) : mPtr(ptr) {}
-        PaintCoW(const PaintCoW&) = delete;
-        PaintCoW(PaintCoW&&) = delete;
-        PaintCoW& operator=(const PaintCoW&) = delete;
-        PaintCoW& operator=(PaintCoW&&) = delete;
-        SkPaint& writeable() {
-            if (!mStorage) {
-                if (!mPtr) {
-                    mStorage.emplace();
-                } else {
-                    mStorage.emplace(*mPtr);
-                }
-                mPtr = &*mStorage;
-            }
-            return *mStorage;
-        }
-        operator const SkPaint*() const { return mPtr; }
-        const SkPaint* operator->() const { assert(mPtr); return mPtr; }
-        explicit operator bool() { return mPtr != nullptr; }
-    private:
-        const SkPaint* mPtr;
-        std::optional<SkPaint> mStorage;
-    };
+    void onFilterPaint(SkPaint& paint);
 
-    /** Filters the paint using the current paint filter.
-     *
-     *  @param paint the paint to filter. Will be initialized with the default
-     *      SkPaint before filtering if filtering is required.
-     */
-    PaintCoW&& filterPaint(PaintCoW&& paint) const;
+    SkPaint filterPaint(const SkPaint& src) {
+        SkPaint dst(src);
+        this->onFilterPaint(dst);
+        return dst;
+    }
 
     // proc(const SkPaint& modifiedPaint)
-    template <typename Proc> void apply_looper(const Paint* paint, Proc proc) {
-        SkPaint skp;
-        BlurDrawLooper* looper = nullptr;
-        if (paint) {
-            skp = *filterPaint(paint);
-            looper = paint->getLooper();
+    template <typename Proc>
+    void applyLooper(const Paint* paint, Proc proc, void (*preFilter)(SkPaint&) = nullptr) {
+        BlurDrawLooper* looper = paint ? paint->getLooper() : nullptr;
+        const SkPaint* skpPtr = paint;
+        SkPaint skp = skpPtr ? *skpPtr : SkPaint();
+        if (preFilter) {
+            preFilter(skp);
         }
+        this->onFilterPaint(skp);
         if (looper) {
             looper->apply(skp, [&](SkPoint offset, const SkPaint& modifiedPaint) {
                 mCanvas->save();
@@ -228,7 +199,6 @@
         }
     }
 
-
 private:
     struct SaveRec {
         int saveCount;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 04e3a1c..af7271e 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -170,36 +170,23 @@
 // Recording Canvas draw operations: Bitmaps
 // ----------------------------------------------------------------------------
 
-SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint) {
-    bool fixBlending = false;
-    bool fixAA = false;
-    if (paint) {
-        // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
-        // older.
-        fixBlending = sApiLevel <= 27 && paint->getBlendMode() == SkBlendMode::kClear;
-        fixAA = paint->isAntiAlias();
+void SkiaRecordingCanvas::FilterForImage(SkPaint& paint) {
+    // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
+    // older.
+    if (sApiLevel <= 27 && paint.getBlendMode() == SkBlendMode::kClear) {
+        paint.setBlendMode(SkBlendMode::kDstOut);
     }
 
-    if (fixBlending || fixAA) {
-        SkPaint& tmpPaint = paint.writeable();
-
-        if (fixBlending) {
-            tmpPaint.setBlendMode(SkBlendMode::kDstOut);
-        }
-
-        // disabling AA on bitmap draws matches legacy HWUI behavior
-        tmpPaint.setAntiAlias(false);
-    }
-
-    return filterPaint(std::move(paint));
+    // disabling AA on bitmap draws matches legacy HWUI behavior
+    paint.setAntiAlias(false);
 }
 
-static SkFilterMode Paint_to_filter(const SkPaint* paint) {
-    return paint && paint->getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear
-                                                                       : SkFilterMode::kNearest;
+static SkFilterMode Paint_to_filter(const SkPaint& paint) {
+    return paint.getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear
+                                                             : SkFilterMode::kNearest;
 }
 
-static SkSamplingOptions Paint_to_sampling(const SkPaint* paint) {
+static SkSamplingOptions Paint_to_sampling(const SkPaint& paint) {
     // Android only has 1-bit for "filter", so we don't try to cons-up mipmaps or cubics
     return SkSamplingOptions(Paint_to_filter(paint), SkMipmapMode::kNone);
 }
@@ -207,9 +194,12 @@
 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
     sk_sp<SkImage> image = bitmap.makeImage();
 
-    applyLooper(paint, [&](SkScalar x, SkScalar y, const SkPaint* p) {
-        mRecorder.drawImage(image, left + x, top + y, Paint_to_sampling(p), p, bitmap.palette());
-    });
+    applyLooper(
+            paint,
+            [&](const SkPaint& p) {
+                mRecorder.drawImage(image, left, top, Paint_to_sampling(p), &p, bitmap.palette());
+            },
+            FilterForImage);
 
     // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
     // it is not safe to store a raw SkImage pointer, because the image object will be destroyed
@@ -225,9 +215,12 @@
 
     sk_sp<SkImage> image = bitmap.makeImage();
 
-    applyLooper(paint, [&](SkScalar x, SkScalar y, const SkPaint* p) {
-        mRecorder.drawImage(image, x, y, Paint_to_sampling(p), p, bitmap.palette());
-    });
+    applyLooper(
+            paint,
+            [&](const SkPaint& p) {
+                mRecorder.drawImage(image, 0, 0, Paint_to_sampling(p), &p, bitmap.palette());
+            },
+            FilterForImage);
 
     if (!bitmap.isImmutable() && image.get() && !image->unique()) {
         mDisplayList->mMutableImages.push_back(image.get());
@@ -242,10 +235,13 @@
 
     sk_sp<SkImage> image = bitmap.makeImage();
 
-    applyLooper(paint, [&](SkScalar x, SkScalar y, const SkPaint* p) {
-        mRecorder.drawImageRect(image, srcRect, dstRect.makeOffset(x, y), Paint_to_sampling(p),
-                                p, SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
-    });
+    applyLooper(
+            paint,
+            [&](const SkPaint& p) {
+                mRecorder.drawImageRect(image, srcRect, dstRect, Paint_to_sampling(p), &p,
+                                        SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
+            },
+            FilterForImage);
 
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
         !dstRect.isEmpty()) {
@@ -281,10 +277,12 @@
     // HWUI always draws 9-patches with linear filtering, regardless of the Paint.
     const SkFilterMode filter = SkFilterMode::kLinear;
 
-    applyLooper(paint, [&](SkScalar x, SkScalar y, const SkPaint* p) {
-        mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), filter, p,
-                                   bitmap.palette());
-    });
+    applyLooper(
+            paint,
+            [&](const SkPaint& p) {
+                mRecorder.drawImageLattice(image, lattice, dst, filter, &p, bitmap.palette());
+            },
+            FilterForImage);
 
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
         mDisplayList->mMutableImages.push_back(image.get());
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 1e404b8..ff03e0c 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -87,22 +87,7 @@
     std::unique_ptr<SkiaDisplayList> mDisplayList;
     StartReorderBarrierDrawable* mCurrentBarrier;
 
-    template <typename Proc>
-    void applyLooper(const Paint* paint, Proc proc) {
-        SkPaint skp;
-        BlurDrawLooper* looper = nullptr;
-        if (paint) {
-            skp = *filterBitmap(paint);
-            looper = paint->getLooper();
-        }
-        if (looper) {
-            looper->apply(skp, [&](SkPoint offset, const SkPaint& modifiedPaint) {
-                proc(offset.fX, offset.fY, &modifiedPaint);
-            });
-        } else {
-            proc(0, 0, &skp);
-        }
-    }
+    static void FilterForImage(SkPaint&);
 
     /**
      *  A new SkiaDisplayList is created or recycled if available.
@@ -113,7 +98,7 @@
      */
     void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height);
 
-    PaintCoW&& filterBitmap(PaintCoW&& paint);
+    using INHERITED = SkiaCanvas;
 };
 
 }  // namespace skiapipeline