diff --git a/engine/src/flutter/flow/display_list.cc b/engine/src/flutter/flow/display_list.cc index 2de6809531b..7da9ec2b55d 100644 --- a/engine/src/flutter/flow/display_list.cc +++ b/engine/src/flutter/flow/display_list.cc @@ -188,6 +188,7 @@ struct SetBlendModeOp final : DLOp { dispatcher.set##name(field); \ } \ }; +DEFINE_SET_CLEAR_SKREF_OP(Blender, blender) DEFINE_SET_CLEAR_SKREF_OP(Shader, shader) DEFINE_SET_CLEAR_SKREF_OP(ImageFilter, filter) DEFINE_SET_CLEAR_SKREF_OP(ColorFilter, filter) @@ -1063,6 +1064,11 @@ void DisplayListBuilder::setColor(SkColor color) { void DisplayListBuilder::setBlendMode(SkBlendMode mode) { Push(0, mode); } +void DisplayListBuilder::setBlender(sk_sp blender) { + blender // + ? Push(0, std::move(blender)) + : Push(0); +} void DisplayListBuilder::setShader(sk_sp shader) { shader // ? Push(0, std::move(shader)) diff --git a/engine/src/flutter/flow/display_list.h b/engine/src/flutter/flow/display_list.h index 22858511b41..c1990eadeb0 100644 --- a/engine/src/flutter/flow/display_list.h +++ b/engine/src/flutter/flow/display_list.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_FLOW_DISPLAY_LIST_H_ #define FLUTTER_FLOW_DISPLAY_LIST_H_ +#include "third_party/skia/include/core/SkBlender.h" #include "third_party/skia/include/core/SkBlurTypes.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColorFilter.h" @@ -77,6 +78,8 @@ namespace flutter { V(SetColor) \ V(SetBlendMode) \ \ + V(SetBlender) \ + V(ClearBlender) \ V(SetShader) \ V(ClearShader) \ V(SetColorFilter) \ @@ -231,6 +234,7 @@ class Dispatcher { virtual void setMiterLimit(SkScalar limit) = 0; virtual void setColor(SkColor color) = 0; virtual void setBlendMode(SkBlendMode mode) = 0; + virtual void setBlender(sk_sp blender) = 0; virtual void setShader(sk_sp shader) = 0; virtual void setImageFilter(sk_sp filter) = 0; virtual void setColorFilter(sk_sp filter) = 0; @@ -343,6 +347,7 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { void setMiterLimit(SkScalar limit) override; void setColor(SkColor color) override; void setBlendMode(SkBlendMode mode) override; + void setBlender(sk_sp blender) override; void setShader(sk_sp shader) override; void setImageFilter(sk_sp filter) override; void setColorFilter(sk_sp filter) override; diff --git a/engine/src/flutter/flow/display_list_canvas.cc b/engine/src/flutter/flow/display_list_canvas.cc index 7e37e8f61d0..2da08e5406c 100644 --- a/engine/src/flutter/flow/display_list_canvas.cc +++ b/engine/src/flutter/flow/display_list_canvas.cc @@ -420,9 +420,19 @@ void DisplayListCanvasRecorder::RecordPaintAttributes(const SkPaint* paint, current_color_ != paint->getColor()) { builder_->setColor(current_color_ = paint->getColor()); } - if ((dataNeeded & kBlendNeeded_) != 0 && - current_blend_ != paint->getBlendMode()) { - builder_->setBlendMode(current_blend_ = paint->getBlendMode()); + if ((dataNeeded & kBlendNeeded_)) { + skstd::optional mode_optional = paint->asBlendMode(); + if (mode_optional) { + SkBlendMode mode = mode_optional.value(); + if (current_blender_ || current_blend_ != mode) { + builder_->setBlendMode(current_blend_ = mode); + current_blender_ = nullptr; + } + } else { + if (current_blender_.get() != paint->getBlender()) { + builder_->setBlender(current_blender_ = sk_ref_sp(paint->getBlender())); + } + } } // invert colors is a Flutter::Paint thing, not an SkPaint thing // if ((dataNeeded & invertColorsNeeded_) != 0 && diff --git a/engine/src/flutter/flow/display_list_canvas.h b/engine/src/flutter/flow/display_list_canvas.h index 76e49615dec..aa99ac8dc12 100644 --- a/engine/src/flutter/flow/display_list_canvas.h +++ b/engine/src/flutter/flow/display_list_canvas.h @@ -301,6 +301,7 @@ class DisplayListCanvasRecorder SkScalar current_miter_limit_ = 4.0; SkPaint::Cap current_cap_ = SkPaint::Cap::kButt_Cap; SkPaint::Join current_join_ = SkPaint::Join::kMiter_Join; + sk_sp current_blender_; sk_sp current_shader_; sk_sp current_color_filter_; sk_sp current_image_filter_; diff --git a/engine/src/flutter/flow/display_list_canvas_unittests.cc b/engine/src/flutter/flow/display_list_canvas_unittests.cc index a7419769a57..6b763bdf6fa 100644 --- a/engine/src/flutter/flow/display_list_canvas_unittests.cc +++ b/engine/src/flutter/flow/display_list_canvas_unittests.cc @@ -14,6 +14,7 @@ #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/core/SkVertices.h" +#include "third_party/skia/include/effects/SkBlenders.h" #include "third_party/skia/include/effects/SkDashPathEffect.h" #include "third_party/skia/include/effects/SkDiscretePathEffect.h" #include "third_party/skia/include/effects/SkGradientShader.h" @@ -45,7 +46,7 @@ constexpr SkRect RenderBounds = SkRect::MakeLTRB(RenderLeft, RenderTop, RenderRight, RenderBottom); class CanvasCompareTester { - public: + private: // If a test is using any shadow operations then we cannot currently // record those in an SkCanvas and play it back into a DisplayList // because internally the operation gets encapsulated in a Skia @@ -53,17 +54,61 @@ class CanvasCompareTester { // that use shadows, we can perform a lot of tests, but not the tests // that require SkCanvas->DisplayList transfers. // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 - static bool UsingShadows; + static bool TestingDrawShadows; + // The CPU renders nothing for drawVertices with a Blender. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12200 + static bool TestingDrawVertices; + // The CPU renders nothing for drawAtlas with a Blender. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12199 + static bool TestingDrawAtlas; + public: typedef const std::function CvRenderer; typedef const std::function DlRenderer; + // All of the tests should eventually use this method except for the + // tests that call |RenderNoAttributes| because they do not use the + // SkPaint object. + // But there are a couple of conditions beyond our control which require + // the use of one of the variant methods below (|RenderShadows|, + // |RenderVertices|, |RenderAtlas|). static void RenderAll(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { RenderWithAttributes(cv_renderer, dl_renderer); RenderWithTransforms(cv_renderer, dl_renderer); RenderWithClips(cv_renderer, dl_renderer); } + // Used by the tests that render shadows to deal with a condition where + // we cannot recapture the shadow information from an SkCanvas stream + // due to the DrawShadowRec used by Skia is not properly exported. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 + static void RenderShadows(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { + TestingDrawShadows = true; + RenderNoAttributes(cv_renderer, dl_renderer); + TestingDrawShadows = false; + } + + // Used by the tests that call drawVertices to avoid using an SkBlender + // during testing because the CPU renderer appears not to render anything. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12200 + static void RenderVertices(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { + TestingDrawVertices = true; + RenderAll(cv_renderer, dl_renderer); + TestingDrawVertices = false; + } + + // Used by the tests that call drawAtlas to avoid using an SkBlender + // during testing because the CPU renderer appears not to render anything. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12199 + static void RenderAtlas(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { + TestingDrawAtlas = true; + RenderAll(cv_renderer, dl_renderer); + TestingDrawAtlas = false; + } + + // Used by the tests that call a draw method that does not take a paint + // call. Those tests could use |RenderAll| but there would be a lot of + // wasted test runs that prepare an SkPaint that is never used. static void RenderNoAttributes(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { RenderWith([=](SkCanvas*, SkPaint& p) {}, // @@ -164,6 +209,26 @@ class CanvasCompareTester { cv_renderer, dl_renderer, "Blend == DstIn", &bg); } + if (!(TestingDrawAtlas || TestingDrawVertices)) { + sk_sp blender = + SkBlenders::Arithmetic(0.25, 0.25, 0.25, 0.25, false); + { + RenderWith([=](SkCanvas*, SkPaint& p) { p.setBlender(blender); }, + [=](DisplayListBuilder& b) { b.setBlender(blender); }, + cv_renderer, dl_renderer, + "ImageFilter == Blender Arithmetic 0.25-false"); + } + ASSERT_TRUE(blender->unique()) << "Blender Cleanup"; + blender = SkBlenders::Arithmetic(0.25, 0.25, 0.25, 0.25, true); + { + RenderWith([=](SkCanvas*, SkPaint& p) { p.setBlender(blender); }, + [=](DisplayListBuilder& b) { b.setBlender(blender); }, + cv_renderer, dl_renderer, + "ImageFilter == Blender Arithmetic 0.25-true"); + } + ASSERT_TRUE(blender->unique()) << "Blender Cleanup"; + } + { sk_sp filter = SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr, nullptr); @@ -654,7 +719,7 @@ class CanvasCompareTester { // This test cannot work if the rendering is using shadows until // we can access the Skia ShadowRec via public headers. - if (!UsingShadows) { + if (!TestingDrawShadows) { // This sequence renders SkCanvas calls to a DisplayList and then // plays them back on SkCanvas to SkSurface // SkCanvas calls => DisplayList => rendering @@ -783,7 +848,10 @@ class CanvasCompareTester { } }; -bool CanvasCompareTester::UsingShadows = false; +bool CanvasCompareTester::TestingDrawShadows = false; +bool CanvasCompareTester::TestingDrawVertices = false; +bool CanvasCompareTester::TestingDrawAtlas = false; + const sk_sp CanvasCompareTester::testImage = CanvasCompareTester::makeTestImage(); @@ -1020,7 +1088,7 @@ TEST(DisplayListCanvas, DrawVerticesWithColors) { const SkColor colors[3] = {SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN}; const sk_sp vertices = SkVertices::MakeCopy( SkVertices::kTriangles_VertexMode, 3, pts, nullptr, colors); - CanvasCompareTester::RenderAll( + CanvasCompareTester::RenderVertices( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); }, @@ -1045,7 +1113,7 @@ TEST(DisplayListCanvas, DrawVerticesWithImage) { SkVertices::kTriangles_VertexMode, 3, pts, tex, nullptr); const sk_sp shader = CanvasCompareTester::testImage->makeShader( SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions()); - CanvasCompareTester::RenderAll( + CanvasCompareTester::RenderVertices( [=](SkCanvas* canvas, SkPaint& paint) { // paint.setShader(shader); canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); @@ -1208,7 +1276,7 @@ TEST(DisplayListCanvas, DrawAtlasNearest) { SK_ColorGREEN, }; const sk_sp image = CanvasCompareTester::testImage; - CanvasCompareTester::RenderAll( + CanvasCompareTester::RenderAtlas( [=](SkCanvas* canvas, SkPaint& paint) { canvas->drawAtlas(image.get(), xform, tex, colors, 2, SkBlendMode::kSrcOver, DisplayList::NearestSampling, @@ -1235,7 +1303,7 @@ TEST(DisplayListCanvas, DrawAtlasLinear) { SK_ColorGREEN, }; const sk_sp image = CanvasCompareTester::testImage; - CanvasCompareTester::RenderAll( + CanvasCompareTester::RenderAtlas( [=](SkCanvas* canvas, SkPaint& paint) { canvas->drawAtlas(image.get(), xform, tex, colors, 2, // SkBlendMode::kSrcOver, DisplayList::LinearSampling, @@ -1333,7 +1401,6 @@ TEST(DisplayListCanvas, DrawTextBlob) { } TEST(DisplayListCanvas, DrawShadow) { - CanvasCompareTester::UsingShadows = true; SkPath path; path.moveTo(RenderCenterX, RenderTop); path.lineTo(RenderRight, RenderBottom); @@ -1344,7 +1411,7 @@ TEST(DisplayListCanvas, DrawShadow) { const SkColor color = SK_ColorDKGRAY; const SkScalar elevation = 10; - CanvasCompareTester::RenderNoAttributes( + CanvasCompareTester::RenderShadows( [=](SkCanvas* canvas, SkPaint& paint) { // PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, false, 1.0); @@ -1352,11 +1419,9 @@ TEST(DisplayListCanvas, DrawShadow) { [=](DisplayListBuilder& builder) { // builder.drawShadow(path, color, elevation, false, 1.0); }); - CanvasCompareTester::UsingShadows = false; } TEST(DisplayListCanvas, DrawOccludingShadow) { - CanvasCompareTester::UsingShadows = true; SkPath path; path.moveTo(RenderCenterX, RenderTop); path.lineTo(RenderRight, RenderBottom); @@ -1367,7 +1432,7 @@ TEST(DisplayListCanvas, DrawOccludingShadow) { const SkColor color = SK_ColorDKGRAY; const SkScalar elevation = 10; - CanvasCompareTester::RenderNoAttributes( + CanvasCompareTester::RenderShadows( [=](SkCanvas* canvas, SkPaint& paint) { // PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, true, 1.0); @@ -1375,11 +1440,9 @@ TEST(DisplayListCanvas, DrawOccludingShadow) { [=](DisplayListBuilder& builder) { // builder.drawShadow(path, color, elevation, true, 1.0); }); - CanvasCompareTester::UsingShadows = false; } TEST(DisplayListCanvas, DrawShadowDpr) { - CanvasCompareTester::UsingShadows = true; SkPath path; path.moveTo(RenderCenterX, RenderTop); path.lineTo(RenderRight, RenderBottom); @@ -1390,7 +1453,7 @@ TEST(DisplayListCanvas, DrawShadowDpr) { const SkColor color = SK_ColorDKGRAY; const SkScalar elevation = 10; - CanvasCompareTester::RenderNoAttributes( + CanvasCompareTester::RenderShadows( [=](SkCanvas* canvas, SkPaint& paint) { // PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, false, 2.5); @@ -1398,7 +1461,6 @@ TEST(DisplayListCanvas, DrawShadowDpr) { [=](DisplayListBuilder& builder) { // builder.drawShadow(path, color, elevation, false, 2.5); }); - CanvasCompareTester::UsingShadows = false; } } // namespace testing diff --git a/engine/src/flutter/flow/display_list_unittests.cc b/engine/src/flutter/flow/display_list_unittests.cc index 151a4f21ef4..bdaf92b0e99 100644 --- a/engine/src/flutter/flow/display_list_unittests.cc +++ b/engine/src/flutter/flow/display_list_unittests.cc @@ -14,6 +14,7 @@ #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/core/SkVertices.h" +#include "third_party/skia/include/effects/SkBlenders.h" #include "third_party/skia/include/effects/SkDashPathEffect.h" #include "third_party/skia/include/effects/SkGradientShader.h" #include "third_party/skia/include/effects/SkImageFilters.h" @@ -66,6 +67,12 @@ constexpr SkPoint TestPoints[] = { }; #define TestPointCount sizeof(TestPoints) / (sizeof(TestPoints[0])) +static const sk_sp TestBlender1 = + SkBlenders::Arithmetic(0.2, 0.2, 0.2, 0.2, false); +static const sk_sp TestBlender2 = + SkBlenders::Arithmetic(0.2, 0.2, 0.2, 0.2, true); +static const sk_sp TestBlender3 = + SkBlenders::Arithmetic(0.3, 0.3, 0.3, 0.3, true); static const sk_sp TestShader1 = SkGradientShader::MakeLinear(end_points, colors, @@ -295,6 +302,13 @@ std::vector allGroups = { {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kDstIn);}}, } }, + { "SetBlender", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setBlender(nullptr);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setBlender(TestBlender1);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setBlender(TestBlender2);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setBlender(TestBlender3);}}, + } + }, { "SetShader", { {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setShader(nullptr);}}, {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader1);}}, diff --git a/engine/src/flutter/flow/display_list_utils.cc b/engine/src/flutter/flow/display_list_utils.cc index 72780fa1fb6..8c3b6a1af9a 100644 --- a/engine/src/flutter/flow/display_list_utils.cc +++ b/engine/src/flutter/flow/display_list_utils.cc @@ -57,6 +57,9 @@ void SkPaintDispatchHelper::setColor(SkColor color) { void SkPaintDispatchHelper::setBlendMode(SkBlendMode mode) { paint_.setBlendMode(mode); } +void SkPaintDispatchHelper::setBlender(sk_sp blender) { + paint_.setBlender(blender); +} void SkPaintDispatchHelper::setShader(sk_sp shader) { paint_.setShader(shader); } diff --git a/engine/src/flutter/flow/display_list_utils.h b/engine/src/flutter/flow/display_list_utils.h index bba718e388f..b950ebd00f9 100644 --- a/engine/src/flutter/flow/display_list_utils.h +++ b/engine/src/flutter/flow/display_list_utils.h @@ -49,6 +49,7 @@ class IgnoreAttributeDispatchHelper : public virtual Dispatcher { void setMiterLimit(SkScalar limit) override {} void setColor(SkColor color) override {} void setBlendMode(SkBlendMode mode) override {} + void setBlender(sk_sp blender) override {} void setShader(sk_sp shader) override {} void setImageFilter(sk_sp filter) override {} void setColorFilter(sk_sp filter) override {} @@ -105,6 +106,7 @@ class SkPaintDispatchHelper : public virtual Dispatcher { void setMiterLimit(SkScalar limit) override; void setColor(SkColor color) override; void setBlendMode(SkBlendMode mode) override; + void setBlender(sk_sp blender) override; void setShader(sk_sp shader) override; void setImageFilter(sk_sp filter) override; void setColorFilter(sk_sp filter) override;