diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4e896a581d904..de7c844209f76 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -93,6 +93,9 @@ FILE: ../../../flutter/display_list/display_list_ops.h FILE: ../../../flutter/display_list/display_list_paint.cc FILE: ../../../flutter/display_list/display_list_paint.h FILE: ../../../flutter/display_list/display_list_paint_unittests.cc +FILE: ../../../flutter/display_list/display_list_path_effect.cc +FILE: ../../../flutter/display_list/display_list_path_effect.h +FILE: ../../../flutter/display_list/display_list_path_effect_unittests.cc FILE: ../../../flutter/display_list/display_list_test_utils.cc FILE: ../../../flutter/display_list/display_list_test_utils.h FILE: ../../../flutter/display_list/display_list_tile_mode.h diff --git a/display_list/BUILD.gn b/display_list/BUILD.gn index 1f919a9ec4971..eb2692869df83 100644 --- a/display_list/BUILD.gn +++ b/display_list/BUILD.gn @@ -46,6 +46,8 @@ source_set("display_list") { "display_list_ops.h", "display_list_paint.cc", "display_list_paint.h", + "display_list_path_effect.cc", + "display_list_path_effect.h", "display_list_tile_mode.h", "display_list_utils.cc", "display_list_utils.h", @@ -94,6 +96,7 @@ if (enable_unittests) { "display_list_image_filter_unittests.cc", "display_list_mask_filter_unittests.cc", "display_list_paint_unittests.cc", + "display_list_path_effect_unittests.cc", "display_list_unittests.cc", "display_list_vertices_unittests.cc", ] diff --git a/display_list/display_list.h b/display_list/display_list.h index 651332a375380..204a4d895364b 100644 --- a/display_list/display_list.h +++ b/display_list/display_list.h @@ -74,7 +74,9 @@ namespace flutter { \ V(SetBlender) \ V(ClearBlender) \ - V(SetPathEffect) \ + \ + V(SetSkPathEffect) \ + V(SetPodPathEffect) \ V(ClearPathEffect) \ \ V(ClearColorFilter) \ diff --git a/display_list/display_list_builder.cc b/display_list/display_list_builder.cc index 10331d98f24bb..96aacead6684b 100644 --- a/display_list/display_list_builder.cc +++ b/display_list/display_list_builder.cc @@ -275,10 +275,25 @@ void DisplayListBuilder::onSetColorFilter(const DlColorFilter* filter) { } UpdateCurrentOpacityCompatibility(); } -void DisplayListBuilder::onSetPathEffect(sk_sp effect) { - (current_path_effect_ = effect) // - ? Push(0, 0, std::move(effect)) - : Push(0, 0); +void DisplayListBuilder::onSetPathEffect(const DlPathEffect* effect) { + if (effect == nullptr) { + current_.setPathEffect(nullptr); + Push(0, 0); + } else { + current_.setPathEffect(effect->shared()); + switch (effect->type()) { + case DlPathEffectType::kDash: { + const DlDashPathEffect* dash_effect = effect->asDash(); + void* pod = Push(dash_effect->size(), 0); + new (pod) DlDashPathEffect(dash_effect); + break; + } + case DlPathEffectType::kUnknown: { + Push(0, 0, effect->skia_object()); + break; + } + } + } } void DisplayListBuilder::onSetMaskFilter(const DlMaskFilter* filter) { if (filter == nullptr) { @@ -389,7 +404,8 @@ void DisplayListBuilder::setAttributesFromPaint( setImageFilter(DlImageFilter::From(paint.getImageFilter()).get()); } if (flags.applies_path_effect()) { - setPathEffect(sk_ref_sp(paint.getPathEffect())); + SkPathEffect* path_effect = paint.getPathEffect(); + setPathEffect(DlPathEffect::From(path_effect).get()); } if (flags.applies_mask_filter()) { SkMaskFilter* mask_filter = paint.getMaskFilter(); diff --git a/display_list/display_list_builder.h b/display_list/display_list_builder.h index 6bdc3dc913e66..49b085d648f10 100644 --- a/display_list/display_list_builder.h +++ b/display_list/display_list_builder.h @@ -12,6 +12,7 @@ #include "flutter/display_list/display_list_flags.h" #include "flutter/display_list/display_list_image.h" #include "flutter/display_list/display_list_paint.h" +#include "flutter/display_list/display_list_path_effect.h" #include "flutter/display_list/types.h" #include "flutter/fml/macros.h" @@ -102,9 +103,9 @@ class DisplayListBuilder final : public virtual Dispatcher, onSetColorFilter(filter); } } - void setPathEffect(sk_sp effect) override { - if (current_path_effect_ != effect) { - onSetPathEffect(std::move(effect)); + void setPathEffect(const DlPathEffect* effect) override { + if (NotEquals(current_.getPathEffect(), effect)) { + onSetPathEffect(effect); } } void setMaskFilter(const DlMaskFilter* filter) override { @@ -139,7 +140,9 @@ class DisplayListBuilder final : public virtual Dispatcher, return current_blender_ ? current_blender_ : SkBlender::Mode(ToSk(current_.getBlendMode())); } - sk_sp getPathEffect() const { return current_path_effect_; } + std::shared_ptr getPathEffect() const { + return current_.getPathEffect(); + } std::shared_ptr getMaskFilter() const { return current_.getMaskFilter(); } @@ -453,14 +456,13 @@ class DisplayListBuilder final : public virtual Dispatcher, void onSetColorSource(const DlColorSource* source); void onSetImageFilter(const DlImageFilter* filter); void onSetColorFilter(const DlColorFilter* filter); - void onSetPathEffect(sk_sp effect); + void onSetPathEffect(const DlPathEffect* effect); void onSetMaskFilter(const DlMaskFilter* filter); void onSetMaskBlurFilter(SkBlurStyle style, SkScalar sigma); DlPaint current_; // If |current_blender_| is set then ignore |current_.getBlendMode()| sk_sp current_blender_; - sk_sp current_path_effect_; }; } // namespace flutter diff --git a/display_list/display_list_canvas_unittests.cc b/display_list/display_list_canvas_unittests.cc index 4ac2cb018c7d0..99966bb2d0522 100644 --- a/display_list/display_list_canvas_unittests.cc +++ b/display_list/display_list_canvas_unittests.cc @@ -387,13 +387,12 @@ class TestParameters { NotEquals(ref_attr.getColorSource(), attr.getColorSource())) { return false; } + DisplayListSpecialGeometryFlags geo_flags = - flags_.WithPathEffect(attr.getPathEffect()); + flags_.WithPathEffect(attr.getPathEffect().get()); if (flags_.applies_path_effect() && // ref_attr.getPathEffect() != attr.getPathEffect()) { - SkPathEffect::DashInfo info; - if (attr.getPathEffect()->asADash(&info) != - SkPathEffect::kDash_DashType) { + if (attr.getPathEffect()->asDash() == nullptr) { return false; } if (!ignores_dashes()) { @@ -484,8 +483,10 @@ class TestParameters { adjust = half_width * paint.getStrokeMiter() + tolerance.discrete_offset(); } + auto paint_effect = paint.refPathEffect(); + DisplayListSpecialGeometryFlags geo_flags = - flags_.WithPathEffect(paint.refPathEffect()); + flags_.WithPathEffect(DlPathEffect::From(paint.refPathEffect()).get()); if (paint.getStrokeCap() == SkPaint::kButt_Cap && !geo_flags.butt_cap_becomes_square()) { adjust = std::max(adjust, half_width); @@ -1254,7 +1255,7 @@ class CanvasCompareTester { [=](DisplayListBuilder& b) { b.setStrokeWidth(5.0); b.setStrokeMiter(3.0); - b.setPathEffect(effect); + b.setPathEffect(DlPathEffect::From(effect).get()); })); } EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique()) @@ -1283,7 +1284,7 @@ class CanvasCompareTester { [=](DisplayListBuilder& b) { b.setStrokeWidth(5.0); b.setStrokeMiter(2.5); - b.setPathEffect(effect); + b.setPathEffect(DlPathEffect::From(effect).get()); })); } EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique()) @@ -1503,7 +1504,7 @@ class CanvasCompareTester { { const SkScalar TestDashes1[] = {29.0, 2.0}; const SkScalar TestDashes2[] = {17.0, 1.5}; - sk_sp effect = SkDashPathEffect::Make(TestDashes1, 2, 0.0f); + auto effect = DlDashPathEffect::Make(TestDashes1, 2, 0.0f); { RenderWith(testP, stroke_base_env, tolerance, CaseParameters( @@ -1513,19 +1514,17 @@ class CanvasCompareTester { p.setStyle(SkPaint::kStroke_Style); // Provide some non-trivial stroke size to get dashed p.setStrokeWidth(5.0); - p.setPathEffect(effect); + p.setPathEffect(effect->skia_object()); }, [=](DisplayListBuilder& b) { // Need stroke style to see dashing properly b.setStyle(DlDrawStyle::kStroke); // Provide some non-trivial stroke size to get dashed b.setStrokeWidth(5.0); - b.setPathEffect(effect); + b.setPathEffect(effect.get()); })); } - EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique()) - << "PathEffect == Dash-29-2 Cleanup"; - effect = SkDashPathEffect::Make(TestDashes2, 2, 0.0f); + effect = DlDashPathEffect::Make(TestDashes2, 2, 0.0f); { RenderWith(testP, stroke_base_env, tolerance, CaseParameters( @@ -1535,18 +1534,16 @@ class CanvasCompareTester { p.setStyle(SkPaint::kStroke_Style); // Provide some non-trivial stroke size to get dashed p.setStrokeWidth(5.0); - p.setPathEffect(effect); + p.setPathEffect(effect->skia_object()); }, [=](DisplayListBuilder& b) { // Need stroke style to see dashing properly b.setStyle(DlDrawStyle::kStroke); // Provide some non-trivial stroke size to get dashed b.setStrokeWidth(5.0); - b.setPathEffect(effect); + b.setPathEffect(effect.get()); })); } - EXPECT_TRUE(testP.is_draw_text_blob() || effect->unique()) - << "PathEffect == Dash-17-1.5 Cleanup"; } } diff --git a/display_list/display_list_complexity_helper.h b/display_list/display_list_complexity_helper.h index efaf0e97f83ac..e17c41638beb8 100644 --- a/display_list/display_list_complexity_helper.h +++ b/display_list/display_list_complexity_helper.h @@ -112,7 +112,7 @@ class ComplexityCalculatorHelper void setColorSource(const DlColorSource* source) override {} void setImageFilter(const DlImageFilter* filter) override {} void setColorFilter(const DlColorFilter* filter) override {} - void setPathEffect(sk_sp effect) override {} + void setPathEffect(const DlPathEffect* effect) override {} void setMaskFilter(const DlMaskFilter* filter) override {} void save() override {} diff --git a/display_list/display_list_dispatcher.h b/display_list/display_list_dispatcher.h index ce2236a0f25eb..c12fd90467f04 100644 --- a/display_list/display_list_dispatcher.h +++ b/display_list/display_list_dispatcher.h @@ -13,6 +13,7 @@ #include "flutter/display_list/display_list_image_filter.h" #include "flutter/display_list/display_list_mask_filter.h" #include "flutter/display_list/display_list_paint.h" +#include "flutter/display_list/display_list_path_effect.h" #include "flutter/display_list/display_list_vertices.h" namespace flutter { @@ -52,7 +53,7 @@ class Dispatcher { virtual void setInvertColors(bool invert) = 0; virtual void setBlendMode(DlBlendMode mode) = 0; virtual void setBlender(sk_sp blender) = 0; - virtual void setPathEffect(sk_sp effect) = 0; + virtual void setPathEffect(const DlPathEffect* effect) = 0; virtual void setMaskFilter(const DlMaskFilter* filter) = 0; virtual void setImageFilter(const DlImageFilter* filter) = 0; diff --git a/display_list/display_list_flags.cc b/display_list/display_list_flags.cc index 36ed9ea48d871..2b0a9d9043c2d 100644 --- a/display_list/display_list_flags.cc +++ b/display_list/display_list_flags.cc @@ -3,9 +3,28 @@ // found in the LICENSE file. #include "flutter/display_list/display_list_flags.h" - +#include "flutter/display_list/display_list_path_effect.h" namespace flutter { -// Just exists to ensure that the header can be cleanly imported. +const DisplayListSpecialGeometryFlags DisplayListAttributeFlags::WithPathEffect( + const DlPathEffect* effect) const { + if (is_geometric() && effect) { + if (effect->asDash()) { + // A dash effect has a very simple impact. It cannot introduce any + // miter joins that weren't already present in the original path + // and it does not grow the bounds of the path, but it can add + // end caps to areas that might not have had them before so all + // we need to do is to indicate the potential for diagonal + // end caps and move on. + return special_flags_.with(kMayHaveCaps_ | kMayHaveDiagonalCaps_); + } else { + // An arbitrary path effect can introduce joins at an arbitrary + // angle and may change the geometry of the end caps + return special_flags_.with(kMayHaveCaps_ | kMayHaveDiagonalCaps_ | + kMayHaveJoins_ | kMayHaveAcuteJoins_); + } + } + return special_flags_; +} } // namespace flutter diff --git a/display_list/display_list_flags.h b/display_list/display_list_flags.h index 568be0aa47ce3..f5362599c3b93 100644 --- a/display_list/display_list_flags.h +++ b/display_list/display_list_flags.h @@ -11,6 +11,7 @@ namespace flutter { +class DlPathEffect; /// The base class for the classes that maintain a list of /// attributes that might be important for a number of operations /// including which rendering attributes need to be set before @@ -159,26 +160,7 @@ class DisplayListSpecialGeometryFlags : DisplayListFlagsBase { class DisplayListAttributeFlags : DisplayListFlagsBase { public: const DisplayListSpecialGeometryFlags WithPathEffect( - sk_sp effect) const { - if (is_geometric() && effect) { - SkPathEffect::DashInfo info; - if (effect->asADash(&info) == SkPathEffect::kDash_DashType) { - // A dash effect has a very simple impact. It cannot introduce any - // miter joins that weren't already present in the original path - // and it does not grow the bounds of the path, but it can add - // end caps to areas that might not have had them before so all - // we need to do is to indicate the potential for diagonal - // end caps and move on. - return special_flags_.with(kMayHaveCaps_ | kMayHaveDiagonalCaps_); - } else { - // An arbitrary path effect can introduce joins at an arbitrary - // angle and may change the geometry of the end caps - return special_flags_.with(kMayHaveCaps_ | kMayHaveDiagonalCaps_ | - kMayHaveJoins_ | kMayHaveAcuteJoins_); - } - } - return special_flags_; - } + const DlPathEffect* effect) const; bool ignores_paint() const { return has_any(kIgnoresPaint_); } diff --git a/display_list/display_list_ops.h b/display_list/display_list_ops.h index d451accd6556f..91893813628f5 100644 --- a/display_list/display_list_ops.h +++ b/display_list/display_list_ops.h @@ -176,7 +176,6 @@ struct SetBlendModeOp final : DLOp { } \ }; DEFINE_SET_CLEAR_SKREF_OP(Blender, blender) -DEFINE_SET_CLEAR_SKREF_OP(PathEffect, effect) #undef DEFINE_SET_CLEAR_SKREF_OP // Clear: 4 byte header + unused 4 byte payload uses 8 bytes @@ -224,6 +223,7 @@ DEFINE_SET_CLEAR_DLATTR_OP(ColorFilter, ColorFilter, filter) DEFINE_SET_CLEAR_DLATTR_OP(ImageFilter, ImageFilter, filter) DEFINE_SET_CLEAR_DLATTR_OP(MaskFilter, MaskFilter, filter) DEFINE_SET_CLEAR_DLATTR_OP(ColorSource, Shader, source) +DEFINE_SET_CLEAR_DLATTR_OP(PathEffect, PathEffect, effect) #undef DEFINE_SET_CLEAR_DLATTR_OP // 4 byte header + 80 bytes for the embedded DlImageColorSource diff --git a/display_list/display_list_paint.h b/display_list/display_list_paint.h index 47a5b5fee23db..2892d13685633 100644 --- a/display_list/display_list_paint.h +++ b/display_list/display_list_paint.h @@ -5,12 +5,14 @@ #ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_PAINT_H_ #define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_PAINT_H_ +#include #include "flutter/display_list/display_list_blend_mode.h" #include "flutter/display_list/display_list_color.h" #include "flutter/display_list/display_list_color_filter.h" #include "flutter/display_list/display_list_color_source.h" #include "flutter/display_list/display_list_image_filter.h" #include "flutter/display_list/display_list_mask_filter.h" +#include "flutter/display_list/display_list_path_effect.h" namespace flutter { @@ -199,6 +201,15 @@ class DlPaint { return *this; } + std::shared_ptr getPathEffect() const { + return pathEffect_; + } + const DlPathEffect* getPathEffectPtr() const { return pathEffect_.get(); } + DlPaint& setPathEffect(std::shared_ptr pathEffect) { + pathEffect_ = pathEffect; + return *this; + } + bool operator==(DlPaint const& other) const; bool operator!=(DlPaint const& other) const { return !(*this == other); } @@ -236,8 +247,8 @@ class DlPaint { std::shared_ptr colorFilter_; std::shared_ptr imageFilter_; std::shared_ptr maskFilter_; + std::shared_ptr pathEffect_; // missing (as compared to SkPaint): - // DlPathEffect - waiting for https://github.com/flutter/engine/pull/32159 // DlBlender - not planning on using that object in a pure DisplayList world }; diff --git a/display_list/display_list_path_effect.cc b/display_list/display_list_path_effect.cc new file mode 100644 index 0000000000000..72297464e4f15 --- /dev/null +++ b/display_list/display_list_path_effect.cc @@ -0,0 +1,75 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/display_list/display_list_path_effect.h" + +#include +#include +#include + +#include "include/core/SkRefCnt.h" +#include "include/core/SkScalar.h" + +namespace flutter { + +static void DlPathEffectDeleter(void* p) { + // Some of our target environments would prefer a sized delete, + // but other target environments do not have that operator. + // Use an unsized delete until we get better agreement in the + // environments. + // See https://github.com/flutter/flutter/issues/100327 + ::operator delete(p); +} + +std::shared_ptr DlPathEffect::From(SkPathEffect* sk_path_effect) { + if (sk_path_effect == nullptr) { + return nullptr; + } + + SkPathEffect::DashInfo info; + if (SkPathEffect::DashType::kDash_DashType == + sk_path_effect->asADash(&info)) { + auto dash_path_effect = + DlDashPathEffect::Make(nullptr, info.fCount, info.fPhase); + info.fIntervals = + reinterpret_cast(dash_path_effect.get()) + ->intervals_unsafe(); + sk_path_effect->asADash(&info); + return dash_path_effect; + } + // If not dash path effect, we will use UnknownPathEffect to wrap it. + return std::make_shared(sk_ref_sp(sk_path_effect)); +} + +std::shared_ptr DlDashPathEffect::Make(const SkScalar* intervals, + int count, + SkScalar phase) { + size_t needed = sizeof(DlDashPathEffect) + sizeof(SkScalar) * count; + void* storage = ::operator new(needed); + + std::shared_ptr ret; + ret.reset(new (storage) DlDashPathEffect(intervals, count, phase), + DlPathEffectDeleter); + return std::move(ret); +} + +std::optional DlDashPathEffect::effect_bounds(SkRect& rect) const { + // SkDashPathEffect returns the original bounds as the bounds of the effect + // since the dashed path will always be a subset of the original. + return rect; +} + +std::optional DlUnknownPathEffect::effect_bounds(SkRect& rect) const { + if (!rect.isSorted()) { + return std::nullopt; + } + SkPaint p; + p.setPathEffect(sk_path_effect_); + if (!p.canComputeFastBounds()) { + return std::nullopt; + } + return p.computeFastBounds(rect, &rect); +} + +} // namespace flutter diff --git a/display_list/display_list_path_effect.h b/display_list/display_list_path_effect.h new file mode 100644 index 0000000000000..a6d2f02361df3 --- /dev/null +++ b/display_list/display_list_path_effect.h @@ -0,0 +1,174 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_PATH_EFFECT_H_ +#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_PATH_EFFECT_H_ + +#include +#include +#include "flutter/display_list/display_list_attributes.h" +#include "flutter/display_list/types.h" +#include "flutter/fml/logging.h" +#include "include/core/SkScalar.h" + +namespace flutter { + +class DlDashPathEffect; + +// The DisplayList PathEffect class. This class implements all of the +// facilities and adheres to the design goals of the |DlAttribute| base +// class. + +// An enumerated type for the recognized PathEffect operations. +// In current Flutter we only use the DashPathEffect. +// And another PathEffect outside of the recognized types is needed +// then a |kUnknown| type that simply defers to an SkPathEffect is +// provided as a fallback. +enum class DlPathEffectType { + kDash, + kUnknown, +}; + +class DlPathEffect + : public DlAttribute { + public: + static std::shared_ptr From(SkPathEffect* sk_path_effect); + + static std::shared_ptr From( + sk_sp sk_path_effect) { + return From(sk_path_effect.get()); + } + + virtual const DlDashPathEffect* asDash() const { return nullptr; } + + virtual std::optional effect_bounds(SkRect&) const = 0; + + protected: + DlPathEffect() = default; + + private: + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlPathEffect); +}; + +/// The DashPathEffect which breaks a path up into dash segments, and it +/// only affects stroked paths. +/// intervals: array containing an even number of entries (>=2), with +/// the even indices specifying the length of "on" intervals, and the odd +/// indices specifying the length of "off" intervals. This array will be +/// copied in Make, and can be disposed of freely after. +/// count: number of elements in the intervals array. +/// phase: initial distance into the intervals at which to start the dashing +/// effect for the path. +/// +/// For example: if intervals[] = {10, 20}, count = 2, and phase = 25, +/// this will set up a dashed path like so: +/// 5 pixels off +/// 10 pixels on +/// 20 pixels off +/// 10 pixels on +/// 20 pixels off +/// ... +/// A phase of -5, 25, 55, 85, etc. would all result in the same path, +/// because the sum of all the intervals is 30. +/// +class DlDashPathEffect final : public DlPathEffect { + public: + static std::shared_ptr Make(const SkScalar intervals[], + int count, + SkScalar phase); + + DlPathEffectType type() const override { return DlPathEffectType::kDash; } + size_t size() const override { + return sizeof(*this) + sizeof(SkScalar) * count_; + } + + std::shared_ptr shared() const override { + return Make(intervals(), count_, phase_); + } + + const DlDashPathEffect* asDash() const override { return this; } + + sk_sp skia_object() const override { + return SkDashPathEffect::Make(intervals(), count_, phase_); + } + + const SkScalar* intervals() const { + return reinterpret_cast(this + 1); + } + + std::optional effect_bounds(SkRect& rect) const override; + + protected: + bool equals_(DlPathEffect const& other) const override { + FML_DCHECK(other.type() == DlPathEffectType::kDash); + auto that = static_cast(&other); + return count_ == that->count_ && phase_ == that->phase_ && + memcmp(intervals(), that->intervals(), sizeof(SkScalar) * count_) == + 0; + } + + private: + // DlDashPathEffect constructor assumes the caller has prealloced storage for + // the intervals. If the intervals is nullptr the intervals will + // uninitialized. + DlDashPathEffect(const SkScalar intervals[], int count, SkScalar phase) + : count_(count), phase_(phase) { + if (intervals != nullptr) { + SkScalar* intervals_ = reinterpret_cast(this + 1); + memcpy(intervals_, intervals, sizeof(SkScalar) * count); + } + } + + DlDashPathEffect(const DlDashPathEffect* dash_effect) + : DlDashPathEffect(dash_effect->intervals(), + dash_effect->count_, + dash_effect->phase_) {} + + SkScalar* intervals_unsafe() { return reinterpret_cast(this + 1); } + + int count_; + SkScalar phase_; + + friend class DisplayListBuilder; + friend class DlPathEffect; + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlDashPathEffect); +}; + +class DlUnknownPathEffect final : public DlPathEffect { + public: + DlUnknownPathEffect(sk_sp effect) + : sk_path_effect_(std::move(effect)) {} + DlUnknownPathEffect(const DlUnknownPathEffect& effect) + : DlUnknownPathEffect(effect.sk_path_effect_) {} + DlUnknownPathEffect(const DlUnknownPathEffect* effect) + : DlUnknownPathEffect(effect->sk_path_effect_) {} + + DlPathEffectType type() const override { return DlPathEffectType::kUnknown; } + size_t size() const override { return sizeof(*this); } + + std::shared_ptr shared() const override { + return std::make_shared(this); + } + + sk_sp skia_object() const override { return sk_path_effect_; } + + virtual ~DlUnknownPathEffect() = default; + + std::optional effect_bounds(SkRect& rect) const override; + + protected: + bool equals_(const DlPathEffect& other) const override { + FML_DCHECK(other.type() == DlPathEffectType::kUnknown); + auto that = static_cast(&other); + return sk_path_effect_ == that->sk_path_effect_; + } + + private: + sk_sp sk_path_effect_; +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_PATH_EFFECT_H_ diff --git a/display_list/display_list_path_effect_unittests.cc b/display_list/display_list_path_effect_unittests.cc new file mode 100644 index 0000000000000..91501ecf873e3 --- /dev/null +++ b/display_list/display_list_path_effect_unittests.cc @@ -0,0 +1,124 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/display_list/display_list_attributes_testing.h" +#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/display_list_comparable.h" +#include "flutter/display_list/types.h" +#include "gtest/gtest.h" +#include "include/core/SkPath.h" +#include "include/core/SkScalar.h" + +namespace flutter { +namespace testing { + +TEST(DisplayListPathEffect, BuilderSetGet) { + const SkScalar TestDashes1[] = {4.0, 2.0}; + auto dash_path_effect = DlDashPathEffect::Make(TestDashes1, 2, 0.0); + DisplayListBuilder builder; + ASSERT_EQ(builder.getPathEffect(), nullptr); + builder.setPathEffect(dash_path_effect.get()); + ASSERT_NE(builder.getPathEffect(), nullptr); + ASSERT_TRUE(Equals(builder.getPathEffect(), + static_cast(dash_path_effect.get()))); + builder.setPathEffect(nullptr); + ASSERT_EQ(builder.getPathEffect(), nullptr); +} + +TEST(DisplayListPathEffect, FromSkiaNullPathEffect) { + std::shared_ptr path_effect = DlPathEffect::From(nullptr); + ASSERT_EQ(path_effect, nullptr); + ASSERT_EQ(path_effect.get(), nullptr); +} + +TEST(DisplayListPathEffect, FromSkiaPathEffect) { + const SkScalar TestDashes2[] = {1.0, 1.5}; + sk_sp sk_path_effect = + SkDashPathEffect::Make(TestDashes2, 2, 0.0); + std::shared_ptr dl_path_effect = + DlPathEffect::From(sk_path_effect); + + ASSERT_EQ(dl_path_effect->type(), DlPathEffectType::kDash); + ASSERT_TRUE( + Equals(dl_path_effect, DlDashPathEffect::Make(TestDashes2, 2, 0.0))); +} + +TEST(DisplayListPathEffect, EffectShared) { + const SkScalar TestDashes2[] = {1.0, 1.5}; + auto effect = DlDashPathEffect::Make(TestDashes2, 2, 0.0); + ASSERT_TRUE(Equals(effect->shared(), effect)); +} + +TEST(DisplayListPathEffect, DashEffectAsDash) { + const SkScalar TestDashes2[] = {1.0, 1.5}; + auto effect = DlDashPathEffect::Make(TestDashes2, 2, 0.0); + ASSERT_NE(effect->asDash(), nullptr); + ASSERT_EQ(effect->asDash(), effect.get()); +} + +TEST(DisplayListPathEffect, DashEffectEquals) { + const SkScalar TestDashes2[] = {1.0, 1.5}; + auto effect1 = DlDashPathEffect::Make(TestDashes2, 2, 0.0); + auto effect2 = DlDashPathEffect::Make(TestDashes2, 2, 0.0); + TestEquals(*effect1, *effect1); +} + +TEST(DisplayListPathEffect, CheckEffectProperties) { + const SkScalar TestDashes1[] = {4.0, 2.0}; + const SkScalar TestDashes2[] = {5.0, 2.0}; + const SkScalar TestDashes3[] = {4.0, 3.0}; + const SkScalar TestDashes4[] = {4.0, 2.0, 6.0}; + auto effect1 = DlDashPathEffect::Make(TestDashes1, 2, 0.0); + auto effect2 = DlDashPathEffect::Make(TestDashes2, 2, 0.0); + auto effect3 = DlDashPathEffect::Make(TestDashes3, 2, 0.0); + auto effect4 = DlDashPathEffect::Make(TestDashes4, 3, 0.0); + auto effect5 = DlDashPathEffect::Make(TestDashes1, 2, 1.0); + + TestNotEquals(*effect1, *effect2, "Interval 1 differs"); + TestNotEquals(*effect1, *effect3, "Interval 2 differs"); + TestNotEquals(*effect1, *effect4, "Dash count differs"); + TestNotEquals(*effect1, *effect5, "Dash phase differs"); +} + +TEST(DisplayListPathEffect, UnknownConstructor) { + const SkScalar TestDashes1[] = {4.0, 2.0}; + DlUnknownPathEffect path_effect(SkDashPathEffect::Make(TestDashes1, 2, 0.0)); +} + +TEST(DisplayListPathEffect, UnknownShared) { + const SkScalar TestDashes1[] = {4.0, 2.0}; + DlUnknownPathEffect path_effect(SkDashPathEffect::Make(TestDashes1, 2, 0.0)); + ASSERT_NE(path_effect.shared().get(), &path_effect); + ASSERT_EQ(*path_effect.shared(), path_effect); +} + +TEST(DisplayListPathEffect, UnknownContents) { + const SkScalar TestDashes1[] = {4.0, 2.0}; + sk_sp sk_effect = SkDashPathEffect::Make(TestDashes1, 2, 0.0); + DlUnknownPathEffect effect(sk_effect); + ASSERT_EQ(effect.skia_object(), sk_effect); + ASSERT_EQ(effect.skia_object().get(), sk_effect.get()); +} + +TEST(DisplayListPathEffect, UnknownEquals) { + const SkScalar TestDashes1[] = {4.0, 2.0}; + sk_sp sk_effect = SkDashPathEffect::Make(TestDashes1, 2, 0.0); + DlUnknownPathEffect effect1(sk_effect); + DlUnknownPathEffect effect2(sk_effect); + TestEquals(effect1, effect1); +} + +TEST(DisplayListPathEffect, UnknownNotEquals) { + const SkScalar TestDashes1[] = {4.0, 2.0}; + // Even though the effect is the same, it is a different instance + // and we cannot currently tell them apart because the Skia + // DashEffect::Make objects do not implement == + DlUnknownPathEffect path_effect1(SkDashPathEffect::Make(TestDashes1, 2, 0.0)); + DlUnknownPathEffect path_effect2(SkDashPathEffect::Make(TestDashes1, 2, 0.0)); + TestNotEquals(path_effect1, path_effect2, + "SkDashPathEffect instance differs"); +} + +} // namespace testing +} // namespace flutter diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index 23f4156594f01..320a40ee528f6 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "flutter/display_list/display_list.h" #include "flutter/display_list/display_list_builder.h" #include "flutter/display_list/display_list_canvas_recorder.h" @@ -173,10 +174,10 @@ static const DlComposeImageFilter TestComposeImageFilter3( TestMatrixImageFilter2); static const DlColorFilterImageFilter TestCFImageFilter1(TestBlendColorFilter1); static const DlColorFilterImageFilter TestCFImageFilter2(TestBlendColorFilter2); -static const sk_sp TestPathEffect1 = - SkDashPathEffect::Make(TestDashes1, 2, 0.0f); -static const sk_sp TestPathEffect2 = - SkDashPathEffect::Make(TestDashes2, 2, 0.0f); +static const std::shared_ptr TestPathEffect1 = + DlDashPathEffect::Make(TestDashes1, 2, 0.0f); +static const std::shared_ptr TestPathEffect2 = + DlDashPathEffect::Make(TestDashes2, 2, 0.0f); static const DlBlurMaskFilter TestMaskFilter1(kNormal_SkBlurStyle, 3.0); static const DlBlurMaskFilter TestMaskFilter2(kNormal_SkBlurStyle, 5.0); static const DlBlurMaskFilter TestMaskFilter3(kSolid_SkBlurStyle, 3.0); @@ -386,6 +387,7 @@ std::vector allGroups = { }, { "SetColorSource", { {0, 112, 0, 0, [](DisplayListBuilder& b) {b.setColorSource(&TestSource1);}}, + // stop_count * (sizeof(float) + sizeof(uint32_t)) = 80 {0, 80 + 6 * 4, 0, 0, [](DisplayListBuilder& b) {b.setColorSource(TestSource2.get());}}, {0, 80 + 6 * 4, 0, 0, [](DisplayListBuilder& b) {b.setColorSource(TestSource3.get());}}, {0, 88 + 6 * 4, 0, 0, [](DisplayListBuilder& b) {b.setColorSource(TestSource4.get());}}, @@ -427,8 +429,9 @@ std::vector allGroups = { } }, { "SetPathEffect", { - {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setPathEffect(TestPathEffect1);}}, - {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setPathEffect(TestPathEffect2);}}, + // sizeof(DlDashPathEffect) + 2 * sizeof(SkScalar) + {0, 32, 0, 0, [](DisplayListBuilder& b) {b.setPathEffect(TestPathEffect1.get());}}, + {0, 32, 0, 0, [](DisplayListBuilder& b) {b.setPathEffect(TestPathEffect2.get());}}, {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setPathEffect(nullptr);}}, } }, @@ -1333,27 +1336,6 @@ TEST(DisplayList, DisplayListBlenderRefHandling) { ASSERT_TRUE(tester.ref_is_unique()); } -TEST(DisplayList, DisplayListPathEffectRefHandling) { - class PathEffectRefTester : public virtual AttributeRefTester { - public: - void setRefToPaint(SkPaint& paint) const override { - paint.setPathEffect(path_effect); - } - void setRefToDisplayList(DisplayListBuilder& builder) const override { - builder.setPathEffect(path_effect); - } - bool ref_is_unique() const override { return path_effect->unique(); } - - private: - sk_sp path_effect = - SkDashPathEffect::Make(TestDashes1, 2, 0.0); - }; - - PathEffectRefTester tester; - tester.test(); - ASSERT_TRUE(tester.ref_is_unique()); -} - TEST(DisplayList, DisplayListFullPerspectiveTransformHandling) { // SkM44 constructor takes row-major order SkM44 sk_matrix = SkM44( diff --git a/display_list/display_list_utils.cc b/display_list/display_list_utils.cc index 123465df8c6ad..3d74a87873a52 100644 --- a/display_list/display_list_utils.cc +++ b/display_list/display_list_utils.cc @@ -85,8 +85,8 @@ void SkPaintDispatchHelper::setColorFilter(const DlColorFilter* filter) { color_filter_ = filter ? filter->shared() : nullptr; paint_.setColorFilter(makeColorFilter()); } -void SkPaintDispatchHelper::setPathEffect(sk_sp effect) { - paint_.setPathEffect(effect); +void SkPaintDispatchHelper::setPathEffect(const DlPathEffect* effect) { + paint_.setPathEffect(effect ? effect->skia_object() : nullptr); } void SkPaintDispatchHelper::setMaskFilter(const DlMaskFilter* filter) { paint_.setMaskFilter(filter ? filter->skia_object() : nullptr); @@ -277,8 +277,8 @@ void DisplayListBoundsCalculator::setImageFilter(const DlImageFilter* filter) { void DisplayListBoundsCalculator::setColorFilter(const DlColorFilter* filter) { color_filter_ = filter ? filter->shared() : nullptr; } -void DisplayListBoundsCalculator::setPathEffect(sk_sp effect) { - path_effect_ = std::move(effect); +void DisplayListBoundsCalculator::setPathEffect(const DlPathEffect* effect) { + path_effect_ = effect ? effect->shared() : nullptr; } void DisplayListBoundsCalculator::setMaskFilter(const DlMaskFilter* filter) { mask_filter_ = filter ? filter->shared() : nullptr; @@ -580,14 +580,13 @@ bool DisplayListBoundsCalculator::AdjustBoundsForPaint( if (flags.is_geometric()) { // Path effect occurs before stroking... DisplayListSpecialGeometryFlags special_flags = - flags.WithPathEffect(path_effect_); + flags.WithPathEffect(path_effect_.get()); if (path_effect_) { - SkPaint p; - p.setPathEffect(path_effect_); - if (!p.canComputeFastBounds()) { + auto effect_bounds = path_effect_->effect_bounds(bounds); + if (!effect_bounds.has_value()) { return false; } - bounds = p.computeFastBounds(bounds, &bounds); + bounds = effect_bounds.value(); } if (flags.is_stroked(style_)) { diff --git a/display_list/display_list_utils.h b/display_list/display_list_utils.h index 648dff9ad42fa..8f3dd5486be3a 100644 --- a/display_list/display_list_utils.h +++ b/display_list/display_list_utils.h @@ -58,7 +58,7 @@ class IgnoreAttributeDispatchHelper : public virtual Dispatcher { void setColorSource(const DlColorSource* source) override {} void setImageFilter(const DlImageFilter* filter) override {} void setColorFilter(const DlColorFilter* filter) override {} - void setPathEffect(sk_sp effect) override {} + void setPathEffect(const DlPathEffect* effect) override {} void setMaskFilter(const DlMaskFilter* filter) override {} }; @@ -185,7 +185,7 @@ class SkPaintDispatchHelper : public virtual Dispatcher { void setInvertColors(bool invert) override; void setBlendMode(DlBlendMode mode) override; void setBlender(sk_sp blender) override; - void setPathEffect(sk_sp effect) override; + void setPathEffect(const DlPathEffect* effect) override; void setMaskFilter(const DlMaskFilter* filter) override; void setImageFilter(const DlImageFilter* filter) override; @@ -404,7 +404,7 @@ class DisplayListBoundsCalculator final void setBlender(sk_sp blender) override; void setImageFilter(const DlImageFilter* filter) override; void setColorFilter(const DlColorFilter* filter) override; - void setPathEffect(sk_sp effect) override; + void setPathEffect(const DlPathEffect* effect) override; void setMaskFilter(const DlMaskFilter* filter) override; void save() override; @@ -582,7 +582,7 @@ class DisplayListBoundsCalculator final bool join_is_miter_ = true; bool cap_is_square_ = false; std::shared_ptr image_filter_; - sk_sp path_effect_; + std::shared_ptr path_effect_; std::shared_ptr mask_filter_; bool paint_nops_on_transparency(); diff --git a/display_list/types.h b/display_list/types.h index e00bc96056363..6d1e06971552b 100644 --- a/display_list/types.h +++ b/display_list/types.h @@ -25,7 +25,9 @@ #include "third_party/skia/include/core/SkShader.h" #include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/core/SkVertices.h" +#include "third_party/skia/include/effects/SkCornerPathEffect.h" +#include "third_party/skia/include/effects/SkDashPathEffect.h" +#include "third_party/skia/include/effects/SkDiscretePathEffect.h" #include "third_party/skia/include/gpu/GrTypes.h" #include "third_party/skia/include/utils/SkShadowUtils.h" - #endif // FLUTTER_DISPLAY_LIST_TYPES_H_ diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index b29ba0f850eca..017359b1fb211 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -6,6 +6,7 @@ #include +#include "display_list/display_list_path_effect.h" #include "flutter/fml/trace_event.h" #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/linear_gradient_contents.h" @@ -249,7 +250,7 @@ void DisplayListDispatcher::setBlender(sk_sp blender) { } // |flutter::Dispatcher| -void DisplayListDispatcher::setPathEffect(sk_sp effect) { +void DisplayListDispatcher::setPathEffect(const flutter::DlPathEffect* effect) { // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index fd51545aadbe5..cf5864671d431 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -4,6 +4,7 @@ #pragma once +#include "display_list/display_list_path_effect.h" #include "flutter/display_list/display_list.h" #include "flutter/display_list/display_list_blend_mode.h" #include "flutter/display_list/display_list_dispatcher.h" @@ -61,7 +62,7 @@ class DisplayListDispatcher final : public flutter::Dispatcher { void setBlender(sk_sp blender) override; // |flutter::Dispatcher| - void setPathEffect(sk_sp effect) override; + void setPathEffect(const flutter::DlPathEffect* effect) override; // |flutter::Dispatcher| void setMaskFilter(const flutter::DlMaskFilter* filter) override; diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc index b9ca0b1e05ae5..ed8b19b014deb 100644 --- a/testing/display_list_testing.cc +++ b/testing/display_list_testing.cc @@ -479,7 +479,7 @@ void DisplayListStreamDispatcher::setBlendMode(DlBlendMode mode) { void DisplayListStreamDispatcher::setBlender(sk_sp blender) { startl() << "setBlender(" << blender << ");" << std::endl; } -void DisplayListStreamDispatcher::setPathEffect(sk_sp effect) { +void DisplayListStreamDispatcher::setPathEffect(const DlPathEffect* effect) { startl() << "setPathEffect(" << effect << ");" << std::endl; } void DisplayListStreamDispatcher::setMaskFilter(const DlMaskFilter* filter) { diff --git a/testing/display_list_testing.h b/testing/display_list_testing.h index 34a0ce1189e15..cadc7c6e8fc66 100644 --- a/testing/display_list_testing.h +++ b/testing/display_list_testing.h @@ -9,6 +9,7 @@ #include "flutter/display_list/display_list.h" #include "flutter/display_list/display_list_dispatcher.h" +#include "flutter/display_list/display_list_path_effect.h" namespace flutter { namespace testing { @@ -53,7 +54,7 @@ class DisplayListStreamDispatcher final : public Dispatcher { void setInvertColors(bool invert) override; void setBlendMode(DlBlendMode mode) override; void setBlender(sk_sp blender) override; - void setPathEffect(sk_sp effect) override; + void setPathEffect(const DlPathEffect* effect) override; void setMaskFilter(const DlMaskFilter* filter) override; void setImageFilter(const DlImageFilter* filter) override;