Force-enable dithering in wide gamut & HDR

Fixes: 276779571
Test: SilkFX gradient sweep
Change-Id: I26907913feb216e43bbbc735878d12311735c3af
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 8ea71f1..1f92968 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -28,6 +28,7 @@
 #include <SkMultiPictureDocument.h>
 #include <SkOverdrawCanvas.h>
 #include <SkOverdrawColorFilter.h>
+#include <SkPaintFilterCanvas.h>
 #include <SkPicture.h>
 #include <SkPictureRecorder.h>
 #include <SkRect.h>
@@ -36,15 +37,15 @@
 #include <SkStream.h>
 #include <SkString.h>
 #include <SkTypeface.h>
-#include "include/gpu/GpuTypes.h" // from Skia
 #include <android-base/properties.h>
+#include <gui/TraceUtils.h>
 #include <unistd.h>
 
 #include <sstream>
 
-#include <gui/TraceUtils.h>
 #include "LightingInfo.h"
 #include "VectorDrawable.h"
+#include "include/gpu/GpuTypes.h"  // from Skia
 #include "thread/CommonPool.h"
 #include "tools/SkSharingProc.h"
 #include "utils/Color.h"
@@ -449,6 +450,23 @@
     }
 }
 
+class ForceDitherCanvas : public SkPaintFilterCanvas {
+public:
+    ForceDitherCanvas(SkCanvas* canvas) : SkPaintFilterCanvas(canvas) {}
+
+protected:
+    bool onFilter(SkPaint& paint) const override {
+        paint.setDither(true);
+        return true;
+    }
+
+    void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
+        // We unroll the drawable using "this" canvas, so that draw calls contained inside will
+        // get dithering applied
+        drawable->draw(this, matrix);
+    }
+};
+
 void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
                                const std::vector<sp<RenderNode>>& nodes, bool opaque,
                                const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
@@ -503,6 +521,12 @@
         canvas->clear(SK_ColorTRANSPARENT);
     }
 
+    std::optional<ForceDitherCanvas> forceDitherCanvas;
+    if (shouldForceDither()) {
+        forceDitherCanvas.emplace(canvas);
+        canvas = &forceDitherCanvas.value();
+    }
+
     if (1 == nodes.size()) {
         if (!nodes[0]->nothingToDraw()) {
             RenderNodeDrawable root(nodes[0].get(), canvas);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index befee89..0763b06 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -98,6 +98,8 @@
 
     bool isCapturingSkp() const { return mCaptureMode != CaptureMode::None; }
 
+    virtual bool shouldForceDither() const { return mColorMode != ColorMode::Default; }
+
 private:
     void renderFrameImpl(const SkRect& clip,
                          const std::vector<sp<RenderNode>>& nodes, bool opaque,
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index c8f2e69..6f1b99b 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -203,6 +203,11 @@
     return nullptr;
 }
 
+bool SkiaVulkanPipeline::shouldForceDither() const {
+    if (mVkSurface && mVkSurface->isBeyond8Bit()) return false;
+    return SkiaPipeline::shouldForceDither();
+}
+
 void SkiaVulkanPipeline::onContextDestroyed() {
     if (mVkSurface) {
         vulkanManager().destroySurface(mVkSurface);
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index d921ddb..0713e93 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -63,6 +63,8 @@
 protected:
     void onContextDestroyed() override;
 
+    bool shouldForceDither() const override;
+
 private:
     renderthread::VulkanManager& vulkanManager();
     renderthread::VulkanSurface* mVkSurface = nullptr;