Replace SkDrawFilter with PaintFilter.

Skia is removing SkDrawFilter.

Test: refactoring CL. Existing unit tests still pass.
Change-Id: If81c1a2feabf7d377ae1d7bcb3e8cf0f599ad3ce
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 7b41f89..17f1a3b 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -21,6 +21,7 @@
 #include "VectorDrawable.h"
 #include "hwui/Bitmap.h"
 #include "hwui/MinikinUtils.h"
+#include "hwui/PaintFilter.h"
 #include "pipeline/skia/AnimatedDrawables.h"
 
 #include <SkAnimatedImage.h>
@@ -28,7 +29,6 @@
 #include <SkColorFilter.h>
 #include <SkColorSpaceXformCanvas.h>
 #include <SkDeque.h>
-#include <SkDrawFilter.h>
 #include <SkDrawable.h>
 #include <SkGraphics.h>
 #include <SkImage.h>
@@ -40,6 +40,8 @@
 #include <SkTextBlob.h>
 
 #include <memory>
+#include <optional>
+#include <utility>
 
 namespace android {
 
@@ -211,7 +213,7 @@
     Clip(const SkRRect& rrect, SkClipOp op, const SkMatrix& m)
             : mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {}
     Clip(const SkPath& path, SkClipOp op, const SkMatrix& m)
-            : mType(Type::Path), mOp(op), mMatrix(m), mPath(&path) {}
+            : mType(Type::Path), mOp(op), mMatrix(m), mPath(std::in_place, path) {}
 
     void apply(SkCanvas* canvas) const {
         canvas->setMatrix(mMatrix);
@@ -223,7 +225,7 @@
                 canvas->clipRRect(mRRect, mOp);
                 break;
             case Type::Path:
-                canvas->clipPath(*mPath.get(), mOp);
+                canvas->clipPath(mPath.value(), mOp);
                 break;
         }
     }
@@ -240,7 +242,7 @@
     SkMatrix mMatrix;
 
     // These are logically a union (tracked separately due to non-POD path).
-    SkTLazy<SkPath> mPath;
+    std::optional<SkPath> mPath;
     SkRRect mRRect;
 };
 
@@ -400,12 +402,12 @@
 // Canvas state operations: Filters
 // ----------------------------------------------------------------------------
 
-SkDrawFilter* SkiaCanvas::getDrawFilter() {
-    return mCanvas->getDrawFilter();
+PaintFilter* SkiaCanvas::getPaintFilter() {
+    return mPaintFilter.get();
 }
 
-void SkiaCanvas::setDrawFilter(SkDrawFilter* drawFilter) {
-    mCanvas->setDrawFilter(drawFilter);
+void SkiaCanvas::setPaintFilter(sk_sp<PaintFilter> paintFilter) {
+    mPaintFilter = std::move(paintFilter);
 }
 
 // ----------------------------------------------------------------------------
@@ -439,8 +441,15 @@
     mCanvas->drawColor(color, mode);
 }
 
+SkiaCanvas::PaintCoW&& SkiaCanvas::filterPaint(PaintCoW&& paint) const {
+    if (mPaintFilter) {
+        mPaintFilter->filter(&paint.writeable());
+    }
+    return std::move(paint);
+}
+
 void SkiaCanvas::drawPaint(const SkPaint& paint) {
-    mCanvas->drawPaint(paint);
+    mCanvas->drawPaint(*filterPaint(paint));
 }
 
 // ----------------------------------------------------------------------------
@@ -457,53 +466,53 @@
         pts[i].set(points[0], points[1]);
         points += 2;
     }
-    mCanvas->drawPoints(mode, count, pts.get(), paint);
+    mCanvas->drawPoints(mode, count, pts.get(), *filterPaint(paint));
 }
 
 void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) {
-    mCanvas->drawPoint(x, y, paint);
+    mCanvas->drawPoint(x, y, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
-    this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode);
+    this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kPoints_PointMode);
 }
 
 void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
                           const SkPaint& paint) {
-    mCanvas->drawLine(startX, startY, stopX, stopY, paint);
+    mCanvas->drawLine(startX, startY, stopX, stopY, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
     if (CC_UNLIKELY(count < 4 || paint.nothingToDraw())) return;
-    this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
+    this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kLines_PointMode);
 }
 
 void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
-    mCanvas->drawRect({left, top, right, bottom}, paint);
+    mCanvas->drawRect({left, top, right, bottom}, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
-    mCanvas->drawRegion(region, paint);
+    mCanvas->drawRegion(region, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
                                const SkPaint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
-    mCanvas->drawRoundRect(rect, rx, ry, paint);
+    mCanvas->drawRoundRect(rect, rx, ry, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
     if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
-    mCanvas->drawCircle(x, y, radius, paint);
+    mCanvas->drawCircle(x, y, radius, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
-    mCanvas->drawOval(oval, paint);
+    mCanvas->drawOval(oval, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle,
@@ -511,9 +520,9 @@
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
     if (fabs(sweepAngle) >= 360.0f) {
-        mCanvas->drawOval(arc, paint);
+        mCanvas->drawOval(arc, *filterPaint(paint));
     } else {
-        mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint);
+        mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, *filterPaint(paint));
     }
 }
 
@@ -522,19 +531,19 @@
     if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) {
         return;
     }
-    mCanvas->drawPath(path, paint);
+    mCanvas->drawPath(path, *filterPaint(paint));
 }
 
 void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
-    mCanvas->drawVertices(vertices, mode, paint);
+    mCanvas->drawVertices(vertices, mode, *filterPaint(paint));
 }
 
 // ----------------------------------------------------------------------------
 // Canvas draw operations: Bitmaps
 // ----------------------------------------------------------------------------
 
-const SkPaint* SkiaCanvas::addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
-                                     sk_sp<SkColorFilter> colorSpaceFilter) {
+SkiaCanvas::PaintCoW&& SkiaCanvas::filterBitmap(PaintCoW&& paint,
+                                                sk_sp<SkColorFilter> colorSpaceFilter) const {
     /* We don't apply the colorSpace filter if this canvas is already wrapped with
      * a SkColorSpaceXformCanvas since it already takes care of converting the
      * contents of the bitmap into the appropriate colorspace.  The mCanvasWrapper
@@ -542,39 +551,31 @@
      * to have a non-sRGB colorspace.
      */
     if (!mCanvasWrapper && colorSpaceFilter) {
-        if (origPaint) {
-            *tmpPaint = *origPaint;
-        }
-
-        if (tmpPaint->getColorFilter()) {
-            tmpPaint->setColorFilter(
-                    SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorSpaceFilter));
-            LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
+        SkPaint& tmpPaint = paint.writeable();
+        if (tmpPaint.getColorFilter()) {
+            tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(tmpPaint.refColorFilter(),
+                                                                     std::move(colorSpaceFilter)));
+            LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter());
         } else {
-            tmpPaint->setColorFilter(colorSpaceFilter);
+            tmpPaint.setColorFilter(std::move(colorSpaceFilter));
         }
-
-        return tmpPaint;
-    } else {
-        return origPaint;
     }
+    return filterPaint(std::move(paint));
 }
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImage(image, left, top, addFilter(paint, &tmpPaint, colorFilter));
+    mCanvas->drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)));
 }
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
     SkAutoCanvasRestore acr(mCanvas, true);
     mCanvas->concat(matrix);
 
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImage(image, 0, 0, addFilter(paint, &tmpPaint, colorFilter));
+    mCanvas->drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
 }
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
@@ -583,10 +584,9 @@
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImageRect(image, srcRect, dstRect, addFilter(paint, &tmpPaint, colorFilter),
+    mCanvas->drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
                            SkCanvas::kFast_SrcRectConstraint);
 }
 
@@ -665,21 +665,20 @@
 #endif
 
     // cons-up a shader for the bitmap
-    SkPaint tmpPaint;
-    if (paint) {
-        tmpPaint = *paint;
-    }
+    PaintCoW paintCoW(paint);
+    SkPaint& tmpPaint = paintCoW.writeable();
 
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
     sk_sp<SkShader> shader =
             image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     if (colorFilter) {
-        shader = shader->makeWithColorFilter(colorFilter);
+        shader = shader->makeWithColorFilter(std::move(colorFilter));
     }
-    tmpPaint.setShader(shader);
+    tmpPaint.setShader(std::move(shader));
 
-    mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate, tmpPaint);
+    mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate,
+                          *filterPaint(std::move(paintCoW)));
 }
 
 void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft,
@@ -706,10 +705,10 @@
     lattice.fBounds = nullptr;
     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImageLattice(image.get(), lattice, dst, addFilter(paint, &tmpPaint, colorFilter));
+    mCanvas->drawImageLattice(image.get(), lattice, dst,
+                              filterBitmap(paint, std::move(colorFilter)));
 }
 
 double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) {
@@ -732,6 +731,9 @@
     // glyphs centered or right-aligned; the offset above takes
     // care of all alignment.
     SkPaint paintCopy(paint);
+    if (mPaintFilter) {
+        mPaintFilter->filter(&paintCopy);
+    }
     paintCopy.setTextAlign(SkPaint::kLeft_Align);
     SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
     // Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
@@ -760,6 +762,9 @@
     // glyphs centered or right-aligned; the offsets take care of
     // that portion of the alignment.
     SkPaint paintCopy(paint);
+    if (mPaintFilter) {
+        mPaintFilter->filter(&paintCopy);
+    }
     paintCopy.setTextAlign(SkPaint::kLeft_Align);
     SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
 
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 3efc22a..24b7ec6 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -22,7 +22,9 @@
 #include "hwui/Canvas.h"
 
 #include <SkCanvas.h>
-#include <SkTLazy.h>
+
+#include <cassert>
+#include <optional>
 
 namespace android {
 
@@ -87,8 +89,8 @@
     virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override;
     virtual bool clipPath(const SkPath* path, SkClipOp op) override;
 
-    virtual SkDrawFilter* getDrawFilter() override;
-    virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
+    virtual PaintFilter* getPaintFilter() override;
+    virtual void setPaintFilter(sk_sp<PaintFilter> paintFilter) override;
 
     virtual SkCanvasState* captureCanvasState() const override;
 
@@ -158,6 +160,46 @@
                                   const SkPaint& 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; }
+        const SkPaint& operator*() const { assert(mPtr); return *mPtr; }
+        explicit operator bool() { return mPtr != nullptr; }
+    private:
+        const SkPaint* mPtr;
+        std::optional<SkPaint> mStorage;
+    };
+
+    /** 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;
+
 private:
     struct SaveRec {
         int saveCount;
@@ -174,8 +216,15 @@
 
     void drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode);
 
-    const SkPaint* addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
-                             sk_sp<SkColorFilter> colorSpaceFilter);
+    /** Filters the paint for bitmap drawing.
+     *
+     *  After filtering the paint for bitmap drawing,
+     *  also calls filterPaint on the paint.
+     *
+     *  @param paint the paint to filter. Will be initialized with the default
+     *      SkPaint before filtering if filtering is required.
+     */
+    PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp<SkColorFilter> colorSpaceFilter) const;
 
     class Clip;
 
@@ -185,6 +234,7 @@
                                                // unless it is the same as mCanvasOwned.get()
     std::unique_ptr<SkDeque> mSaveStack;       // lazily allocated, tracks partial saves.
     std::vector<Clip> mClipStack;              // tracks persistent clips.
+    sk_sp<PaintFilter> mPaintFilter;
 };
 
 }  // namespace android
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index fb6bd6f..af7f013 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -23,7 +23,7 @@
 #include "Typeface.h"
 #include "pipeline/skia/SkiaRecordingCanvas.h"
 
-#include <SkDrawFilter.h>
+#include "hwui/PaintFilter.h"
 
 namespace android {
 
@@ -40,10 +40,10 @@
 
 void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
     uint32_t flags;
-    SkDrawFilter* drawFilter = getDrawFilter();
-    if (drawFilter) {
+    PaintFilter* paintFilter = getPaintFilter();
+    if (paintFilter) {
         SkPaint paintCopy(paint);
-        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+        paintFilter->filter(&paintCopy);
         flags = paintCopy.getFlags();
     } else {
         flags = paint.getFlags();
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 88b12b2..5d380a6 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -39,6 +39,7 @@
 }
 
 namespace android {
+class PaintFilter;
 
 namespace uirenderer {
 class CanvasPropertyPaint;
@@ -213,8 +214,8 @@
     virtual bool clipPath(const SkPath* path, SkClipOp op) = 0;
 
     // filters
-    virtual SkDrawFilter* getDrawFilter() = 0;
-    virtual void setDrawFilter(SkDrawFilter* drawFilter) = 0;
+    virtual PaintFilter* getPaintFilter() = 0;
+    virtual void setPaintFilter(sk_sp<PaintFilter> paintFilter) = 0;
 
     // WebView only
     virtual SkCanvasState* captureCanvasState() const { return nullptr; }
diff --git a/libs/hwui/hwui/PaintFilter.h b/libs/hwui/hwui/PaintFilter.h
new file mode 100644
index 0000000..bf5627e
--- /dev/null
+++ b/libs/hwui/hwui/PaintFilter.h
@@ -0,0 +1,19 @@
+#ifndef ANDROID_GRAPHICS_PAINT_FILTER_H_
+#define ANDROID_GRAPHICS_PAINT_FILTER_H_
+
+class SkPaint;
+
+namespace android {
+
+class PaintFilter : public SkRefCnt {
+public:
+    /**
+     *  Called with the paint that will be used to draw.
+     *  The implementation may modify the paint as they wish.
+     */
+    virtual void filter(SkPaint*) = 0;
+};
+
+} // namespace android
+
+#endif
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index f0da660..b9fe885 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -148,12 +148,45 @@
 // Recording Canvas draw operations: Bitmaps
 // ----------------------------------------------------------------------------
 
+SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint,
+                                                         sk_sp<SkColorFilter> colorSpaceFilter) {
+    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();
+    }
+
+    if (fixBlending || fixAA || colorSpaceFilter) {
+        SkPaint& tmpPaint = paint.writeable();
+
+        if (fixBlending) {
+            tmpPaint.setBlendMode(SkBlendMode::kDstOut);
+        }
+
+        if (colorSpaceFilter) {
+            if (tmpPaint.getColorFilter()) {
+                tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(
+                       tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
+            } else {
+                tmpPaint.setColorFilter(std::move(colorSpaceFilter));
+            }
+            LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter());
+        }
+
+        // disabling AA on bitmap draws matches legacy HWUI behavior
+        tmpPaint.setAntiAlias(false);
+    }
+
+    return filterPaint(std::move(paint));
+}
 
 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImage(image, left, top, bitmapPaint(paint, &tmpPaint, colorFilter));
+    mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)));
     // 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
     // when this function ends.
@@ -166,10 +199,9 @@
     SkAutoCanvasRestore acr(&mRecorder, true);
     concat(matrix);
 
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImage(image, 0, 0, bitmapPaint(paint, &tmpPaint, colorFilter));
+    mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
     if (!bitmap.isImmutable() && image.get() && !image->unique()) {
         mDisplayList->mMutableImages.push_back(image.get());
     }
@@ -181,10 +213,9 @@
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImageRect(image, srcRect, dstRect, bitmapPaint(paint, &tmpPaint, colorFilter),
+    mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
                             SkCanvas::kFast_SrcRectConstraint);
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
         !dstRect.isEmpty()) {
@@ -216,23 +247,16 @@
     lattice.fBounds = nullptr;
     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    SkPaint tmpPaint;
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    const SkPaint* filteredPaint = bitmapPaint(paint, &tmpPaint, colorFilter);
+    PaintCoW filteredPaint(paint);
     // Besides kNone, the other three SkFilterQualities are treated the same. And Android's
     // Java API only supports kLow and kNone anyway.
     if (!filteredPaint || filteredPaint->getFilterQuality() == kNone_SkFilterQuality) {
-        if (filteredPaint != &tmpPaint) {
-            if (paint) {
-                tmpPaint = *paint;
-            }
-            filteredPaint = &tmpPaint;
-        }
-        tmpPaint.setFilterQuality(kLow_SkFilterQuality);
+        filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
     }
-
-    mRecorder.drawImageLattice(image.get(), lattice, dst, filteredPaint);
+    sk_sp<SkColorFilter> colorFilter;
+    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+    mRecorder.drawImageLattice(image.get(), lattice, dst,
+                               filterBitmap(std::move(filteredPaint), std::move(colorFilter)));
     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 93807a5..ae7fdf8 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -89,44 +89,7 @@
      */
     void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height);
 
-    inline static const SkPaint* bitmapPaint(const SkPaint* origPaint, SkPaint* tmpPaint,
-                                             sk_sp<SkColorFilter> colorSpaceFilter) {
-        bool fixBlending = false;
-        bool fixAA = false;
-        if (origPaint) {
-            // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
-            // older.
-            fixBlending = sApiLevel <= 27 && origPaint->getBlendMode() == SkBlendMode::kClear;
-            fixAA = origPaint->isAntiAlias();
-        }
-
-        if (fixBlending || fixAA || colorSpaceFilter) {
-            if (origPaint) {
-                *tmpPaint = *origPaint;
-            }
-
-            if (fixBlending) {
-                tmpPaint->setBlendMode(SkBlendMode::kDstOut);
-            }
-
-            if (colorSpaceFilter) {
-                if (tmpPaint->getColorFilter()) {
-                    tmpPaint->setColorFilter(SkColorFilter::MakeComposeFilter(
-                            tmpPaint->refColorFilter(), colorSpaceFilter));
-                } else {
-                    tmpPaint->setColorFilter(colorSpaceFilter);
-                }
-                LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
-            }
-
-            // disabling AA on bitmap draws matches legacy HWUI behavior
-            tmpPaint->setAntiAlias(false);
-            return tmpPaint;
-        } else {
-            return origPaint;
-        }
-    }
-
+    PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp<SkColorFilter> colorSpaceFilter);
 };
 
 };  // namespace skiapipeline