diff --git a/DEPS b/DEPS index 081ae73c7e0d9..8ec587359bdb7 100644 --- a/DEPS +++ b/DEPS @@ -110,7 +110,7 @@ deps = { 'src': 'https://github.com/flutter/buildroot.git' + '@' + '79643299bd052c53631b8b200bb582e8badb2708', 'src/flutter/impeller': - Var('github_git') + '/flutter/impeller' + '@' + 'c55014e747541b9a2cca15e6b2cb1b1ef9123da3', + Var('github_git') + '/flutter/impeller' + '@' + '78bc2a026c554c444b2e67b36fd44cd548341a51', # Fuchsia compatibility # diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 43bcb9aa830c0..12acb82b16796 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -52,6 +52,9 @@ FILE: ../../../flutter/display_list/display_list_canvas_dispatcher.h FILE: ../../../flutter/display_list/display_list_canvas_recorder.cc FILE: ../../../flutter/display_list/display_list_canvas_recorder.h FILE: ../../../flutter/display_list/display_list_canvas_unittests.cc +FILE: ../../../flutter/display_list/display_list_color_filter.cc +FILE: ../../../flutter/display_list/display_list_color_filter.h +FILE: ../../../flutter/display_list/display_list_color_filter_unittests.cc FILE: ../../../flutter/display_list/display_list_complexity.cc FILE: ../../../flutter/display_list/display_list_complexity.h FILE: ../../../flutter/display_list/display_list_dispatcher.cc diff --git a/display_list/BUILD.gn b/display_list/BUILD.gn index 44482cb983018..76dc9b591fcce 100644 --- a/display_list/BUILD.gn +++ b/display_list/BUILD.gn @@ -14,6 +14,8 @@ source_set("display_list") { "display_list_canvas_dispatcher.h", "display_list_canvas_recorder.cc", "display_list_canvas_recorder.h", + "display_list_color_filter.cc", + "display_list_color_filter.h", "display_list_complexity.cc", "display_list_complexity.h", "display_list_dispatcher.cc", @@ -40,6 +42,7 @@ source_set("unittests") { sources = [ "display_list_canvas_unittests.cc", + "display_list_color_filter_unittests.cc", "display_list_unittests.cc", ] diff --git a/display_list/display_list.h b/display_list/display_list.h index 46bbb03a67125..3871c96fd2eb9 100644 --- a/display_list/display_list.h +++ b/display_list/display_list.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_FLOW_DISPLAY_LIST_H_ -#define FLUTTER_FLOW_DISPLAY_LIST_H_ +#ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_H_ +#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_H_ #include @@ -76,13 +76,15 @@ namespace flutter { V(ClearBlender) \ V(SetShader) \ V(ClearShader) \ - V(SetColorFilter) \ - V(ClearColorFilter) \ V(SetImageFilter) \ V(ClearImageFilter) \ V(SetPathEffect) \ V(ClearPathEffect) \ \ + V(ClearColorFilter) \ + V(SetColorFilter) \ + V(SetSkColorFilter) \ + \ V(ClearMaskFilter) \ V(SetMaskFilter) \ V(SetMaskBlurFilterNormal) \ @@ -278,4 +280,4 @@ class DisplayList : public SkRefCnt { } // namespace flutter -#endif // FLUTTER_FLOW_DISPLAY_LIST_H_ +#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_H_ diff --git a/display_list/display_list_builder.cc b/display_list/display_list_builder.cc index 0bae1483ea724..086da0e3a12f9 100644 --- a/display_list/display_list_builder.cc +++ b/display_list/display_list_builder.cc @@ -134,10 +134,50 @@ void DisplayListBuilder::onSetImageFilter(sk_sp filter) { ? Push(0, 0, std::move(filter)) : Push(0, 0); } -void DisplayListBuilder::onSetColorFilter(sk_sp filter) { - (current_color_filter_ = filter) // - ? Push(0, 0, std::move(filter)) - : Push(0, 0); +void DisplayListBuilder::onSetColorFilter(const DlColorFilter* filter) { + if (filter == nullptr) { + if (!current_color_filter_) { + return; + } + current_color_filter_ = nullptr; + Push(0, 0); + } else { + if (current_color_filter_ && *current_color_filter_ == *filter) { + return; + } + current_color_filter_ = filter->shared(); + switch (filter->type()) { + case DlColorFilter::kBlend: { + const DlBlendColorFilter* blend_filter = filter->asBlend(); + FML_DCHECK(blend_filter); + void* pod = Push(blend_filter->size(), 0); + new (pod) DlBlendColorFilter(blend_filter); + break; + } + case DlColorFilter::kMatrix: { + const DlMatrixColorFilter* matrix_filter = filter->asMatrix(); + FML_DCHECK(matrix_filter); + void* pod = Push(matrix_filter->size(), 0); + new (pod) DlMatrixColorFilter(matrix_filter); + break; + } + case DlColorFilter::kSrgbToLinearGamma: { + void* pod = Push(filter->size(), 0); + new (pod) DlSrgbToLinearGammaColorFilter(); + break; + } + case DlColorFilter::kLinearToSrgbGamma: { + void* pod = Push(filter->size(), 0); + new (pod) DlLinearToSrgbGammaColorFilter(); + break; + } + case DlColorFilter::kUnknown: { + const sk_sp sk_filter = filter->sk_filter(); + Push(0, 0, sk_filter); + break; + } + } + } UpdateCurrentOpacityCompatibility(); } void DisplayListBuilder::onSetPathEffect(sk_sp effect) { @@ -211,7 +251,12 @@ void DisplayListBuilder::setAttributesFromPaint( // we must clear it because it is a second potential color filter // that is composed with the paint's color filter. setInvertColors(false); - setColorFilter(sk_ref_sp(paint.getColorFilter())); + SkColorFilter* color_filter = paint.getColorFilter(); + if (color_filter) { + setColorFilter(DlColorFilter::From(color_filter).get()); + } else { + setColorFilter(nullptr); + } } if (flags.applies_image_filter()) { setImageFilter(sk_ref_sp(paint.getImageFilter())); diff --git a/display_list/display_list_builder.h b/display_list/display_list_builder.h index 580ab4ecec2ca..8cd08ad49ba08 100644 --- a/display_list/display_list_builder.h +++ b/display_list/display_list_builder.h @@ -93,10 +93,9 @@ class DisplayListBuilder final : public virtual Dispatcher, onSetImageFilter(std::move(filter)); } } - void setColorFilter(sk_sp filter) override { - if (current_color_filter_ != filter) { - onSetColorFilter(std::move(filter)); - } + void setColorFilter(const DlColorFilter* filter) override { + // onSetColorFilter will deal with whether the filter is new + onSetColorFilter(filter); } void setPathEffect(sk_sp effect) override { if (current_path_effect_ != effect) { @@ -128,7 +127,9 @@ class DisplayListBuilder final : public virtual Dispatcher, SkPaint::Cap getStrokeCap() const { return current_stroke_cap_; } SkPaint::Join getStrokeJoin() const { return current_stroke_join_; } sk_sp getShader() const { return current_shader_; } - sk_sp getColorFilter() const { return current_color_filter_; } + sk_sp getColorFilter() const { + return current_color_filter_->sk_filter(); + } bool isInvertColors() const { return current_invert_colors_; } std::optional getBlendMode() const { if (current_blender_) { @@ -390,7 +391,7 @@ class DisplayListBuilder final : public virtual Dispatcher, void onSetBlender(sk_sp blender); void onSetShader(sk_sp shader); void onSetImageFilter(sk_sp filter); - void onSetColorFilter(sk_sp filter); + void onSetColorFilter(const DlColorFilter* filter); void onSetPathEffect(sk_sp effect); void onSetMaskFilter(sk_sp filter); void onSetMaskBlurFilter(SkBlurStyle style, SkScalar sigma); @@ -409,7 +410,7 @@ class DisplayListBuilder final : public virtual Dispatcher, SkBlendMode current_blend_mode_ = SkBlendMode::kSrcOver; sk_sp current_blender_; sk_sp current_shader_; - sk_sp current_color_filter_; + std::shared_ptr current_color_filter_; sk_sp current_image_filter_; sk_sp current_path_effect_; sk_sp current_mask_filter_; diff --git a/display_list/display_list_canvas_unittests.cc b/display_list/display_list_canvas_unittests.cc index f8c9eb5bfa3ea..e0edc4b98c955 100644 --- a/display_list/display_list_canvas_unittests.cc +++ b/display_list/display_list_canvas_unittests.cc @@ -836,48 +836,43 @@ class CanvasCompareTester { 0, 0, 0, 0.5, 0, }; // clang-format on - sk_sp filter = - SkColorFilters::Matrix(rotate_alpha_color_matrix); + DlMatrixColorFilter filter(rotate_alpha_color_matrix); { RenderWith(testP, env, tolerance, CaseParameters( "saveLayer ColorFilter, no bounds", [=](SkCanvas* cv, SkPaint& p) { SkPaint save_p; - save_p.setColorFilter(filter); + save_p.setColorFilter(filter.sk_filter()); cv->saveLayer(nullptr, &save_p); p.setStrokeWidth(5.0); }, [=](DisplayListBuilder& b) { - b.setColorFilter(filter); + b.setColorFilter(&filter); b.saveLayer(nullptr, true); b.setColorFilter(nullptr); b.setStrokeWidth(5.0); }) .with_restore(cv_safe_restore, dl_safe_restore, true)); } - EXPECT_TRUE(filter->unique()) - << "saveLayer ColorFilter, no bounds Cleanup"; { RenderWith(testP, env, tolerance, CaseParameters( "saveLayer ColorFilter and bounds", [=](SkCanvas* cv, SkPaint& p) { SkPaint save_p; - save_p.setColorFilter(filter); + save_p.setColorFilter(filter.sk_filter()); cv->saveLayer(RenderBounds, &save_p); p.setStrokeWidth(5.0); }, [=](DisplayListBuilder& b) { - b.setColorFilter(filter); + b.setColorFilter(&filter); b.saveLayer(&RenderBounds, true); b.setColorFilter(nullptr); b.setStrokeWidth(5.0); }) .with_restore(cv_safe_restore, dl_safe_restore, true)); } - EXPECT_TRUE(filter->unique()) - << "saveLayer ColorFilter and bounds Cleanup"; } { sk_sp filter = SkImageFilters::Arithmetic( @@ -1146,7 +1141,7 @@ class CanvasCompareTester { 1.0, 1.0, 1.0, 1.0, 0, }; // clang-format on - sk_sp filter = SkColorFilters::Matrix(rotate_color_matrix); + DlMatrixColorFilter filter(rotate_color_matrix); { SkColor bg = SK_ColorWHITE; RenderWith(testP, env, tolerance, @@ -1154,16 +1149,15 @@ class CanvasCompareTester { "ColorFilter == RotateRGB", [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorYELLOW); - p.setColorFilter(filter); + p.setColorFilter(filter.sk_filter()); }, [=](DisplayListBuilder& b) { b.setColor(SK_ColorYELLOW); - b.setColorFilter(filter); + b.setColorFilter(&filter); }) .with_bg(bg)); } - EXPECT_TRUE(filter->unique()) << "ColorFilter == RotateRGB Cleanup"; - filter = SkColorFilters::Matrix(invert_color_matrix); + filter = DlMatrixColorFilter(invert_color_matrix); { SkColor bg = SK_ColorWHITE; RenderWith(testP, env, tolerance, @@ -1171,7 +1165,7 @@ class CanvasCompareTester { "ColorFilter == Invert", [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorYELLOW); - p.setColorFilter(filter); + p.setColorFilter(filter.sk_filter()); }, [=](DisplayListBuilder& b) { b.setColor(SK_ColorYELLOW); @@ -1179,7 +1173,6 @@ class CanvasCompareTester { }) .with_bg(bg)); } - EXPECT_TRUE(filter->unique()) << "ColorFilter == Invert Cleanup"; } { diff --git a/display_list/display_list_color_filter.cc b/display_list/display_list_color_filter.cc new file mode 100644 index 0000000000000..8781b014161f1 --- /dev/null +++ b/display_list/display_list_color_filter.cc @@ -0,0 +1,49 @@ +// 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_color_filter.h" + +namespace flutter { + +std::shared_ptr DlColorFilter::From(SkColorFilter* sk_filter) { + if (sk_filter == nullptr) { + return nullptr; + } + if (sk_filter == DlSrgbToLinearGammaColorFilter::sk_filter_.get()) { + // Skia implements these filters as a singleton. + return DlSrgbToLinearGammaColorFilter::instance; + } + if (sk_filter == DlLinearToSrgbGammaColorFilter::sk_filter_.get()) { + // Skia implements these filters as a singleton. + return DlLinearToSrgbGammaColorFilter::instance; + } + { + SkColor color; + SkBlendMode mode; + if (sk_filter->asAColorMode(&color, &mode)) { + return std::make_shared(color, mode); + } + } + { + float matrix[20]; + if (sk_filter->asAColorMatrix(matrix)) { + return std::make_shared(matrix); + } + } + return std::make_shared(sk_ref_sp(sk_filter)); +} + +const std::shared_ptr + DlSrgbToLinearGammaColorFilter::instance = + std::make_shared(); +const sk_sp DlSrgbToLinearGammaColorFilter::sk_filter_ = + SkColorFilters::SRGBToLinearGamma(); + +const std::shared_ptr + DlLinearToSrgbGammaColorFilter::instance = + std::make_shared(); +const sk_sp DlLinearToSrgbGammaColorFilter::sk_filter_ = + SkColorFilters::LinearToSRGBGamma(); + +} // namespace flutter diff --git a/display_list/display_list_color_filter.h b/display_list/display_list_color_filter.h new file mode 100644 index 0000000000000..0918bad6d8411 --- /dev/null +++ b/display_list/display_list_color_filter.h @@ -0,0 +1,361 @@ +// 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_COLOR_FILTER_H_ +#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_COLOR_FILTER_H_ + +#include "flutter/display_list/types.h" +#include "flutter/fml/logging.h" + +namespace flutter { + +class DlBlendColorFilter; +class DlMatrixColorFilter; + +// The DisplayList ColorFilter class. This class was designed to be: +// +// - Typed: +// Even though most references and pointers are passed around as the +// base class, a DlColorFilter::Type can be queried using the |type| +// method to determine which type of ColorFilter is being used. +// +// - Inspectable: +// Any parameters required to full specify the filtering operations are +// provided on the specific base classes. +// +// - Safely Downcast: +// For the subclasses that have specific data to query, methods |asBlend| +// and |asMatrix| are provided to safely downcast the reference for +// inspection. +// +// - Skiafiable: +// The classes override an |sk_filter| method to easily obtain a Skia +// version of the filter on demand. +// +// - Immutable: +// Neither the base class or any of the subclasses specify any mutation +// methods. Instances are often passed around as const as a reminder, +// but the classes have no mutation methods anyway. +// +// - Flat and Embeddable: +// Bulk freed + bulk compared + zero memory fragmentation. +// +// All of these classes are designed to be stored in the DisplayList +// buffer allocated in-line with the rest of the data to avoid dangling +// pointers that require explicit freeing when the DisplayList goes +// away, or that fragment the memory needed to read the operations in +// the DisplayList. Furthermore, the data in the classes can be bulk +// compared using a |memcmp| when performing a |DisplayList::Equals|. +// +// - Passed by Pointer: +// The data shared via the |Dispatcher::setColorFilter| call is stored +// in the buffer itself and so its lifetime is controlled by the +// DisplayList. That memory cannot be shared as by a |shared_ptr| +// because the memory may be freed outside the control of the shared +// pointer. Creating a shared version of the object would require a +// new instantiation which we'd like to avoid on every dispatch call, +// so a raw (const) pointer is shared instead with all of the +// responsibilities of non-ownership in the called method. +// +// But, for methods that need to keep a copy of the data... +// +// - Shared_Ptr-able: +// The classes support a method to return a |std::shared_ptr| version of +// themselves, safely instantiating a new copy of the object into a +// shared_ptr using |std::make_shared|. For those dispatcher objects +// that may want to hold on to the contents of the object (typically +// in a |current_color_filter_| field), they can obtain a shared_ptr +// copy safely and easily using the |shared| method. + +class DlColorFilter { + public: + // An enumerated type for the recognized ColorFilter operations. + // If a custom ColorFilter outside of the recognized types is needed + // then a |kUnknown| type that simply defers to an SkColorFilter is + // provided as a fallback. + enum Type { + kBlend, + kMatrix, + kSrgbToLinearGamma, + kLinearToSrgbGamma, + kUnknown + }; + + // Return a shared_ptr holding a DlColorFilter representing the indicated + // Skia SkColorFilter pointer. + // + // This method can detect each of the 4 recognized types from an analogous + // SkColorFilter. + static std::shared_ptr From(SkColorFilter* sk_filter); + + // Return a shared_ptr holding a DlColorFilter representing the indicated + // Skia SkColorFilter pointer. + // + // This method can detect each of the 4 recognized types from an analogous + // SkColorFilter. + static std::shared_ptr From(sk_sp sk_filter) { + return From(sk_filter.get()); + } + + // Return the recognized type of the ColorFilter operation. + virtual Type type() const = 0; + + // Return the size of the instantiated data (typically used to allocate) + // storage in the DisplayList buffer. + virtual size_t size() const = 0; + + // Return a boolean indicating whether the color filtering operation will + // modify transparent black. This is typically used to determine if applying + // the ColorFilter to a temporary saveLayer buffer will turn the surrounding + // pixels non-transparent and therefore expand the bounds. + virtual bool modifies_transparent_black() const = 0; + + // Return a shared version of a DlColorFilter pointer, or nullptr if the + // pointer is null. + static std::shared_ptr Shared(const DlColorFilter* filter) { + return filter == nullptr ? nullptr : filter->shared(); + } + + // Return a shared version of |this| ColorFilter. The |shared_ptr| returned + // will reference a copy of this object so that the lifetime of the shared + // version is not tied to the storage of this particular instance. + virtual std::shared_ptr shared() const = 0; + + // Return an equivalent |SkColorFilter| version of this object. + virtual sk_sp sk_filter() const = 0; + + // Return a DlBlendColorFilter pointer to this object iff it is a Blend + // type of ColorFilter, otherwise return nullptr. + virtual const DlBlendColorFilter* asBlend() const { return nullptr; } + + // Return a DlMatrixColorFilter pointer to this object iff it is a Matrix + // type of ColorFilter, otherwise return nullptr. + virtual const DlMatrixColorFilter* asMatrix() const { return nullptr; } + + // asSrgb<->Linear and asUnknown are not needed because they + // have no properties to query. Their type fully specifies their + // operation or can be accessed via the common sk_filter() method. + + // Perform a content aware |==| comparison of the ColorFilter. + bool operator==(DlColorFilter const& other) const { + return type() == other.type() && equals_(other); + } + // Perform a content aware |!=| comparison of the ColorFilter. + bool operator!=(DlColorFilter const& other) const { + return !(*this == other); + } + + virtual ~DlColorFilter() = default; + + protected: + // Virtual comparison method to support |==| and |!=|. + virtual bool equals_(DlColorFilter const& other) const = 0; +}; + +// The Blend type of ColorFilter which specifies modifying the +// colors as if the color specified in the Blend filter is the +// source color and the color drawn by the rendering operation +// is the destination color. The mode parameter of the Blend +// filter is then used to combine those colors. +class DlBlendColorFilter final : public DlColorFilter { + public: + DlBlendColorFilter(SkColor color, SkBlendMode mode) + : color_(color), mode_(mode) {} + DlBlendColorFilter(const DlBlendColorFilter& filter) + : DlBlendColorFilter(filter.color_, filter.mode_) {} + DlBlendColorFilter(const DlBlendColorFilter* filter) + : DlBlendColorFilter(filter->color_, filter->mode_) {} + + Type type() const override { return kBlend; } + size_t size() const override { return sizeof(*this); } + bool modifies_transparent_black() const override { + // Look at blend and color to make a faster determination? + return sk_filter()->filterColor(SK_ColorTRANSPARENT) != SK_ColorTRANSPARENT; + } + + std::shared_ptr shared() const override { + return std::make_shared(this); + } + + sk_sp sk_filter() const override { + return SkColorFilters::Blend(color_, mode_); + } + + const DlBlendColorFilter* asBlend() const override { return this; } + + SkColor color() const { return color_; } + SkBlendMode mode() const { return mode_; } + + protected: + bool equals_(DlColorFilter const& other) const override { + FML_DCHECK(other.type() == kBlend); + auto that = static_cast(other); + return color_ == that.color_ && mode_ == that.mode_; + } + + private: + SkColor color_; + SkBlendMode mode_; +}; + +// The Matrix type of ColorFilter which runs every pixel drawn by +// the rendering operation [iR,iG,iB,iA] through a vector/matrix +// multiplication, as in: +// +// [ oR ] [ m[ 0] m[ 1] m[ 2] m[ 3] m[ 4] ] [ iR ] +// [ oG ] [ m[ 5] m[ 6] m[ 7] m[ 8] m[ 9] ] [ iG ] +// [ oB ] = [ m[10] m[11] m[12] m[13] m[14] ] x [ iB ] +// [ oA ] [ m[15] m[16] m[17] m[18] m[19] ] [ iA ] +// [ 1 ] +// +// The resulting color [oR,oG,oB,oA] is then clamped to the range of +// valid pixel components before storing in the output. +class DlMatrixColorFilter final : public DlColorFilter { + public: + DlMatrixColorFilter(const float matrix[20]) { + memcpy(matrix_, matrix, sizeof(matrix_)); + } + DlMatrixColorFilter(const DlMatrixColorFilter& filter) + : DlMatrixColorFilter(filter.matrix_) {} + DlMatrixColorFilter(const DlMatrixColorFilter* filter) + : DlMatrixColorFilter(filter->matrix_) {} + + Type type() const override { return kMatrix; } + size_t size() const override { return sizeof(*this); } + bool modifies_transparent_black() const override { + // Look at the matrix to make a faster determination? + // Basically, are the translation components all 0? + return sk_filter()->filterColor(SK_ColorTRANSPARENT) != SK_ColorTRANSPARENT; + } + + std::shared_ptr shared() const override { + return std::make_shared(this); + } + + sk_sp sk_filter() const override { + return SkColorFilters::Matrix(matrix_); + } + + const DlMatrixColorFilter* asMatrix() const override { return this; } + + const float& operator[](int index) const { return matrix_[index]; } + void get_matrix(float matrix[20]) const { + memcpy(matrix, matrix_, sizeof(matrix_)); + } + + protected: + bool equals_(const DlColorFilter& other) const override { + FML_DCHECK(other.type() == kMatrix); + auto that = static_cast(other); + return memcmp(matrix_, that.matrix_, sizeof(matrix_)) == 0; + } + + private: + float matrix_[20]; +}; + +// The SrgbToLinear type of ColorFilter that applies the inverse of the sRGB +// gamma curve to the rendered pixels. +class DlSrgbToLinearGammaColorFilter final : public DlColorFilter { + public: + static const std::shared_ptr instance; + + DlSrgbToLinearGammaColorFilter() {} + DlSrgbToLinearGammaColorFilter(const DlSrgbToLinearGammaColorFilter& filter) + : DlSrgbToLinearGammaColorFilter() {} + DlSrgbToLinearGammaColorFilter(const DlSrgbToLinearGammaColorFilter* filter) + : DlSrgbToLinearGammaColorFilter() {} + + Type type() const override { return kSrgbToLinearGamma; } + size_t size() const override { return sizeof(*this); } + bool modifies_transparent_black() const override { return false; } + + std::shared_ptr shared() const override { return instance; } + sk_sp sk_filter() const override { return sk_filter_; } + + protected: + bool equals_(const DlColorFilter& other) const override { + FML_DCHECK(other.type() == kSrgbToLinearGamma); + return true; + } + + private: + static const sk_sp sk_filter_; + friend class DlColorFilter; +}; + +// The LinearToSrgb type of ColorFilter that applies the sRGB gamma curve +// to the rendered pixels. +class DlLinearToSrgbGammaColorFilter final : public DlColorFilter { + public: + static const std::shared_ptr instance; + + DlLinearToSrgbGammaColorFilter() {} + DlLinearToSrgbGammaColorFilter(const DlLinearToSrgbGammaColorFilter& filter) + : DlLinearToSrgbGammaColorFilter() {} + DlLinearToSrgbGammaColorFilter(const DlLinearToSrgbGammaColorFilter* filter) + : DlLinearToSrgbGammaColorFilter() {} + + Type type() const override { return kLinearToSrgbGamma; } + size_t size() const override { return sizeof(*this); } + bool modifies_transparent_black() const override { return false; } + + std::shared_ptr shared() const override { return instance; } + sk_sp sk_filter() const override { return sk_filter_; } + + protected: + bool equals_(const DlColorFilter& other) const override { + FML_DCHECK(other.type() == kLinearToSrgbGamma); + return true; + } + + private: + static const sk_sp sk_filter_; + friend class DlColorFilter; +}; + +// A wrapper class for a Skia ColorFilter of unknown type. The above 4 types +// are the only types that can be constructed by Flutter using the +// ui.ColorFilter class so this class should be rarely used. The main use +// would come from the |DisplayListCanvasRecorder| recording Skia rendering +// calls that originated outside of the Flutter dart code. This would +// primarily happen in the Paragraph code that renders the text using the +// SkCanvas interface which we capture into DisplayList data structures. +class DlUnknownColorFilter final : public DlColorFilter { + public: + DlUnknownColorFilter(sk_sp sk_filter) + : sk_filter_(std::move(sk_filter)) {} + DlUnknownColorFilter(const DlUnknownColorFilter& filter) + : DlUnknownColorFilter(filter.sk_filter_) {} + DlUnknownColorFilter(const DlUnknownColorFilter* filter) + : DlUnknownColorFilter(filter->sk_filter_) {} + + Type type() const override { return kUnknown; } + size_t size() const override { return sizeof(*this); } + bool modifies_transparent_black() const override { + return sk_filter()->filterColor(SK_ColorTRANSPARENT) != SK_ColorTRANSPARENT; + } + + std::shared_ptr shared() const override { + return std::make_shared(this); + } + + sk_sp sk_filter() const override { return sk_filter_; } + + virtual ~DlUnknownColorFilter() = default; + + protected: + bool equals_(const DlColorFilter& other) const override { + FML_DCHECK(other.type() == kUnknown); + auto that = static_cast(other); + return sk_filter_ == that.sk_filter_; + } + + private: + sk_sp sk_filter_; +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_COLOR_FILTER_H_ diff --git a/display_list/display_list_color_filter_unittests.cc b/display_list/display_list_color_filter_unittests.cc new file mode 100644 index 0000000000000..b9de53264a549 --- /dev/null +++ b/display_list/display_list_color_filter_unittests.cc @@ -0,0 +1,266 @@ +// 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_color_filter.h" +#include "flutter/display_list/types.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +static const float matrix[20] = { + 1, 2, 3, 4, 5, // + 6, 7, 8, 9, 10, // + 11, 12, 13, 14, 15, // + 16, 17, 18, 19, 20, // +}; + +TEST(DisplayListColorFilter, FromSkiaNullFilter) { + std::shared_ptr filter = DlColorFilter::From(nullptr); + ASSERT_EQ(filter, nullptr); +} + +TEST(DisplayListColorFilter, FromSkiaBlendFilter) { + sk_sp sk_filter = + SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kDstATop); + std::shared_ptr filter = DlColorFilter::From(sk_filter); + DlBlendColorFilter dl_filter(SK_ColorRED, SkBlendMode::kDstATop); + ASSERT_EQ(filter->type(), DlColorFilter::kBlend); + ASSERT_NE(filter->asBlend(), nullptr); + ASSERT_EQ(filter->asMatrix(), nullptr); + ASSERT_EQ(*filter->asBlend(), dl_filter); + ASSERT_EQ(filter->asBlend()->color(), SK_ColorRED); + ASSERT_EQ(filter->asBlend()->mode(), SkBlendMode::kDstATop); +} + +TEST(DisplayListColorFilter, FromSkiaMatrixFilter) { + sk_sp sk_filter = SkColorFilters::Matrix(matrix); + std::shared_ptr filter = DlColorFilter::From(sk_filter); + DlMatrixColorFilter dl_filter(matrix); + ASSERT_EQ(filter->type(), DlColorFilter::kMatrix); + ASSERT_EQ(filter->asBlend(), nullptr); + ASSERT_NE(filter->asMatrix(), nullptr); + ASSERT_EQ(*filter->asMatrix(), dl_filter); + const DlMatrixColorFilter* matrix_filter = filter->asMatrix(); + for (int i = 0; i < 20; i++) { + ASSERT_EQ((*matrix_filter)[i], matrix[i]); + } +} + +TEST(DisplayListColorFilter, FromSkiaSrgbToLinearFilter) { + sk_sp sk_filter = SkColorFilters::SRGBToLinearGamma(); + std::shared_ptr filter = DlColorFilter::From(sk_filter); + ASSERT_EQ(filter->type(), DlColorFilter::kSrgbToLinearGamma); + ASSERT_EQ(filter->asBlend(), nullptr); + ASSERT_EQ(filter->asMatrix(), nullptr); +} + +TEST(DisplayListColorFilter, FromSkiaLinearToSrgbFilter) { + sk_sp sk_filter = SkColorFilters::LinearToSRGBGamma(); + std::shared_ptr filter = DlColorFilter::From(sk_filter); + ASSERT_EQ(filter->type(), DlColorFilter::kLinearToSrgbGamma); + ASSERT_EQ(filter->asBlend(), nullptr); + ASSERT_EQ(filter->asMatrix(), nullptr); +} + +TEST(DisplayListColorFilter, FromSkiaUnrecognizedFilter) { + sk_sp sk_inputA = + SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kOverlay); + sk_sp sk_inputB = + SkColorFilters::Blend(SK_ColorBLUE, SkBlendMode::kScreen); + sk_sp sk_filter = + SkColorFilters::Compose(sk_inputA, sk_inputB); + std::shared_ptr filter = DlColorFilter::From(sk_filter); + ASSERT_EQ(filter->type(), DlColorFilter::kUnknown); + ASSERT_EQ(filter->asBlend(), nullptr); + ASSERT_EQ(filter->asMatrix(), nullptr); +} + +TEST(DisplayListColorFilter, BlendConstructor) { + DlBlendColorFilter filter(SK_ColorRED, SkBlendMode::kDstATop); +} + +TEST(DisplayListColorFilter, BlendShared) { + DlBlendColorFilter filter(SK_ColorRED, SkBlendMode::kDstATop); + ASSERT_NE(filter.shared().get(), &filter); + ASSERT_EQ(*filter.shared(), filter); +} + +TEST(DisplayListColorFilter, BlendAsBlend) { + DlBlendColorFilter filter = + DlBlendColorFilter(SK_ColorRED, SkBlendMode::kDstATop); + ASSERT_NE(filter.asBlend(), nullptr); + ASSERT_EQ(filter.asBlend(), &filter); +} + +TEST(DisplayListColorFilter, BlendContents) { + DlBlendColorFilter filter(SK_ColorRED, SkBlendMode::kDstATop); + ASSERT_EQ(filter.color(), SK_ColorRED); + ASSERT_EQ(filter.mode(), SkBlendMode::kDstATop); +} + +TEST(DisplayListColorFilter, BlendEquals) { + DlBlendColorFilter filter1(SK_ColorRED, SkBlendMode::kDstATop); + DlBlendColorFilter filter2(SK_ColorRED, SkBlendMode::kDstATop); + ASSERT_TRUE(filter1 == filter2); + ASSERT_TRUE(filter2 == filter1); + ASSERT_FALSE(filter1 != filter2); + ASSERT_FALSE(filter2 != filter1); + ASSERT_EQ(filter1, filter2); +} + +TEST(DisplayListColorFilter, BlendNotEquals) { + DlBlendColorFilter filter1(SK_ColorRED, SkBlendMode::kDstATop); + DlBlendColorFilter filter2(SK_ColorBLUE, SkBlendMode::kDstATop); + DlBlendColorFilter filter3(SK_ColorRED, SkBlendMode::kDstIn); + ASSERT_FALSE(filter1 == filter2); + ASSERT_FALSE(filter2 == filter1); + ASSERT_TRUE(filter1 != filter2); + ASSERT_TRUE(filter2 != filter1); + ASSERT_NE(filter1, filter2); + ASSERT_NE(filter2, filter3); + ASSERT_NE(filter3, filter1); +} + +TEST(DisplayListColorFilter, MatrixConstructor) { + DlMatrixColorFilter filter(matrix); +} + +TEST(DisplayListColorFilter, MatrixShared) { + DlMatrixColorFilter filter(matrix); + ASSERT_NE(filter.shared().get(), &filter); + ASSERT_EQ(*filter.shared(), filter); +} + +TEST(DisplayListColorFilter, MatrixAsMatrix) { + DlMatrixColorFilter filter(matrix); + ASSERT_NE(filter.asMatrix(), nullptr); + ASSERT_EQ(filter.asMatrix(), &filter); +} + +TEST(DisplayListColorFilter, MatrixContents) { + float matrix_[20]; + memcpy(matrix_, matrix, sizeof(matrix_)); + DlMatrixColorFilter filter(matrix_); + + // Test deref operator [] + for (int i = 0; i < 20; i++) { + ASSERT_EQ(filter[i], matrix_[i]); + } + + // Test get_matrix + float matrix2[20]; + filter.get_matrix(matrix2); + for (int i = 0; i < 20; i++) { + ASSERT_EQ(matrix2[i], matrix_[i]); + } + + // Test perturbing original array does not affect filter + float original_value = matrix_[4]; + matrix_[4] += 101; + ASSERT_EQ(filter[4], original_value); +} + +TEST(DisplayListColorFilter, MatrixEquals) { + DlMatrixColorFilter filter1(matrix); + DlMatrixColorFilter filter2(matrix); + ASSERT_EQ(filter1, filter2); +} + +TEST(DisplayListColorFilter, MatrixNotEquals) { + float matrix_[20]; + memcpy(matrix_, matrix, sizeof(matrix_)); + DlMatrixColorFilter filter1(matrix_); + matrix_[4] += 101; + DlMatrixColorFilter filter2(matrix_); + ASSERT_NE(filter1, filter2); +} + +TEST(DisplayListColorFilter, SrgbToLinearConstructor) { + DlSrgbToLinearGammaColorFilter filter; +} + +TEST(DisplayListColorFilter, SrgbToLinearShared) { + DlSrgbToLinearGammaColorFilter filter; + ASSERT_NE(filter.shared().get(), &filter); + ASSERT_EQ(*filter.shared(), filter); +} + +TEST(DisplayListColorFilter, SrgbToLinearEquals) { + DlSrgbToLinearGammaColorFilter filter1; + DlSrgbToLinearGammaColorFilter filter2; + ASSERT_TRUE(filter1 == filter2); + ASSERT_TRUE(filter2 == filter1); + ASSERT_FALSE(filter1 != filter2); + ASSERT_FALSE(filter2 != filter1); + ASSERT_EQ(filter1, filter2); + ASSERT_EQ(filter1, *DlSrgbToLinearGammaColorFilter::instance); +} + +TEST(DisplayListColorFilter, LinearToSrgbConstructor) { + DlLinearToSrgbGammaColorFilter filter; +} + +TEST(DisplayListColorFilter, LinearToSrgbShared) { + DlLinearToSrgbGammaColorFilter filter; + ASSERT_NE(filter.shared().get(), &filter); + ASSERT_EQ(*filter.shared(), filter); +} + +TEST(DisplayListColorFilter, LinearToSrgbEquals) { + DlLinearToSrgbGammaColorFilter filter1; + DlLinearToSrgbGammaColorFilter filter2; + ASSERT_TRUE(filter1 == filter2); + ASSERT_TRUE(filter2 == filter1); + ASSERT_FALSE(filter1 != filter2); + ASSERT_FALSE(filter2 != filter1); + ASSERT_EQ(filter1, filter2); + ASSERT_EQ(filter1, *DlLinearToSrgbGammaColorFilter::instance); +} + +TEST(DisplayListColorFilter, UnknownConstructor) { + DlUnknownColorFilter filter(SkColorFilters::LinearToSRGBGamma()); +} + +TEST(DisplayListColorFilter, UnknownShared) { + DlUnknownColorFilter filter(SkColorFilters::LinearToSRGBGamma()); + ASSERT_NE(filter.shared().get(), &filter); + ASSERT_EQ(*filter.shared(), filter); +} + +TEST(DisplayListColorFilter, UnknownContents) { + sk_sp sk_filter = SkColorFilters::LinearToSRGBGamma(); + DlUnknownColorFilter filter(sk_filter); + ASSERT_EQ(sk_filter, filter.sk_filter()); + ASSERT_EQ(sk_filter.get(), filter.sk_filter().get()); +} + +TEST(DisplayListColorFilter, UnknownEquals) { + sk_sp sk_filter = SkColorFilters::LinearToSRGBGamma(); + DlUnknownColorFilter filter1(sk_filter); + DlUnknownColorFilter filter2(sk_filter); + ASSERT_TRUE(filter1 == filter2); + ASSERT_TRUE(filter2 == filter1); + ASSERT_FALSE(filter1 != filter2); + ASSERT_FALSE(filter2 != filter1); + ASSERT_EQ(filter1, filter2); +} + +TEST(DisplayListColorFilter, UnknownNotEquals) { + // Even though the filter is the same, it is a different instance + // and we cannot currently tell them apart because the Skia + // ColorFilter objects do not implement == + DlUnknownColorFilter filter1( + SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kDstATop)); + DlUnknownColorFilter filter2( + SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kDstATop)); + ASSERT_TRUE(filter1 != filter2); + ASSERT_TRUE(filter2 != filter1); + ASSERT_FALSE(filter1 == filter2); + ASSERT_FALSE(filter2 == filter1); + ASSERT_NE(filter1, filter2); +} + +} // namespace testing +} // namespace flutter diff --git a/display_list/display_list_dispatcher.h b/display_list/display_list_dispatcher.h index 9b4edd0eee0e0..7d869408baf45 100644 --- a/display_list/display_list_dispatcher.h +++ b/display_list/display_list_dispatcher.h @@ -6,6 +6,7 @@ #define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_DISPATCHER_H_ #include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_color_filter.h" namespace flutter { @@ -36,7 +37,7 @@ class Dispatcher { virtual void setStrokeCap(SkPaint::Cap cap) = 0; virtual void setStrokeJoin(SkPaint::Join join) = 0; virtual void setShader(sk_sp shader) = 0; - virtual void setColorFilter(sk_sp filter) = 0; + virtual void setColorFilter(const DlColorFilter* filter) = 0; // setInvertColors does not exist in SkPaint, but is a quick way to set // a ColorFilter that inverts the rgb values of all rendered colors. // It is not reset by |setColorFilter|, but instead composed with that diff --git a/display_list/display_list_ops.h b/display_list/display_list_ops.h index 574637a283d71..c99e43d53a08c 100644 --- a/display_list/display_list_ops.h +++ b/display_list/display_list_ops.h @@ -177,11 +177,45 @@ struct SetBlendModeOp final : DLOp { 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) DEFINE_SET_CLEAR_SKREF_OP(MaskFilter, filter) DEFINE_SET_CLEAR_SKREF_OP(PathEffect, effect) #undef DEFINE_SET_CLEAR_SKREF_OP +struct ClearColorFilterOp final : DLOp { + static const auto kType = DisplayListOpType::kClearColorFilter; + + ClearColorFilterOp() {} + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.setColorFilter(nullptr); + } +}; + +struct SetColorFilterOp final : DLOp { + static const auto kType = DisplayListOpType::kSetColorFilter; + + SetColorFilterOp() {} + + void dispatch(Dispatcher& dispatcher) const { + const DlColorFilter* filter = + reinterpret_cast(this + 1); + dispatcher.setColorFilter(filter); + } +}; + +struct SetSkColorFilterOp final : DLOp { + static const auto kType = DisplayListOpType::kSetSkColorFilter; + + SetSkColorFilterOp(sk_sp filter) : filter(filter) {} + + sk_sp filter; + + void dispatch(Dispatcher& dispatcher) const { + DlUnknownColorFilter dl_filter(filter); + dispatcher.setColorFilter(&dl_filter); + } +}; + // 4 byte header + 4 byte payload packs into minimum 8 bytes // Note that the "blur style" is packed into the OpType to prevent // needing an additional 8 bytes for a 4-value enum. diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index 6183470fb9cb7..aed1b51d41c8b 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -96,10 +96,14 @@ static const sk_sp TestImageFilter1 = SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr, nullptr); static const sk_sp TestImageFilter2 = SkImageFilters::Blur(5.0, 5.0, SkTileMode::kClamp, nullptr, nullptr); -static const sk_sp TestColorFilter1 = - SkColorFilters::Matrix(rotate_color_matrix); -static const sk_sp TestColorFilter2 = - SkColorFilters::Matrix(invert_color_matrix); +static const DlBlendColorFilter TestBlendColorFilter1(SK_ColorRED, + SkBlendMode::kDstATop); +static const DlBlendColorFilter TestBlendColorFilter2(SK_ColorBLUE, + SkBlendMode::kDstATop); +static const DlBlendColorFilter TestBlendColorFilter3(SK_ColorRED, + SkBlendMode::kDstIn); +static const DlMatrixColorFilter TestMatrixColorFilter1(rotate_color_matrix); +static const DlMatrixColorFilter TestMatrixColorFilter2(invert_color_matrix); static const sk_sp TestPathEffect1 = SkDashPathEffect::Make(TestDashes1, 2, 0.0f); static const sk_sp TestPathEffect2 = @@ -343,8 +347,13 @@ std::vector allGroups = { } }, { "SetColorFilter", { - {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(TestColorFilter1);}}, - {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(TestColorFilter2);}}, + {0, 24, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(&TestBlendColorFilter1);}}, + {0, 24, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(&TestBlendColorFilter2);}}, + {0, 24, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(&TestBlendColorFilter3);}}, + {0, 96, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(&TestMatrixColorFilter1);}}, + {0, 96, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(&TestMatrixColorFilter2);}}, + {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(DlSrgbToLinearGammaColorFilter::instance.get());}}, + {0, 16, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(DlLinearToSrgbGammaColorFilter::instance.get());}}, {0, 0, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(nullptr);}}, } }, @@ -992,7 +1001,7 @@ TEST(DisplayList, DisplayListSaveLayerBoundsWithAlphaFilter) { 0, 0, 0, 1, 0, }; // clang-format on - sk_sp base_color_filter = SkColorFilters::Matrix(color_matrix); + DlMatrixColorFilter base_color_filter(color_matrix); // clang-format off const float alpha_matrix[] = { 0, 0, 0, 0, 0, @@ -1001,8 +1010,7 @@ TEST(DisplayList, DisplayListSaveLayerBoundsWithAlphaFilter) { 0, 0, 0, 0, 1, }; // clang-format on - sk_sp alpha_color_filter = - SkColorFilters::Matrix(alpha_matrix); + DlMatrixColorFilter alpha_color_filter(alpha_matrix); { // No tricky stuff, just verifying drawing a rect produces rect bounds @@ -1017,7 +1025,7 @@ TEST(DisplayList, DisplayListSaveLayerBoundsWithAlphaFilter) { { // Now checking that a normal color filter still produces rect bounds DisplayListBuilder builder(build_bounds); - builder.setColorFilter(base_color_filter); + builder.setColorFilter(&base_color_filter); builder.saveLayer(&save_bounds, true); builder.setColorFilter(nullptr); builder.drawRect(rect); @@ -1034,7 +1042,7 @@ TEST(DisplayList, DisplayListSaveLayerBoundsWithAlphaFilter) { SkRTreeFactory rtree_factory; SkCanvas* canvas = recorder.beginRecording(build_bounds, &rtree_factory); SkPaint p1; - p1.setColorFilter(alpha_color_filter); + p1.setColorFilter(alpha_color_filter.sk_filter()); canvas->saveLayer(save_bounds, &p1); SkPaint p2; canvas->drawRect(rect, p2); @@ -1049,7 +1057,7 @@ TEST(DisplayList, DisplayListSaveLayerBoundsWithAlphaFilter) { // cull rect of the DisplayListBuilder when it encounters a // save layer that modifies an unbounded region DisplayListBuilder builder(build_bounds); - builder.setColorFilter(alpha_color_filter); + builder.setColorFilter(&alpha_color_filter); builder.saveLayer(&save_bounds, true); builder.setColorFilter(nullptr); builder.drawRect(rect); @@ -1062,7 +1070,7 @@ TEST(DisplayList, DisplayListSaveLayerBoundsWithAlphaFilter) { // Verifying that the save layer bounds are not relevant // to the behavior in the previous example DisplayListBuilder builder(build_bounds); - builder.setColorFilter(alpha_color_filter); + builder.setColorFilter(&alpha_color_filter); builder.saveLayer(nullptr, true); builder.setColorFilter(nullptr); builder.drawRect(rect); @@ -1076,7 +1084,7 @@ TEST(DisplayList, DisplayListSaveLayerBoundsWithAlphaFilter) { // generate the same behavior as setting it as a ColorFilter DisplayListBuilder builder(build_bounds); builder.setImageFilter( - SkImageFilters::ColorFilter(base_color_filter, nullptr)); + SkImageFilters::ColorFilter(base_color_filter.sk_filter(), nullptr)); builder.saveLayer(&save_bounds, true); builder.setImageFilter(nullptr); builder.drawRect(rect); @@ -1090,7 +1098,7 @@ TEST(DisplayList, DisplayListSaveLayerBoundsWithAlphaFilter) { // will generate the same behavior as setting it as a ColorFilter DisplayListBuilder builder(build_bounds); builder.setImageFilter( - SkImageFilters::ColorFilter(alpha_color_filter, nullptr)); + SkImageFilters::ColorFilter(alpha_color_filter.sk_filter(), nullptr)); builder.saveLayer(&save_bounds, true); builder.setImageFilter(nullptr); builder.drawRect(rect); @@ -1103,7 +1111,7 @@ TEST(DisplayList, DisplayListSaveLayerBoundsWithAlphaFilter) { // Same as above (ImageFilter hiding ColorFilter) with no save bounds DisplayListBuilder builder(build_bounds); builder.setImageFilter( - SkImageFilters::ColorFilter(alpha_color_filter, nullptr)); + SkImageFilters::ColorFilter(alpha_color_filter.sk_filter(), nullptr)); builder.saveLayer(nullptr, true); builder.setImageFilter(nullptr); builder.drawRect(rect); @@ -1256,27 +1264,6 @@ TEST(DisplayList, DisplayListImageFilterRefHandling) { ASSERT_TRUE(tester.ref_is_unique()); } -TEST(DisplayList, DisplayListColorFilterRefHandling) { - class ColorFilterRefTester : public virtual AttributeRefTester { - public: - void setRefToPaint(SkPaint& paint) const override { - paint.setColorFilter(color_filter); - } - void setRefToDisplayList(DisplayListBuilder& builder) const override { - builder.setColorFilter(color_filter); - } - bool ref_is_unique() const override { return color_filter->unique(); } - - private: - sk_sp color_filter = - SkColorFilters::Blend(SK_ColorBLUE, SkBlendMode::kSrcIn); - }; - - ColorFilterRefTester tester; - tester.test(); - ASSERT_TRUE(tester.ref_is_unique()); -} - TEST(DisplayList, DisplayListMaskFilterRefHandling) { class MaskFilterRefTester : public virtual AttributeRefTester { public: @@ -1756,7 +1743,7 @@ TEST(DisplayList, SaveLayerColorFilterPreventsOpacityOptimization) { DisplayListBuilder builder; builder.setColor(SkColorSetARGB(127, 255, 255, 255)); - builder.setColorFilter(TestColorFilter1); + builder.setColorFilter(&TestMatrixColorFilter1); builder.saveLayer(nullptr, true); builder.setColorFilter(nullptr); builder.drawRect({10, 10, 20, 20}); @@ -1805,7 +1792,7 @@ TEST(DisplayList, SaveLayerColorFilterOnChildPreventsOpacityOptimization) { DisplayListBuilder builder; builder.setColor(SkColorSetARGB(127, 255, 255, 255)); builder.saveLayer(nullptr, true); - builder.setColorFilter(TestColorFilter1); + builder.setColorFilter(&TestMatrixColorFilter1); builder.drawRect({10, 10, 20, 20}); builder.restore(); diff --git a/display_list/display_list_utils.cc b/display_list/display_list_utils.cc index b219bb24bbe7d..c21b4a2f3e760 100644 --- a/display_list/display_list_utils.cc +++ b/display_list/display_list_utils.cc @@ -79,8 +79,8 @@ void SkPaintDispatchHelper::setShader(sk_sp shader) { void SkPaintDispatchHelper::setImageFilter(sk_sp filter) { paint_.setImageFilter(filter); } -void SkPaintDispatchHelper::setColorFilter(sk_sp filter) { - color_filter_ = filter; +void SkPaintDispatchHelper::setColorFilter(const DlColorFilter* filter) { + color_filter_ = filter ? filter->shared() : nullptr; paint_.setColorFilter(makeColorFilter()); } void SkPaintDispatchHelper::setPathEffect(sk_sp effect) { @@ -94,14 +94,14 @@ void SkPaintDispatchHelper::setMaskBlurFilter(SkBlurStyle style, paint_.setMaskFilter(SkMaskFilter::MakeBlur(style, sigma)); } -sk_sp SkPaintDispatchHelper::makeColorFilter() { +sk_sp SkPaintDispatchHelper::makeColorFilter() const { if (!invert_colors_) { - return color_filter_; + return color_filter_ ? color_filter_->sk_filter() : nullptr; } sk_sp invert_filter = SkColorFilters::Matrix(invert_color_matrix); if (color_filter_) { - invert_filter = invert_filter->makeComposed(color_filter_); + invert_filter = invert_filter->makeComposed(color_filter_->sk_filter()); } return invert_filter; } @@ -266,8 +266,8 @@ void DisplayListBoundsCalculator::setBlender(sk_sp blender) { void DisplayListBoundsCalculator::setImageFilter(sk_sp filter) { image_filter_ = std::move(filter); } -void DisplayListBoundsCalculator::setColorFilter(sk_sp filter) { - color_filter_ = std::move(filter); +void DisplayListBoundsCalculator::setColorFilter(const DlColorFilter* filter) { + color_filter_ = filter ? filter->shared() : nullptr; } void DisplayListBoundsCalculator::setPathEffect(sk_sp effect) { path_effect_ = std::move(effect); @@ -657,8 +657,7 @@ bool DisplayListBoundsCalculator::paint_nops_on_transparency() { // save layer untouched out to the edge of the output surface. // This test assumes that the blend mode checked down below will // NOP on transparent black. - if (color_filter_ && - color_filter_->filterColor(SK_ColorTRANSPARENT) != SK_ColorTRANSPARENT) { + if (color_filter_ && color_filter_->modifies_transparent_black()) { return false; } diff --git a/display_list/display_list_utils.h b/display_list/display_list_utils.h index b88f9f743802d..db5ede9f718bb 100644 --- a/display_list/display_list_utils.h +++ b/display_list/display_list_utils.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_FLOW_DISPLAY_LIST_UTILS_H_ -#define FLUTTER_FLOW_DISPLAY_LIST_UTILS_H_ +#ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_UTILS_H_ +#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_UTILS_H_ #include @@ -56,7 +56,7 @@ class IgnoreAttributeDispatchHelper : public virtual Dispatcher { void setBlender(sk_sp blender) override {} void setShader(sk_sp shader) override {} void setImageFilter(sk_sp filter) override {} - void setColorFilter(sk_sp filter) override {} + void setColorFilter(const DlColorFilter* filter) override {} void setPathEffect(sk_sp effect) override {} void setMaskFilter(sk_sp filter) override {} void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override {} @@ -179,7 +179,7 @@ class SkPaintDispatchHelper : public virtual Dispatcher { void setStrokeCap(SkPaint::Cap cap) override; void setStrokeJoin(SkPaint::Join join) override; void setShader(sk_sp shader) override; - void setColorFilter(sk_sp filter) override; + void setColorFilter(const DlColorFilter* filter) override; void setInvertColors(bool invert) override; void setBlendMode(SkBlendMode mode) override; void setBlender(sk_sp blender) override; @@ -209,9 +209,9 @@ class SkPaintDispatchHelper : public virtual Dispatcher { private: SkPaint paint_; bool invert_colors_ = false; - sk_sp color_filter_; + std::shared_ptr color_filter_; - sk_sp makeColorFilter(); + sk_sp makeColorFilter() const; struct SaveInfo { SaveInfo(SkScalar opacity) : opacity(opacity) {} @@ -400,7 +400,7 @@ class DisplayListBoundsCalculator final void setBlendMode(SkBlendMode mode) override; void setBlender(sk_sp blender) override; void setImageFilter(sk_sp filter) override; - void setColorFilter(sk_sp filter) override; + void setColorFilter(const DlColorFilter* filter) override; void setPathEffect(sk_sp effect) override; void setMaskFilter(sk_sp filter) override; void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override; @@ -571,7 +571,7 @@ class DisplayListBoundsCalculator final static constexpr SkScalar kMinStrokeWidth = 0.01; std::optional blend_mode_ = SkBlendMode::kSrcOver; - sk_sp color_filter_; + std::shared_ptr color_filter_; SkScalar half_stroke_width_ = kMinStrokeWidth; SkScalar miter_limit_ = 4.0; @@ -616,4 +616,4 @@ class DisplayListBoundsCalculator final } // namespace flutter -#endif // FLUTTER_FLOW_DISPLAY_LIST_UTILS_H_ +#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_UTILS_H_ diff --git a/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc index dca87fabea205..1efdac5b59204 100644 --- a/lib/ui/compositing/scene_builder.cc +++ b/lib/ui/compositing/scene_builder.cc @@ -176,8 +176,8 @@ void SceneBuilder::pushOpacity(Dart_Handle layer_handle, void SceneBuilder::pushColorFilter(Dart_Handle layer_handle, const ColorFilter* color_filter, fml::RefPtr oldLayer) { - auto layer = - std::make_shared(color_filter->filter()); + auto layer = std::make_shared( + color_filter->filter()->sk_filter()); PushLayer(layer); EngineLayer::MakeRetained(layer_handle, layer); diff --git a/lib/ui/painting/color_filter.cc b/lib/ui/painting/color_filter.cc index a0f27f2dbeb96..8c4415a2aa6f5 100644 --- a/lib/ui/painting/color_filter.cc +++ b/lib/ui/painting/color_filter.cc @@ -40,33 +40,31 @@ fml::RefPtr ColorFilter::Create() { } void ColorFilter::initMode(int color, int blend_mode) { - filter_ = SkColorFilters::Blend(static_cast(color), - static_cast(blend_mode)); -} - -sk_sp ColorFilter::MakeColorMatrixFilter255( - const float array[20]) { - float tmp[20]; - memcpy(tmp, array, sizeof(tmp)); - tmp[4] *= 1.0f / 255; - tmp[9] *= 1.0f / 255; - tmp[14] *= 1.0f / 255; - tmp[19] *= 1.0f / 255; - return SkColorFilters::Matrix(tmp); + filter_ = std::make_shared( + static_cast(color), static_cast(blend_mode)); } void ColorFilter::initMatrix(const tonic::Float32List& color_matrix) { FML_CHECK(color_matrix.num_elements() == 20); - filter_ = MakeColorMatrixFilter255(color_matrix.data()); + // Flutter still defines the matrix to be biased by 255 in the last column + // (translate). skia is normalized, treating the last column as 0...1, so we + // post-scale here before calling the skia factory. + float matrix[20]; + memcpy(matrix, color_matrix.data(), sizeof(matrix)); + matrix[4] *= 1.0f / 255; + matrix[9] *= 1.0f / 255; + matrix[14] *= 1.0f / 255; + matrix[19] *= 1.0f / 255; + filter_ = std::make_shared(matrix); } void ColorFilter::initLinearToSrgbGamma() { - filter_ = SkColorFilters::LinearToSRGBGamma(); + filter_ = DlLinearToSrgbGammaColorFilter::instance; } void ColorFilter::initSrgbToLinearGamma() { - filter_ = SkColorFilters::SRGBToLinearGamma(); + filter_ = DlSrgbToLinearGammaColorFilter::instance; } ColorFilter::~ColorFilter() = default; diff --git a/lib/ui/painting/color_filter.h b/lib/ui/painting/color_filter.h index e73a89ed84915..021a82bf48893 100644 --- a/lib/ui/painting/color_filter.h +++ b/lib/ui/painting/color_filter.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_LIB_UI_COLOR_FILTER_H_ #define FLUTTER_LIB_UI_COLOR_FILTER_H_ +#include "flutter/display_list/display_list_color_filter.h" #include "flutter/lib/ui/dart_wrapper.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/tonic/typed_data/typed_list.h" @@ -27,11 +28,6 @@ class ColorFilter : public RefCountedDartWrappable { public: static fml::RefPtr Create(); - // Flutter still defines the matrix to be biased by 255 in the last column - // (translate). skia is normalized, treating the last column as 0...1, so we - // post-scale here before calling the skia factory. - static sk_sp MakeColorMatrixFilter255(const float array[20]); - void initMode(int color, int blend_mode); void initMatrix(const tonic::Float32List& color_matrix); void initSrgbToLinearGamma(); @@ -39,12 +35,12 @@ class ColorFilter : public RefCountedDartWrappable { ~ColorFilter() override; - sk_sp filter() const { return filter_; } + std::shared_ptr filter() const { return filter_; } static void RegisterNatives(tonic::DartLibraryNatives* natives); private: - sk_sp filter_; + std::shared_ptr filter_; }; } // namespace flutter diff --git a/lib/ui/painting/image_filter.cc b/lib/ui/painting/image_filter.cc index d4c2974172e9f..f0c4b8d8b1358 100644 --- a/lib/ui/painting/image_filter.cc +++ b/lib/ui/painting/image_filter.cc @@ -92,7 +92,7 @@ void ImageFilter::initMatrix(const tonic::Float64List& matrix4, void ImageFilter::initColorFilter(ColorFilter* colorFilter) { filter_ = SkImageFilters::ColorFilter( - colorFilter ? colorFilter->filter() : nullptr, nullptr); + colorFilter ? colorFilter->filter()->sk_filter() : nullptr, nullptr); } void ImageFilter::initComposeFilter(ImageFilter* outer, ImageFilter* inner) { diff --git a/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc index eaaa69659d8ef..aedadc404b764 100644 --- a/lib/ui/painting/paint.cc +++ b/lib/ui/painting/paint.cc @@ -105,7 +105,7 @@ const SkPaint* Paint::paint(SkPaint& paint) const { if (!Dart_IsNull(color_filter)) { ColorFilter* decoded_color_filter = tonic::DartConverter::FromDart(color_filter); - paint.setColorFilter(decoded_color_filter->filter()); + paint.setColorFilter(decoded_color_filter->filter()->sk_filter()); } Dart_Handle image_filter = values[kImageFilterIndex]; @@ -156,8 +156,7 @@ const SkPaint* Paint::paint(SkPaint& paint) const { } if (uint_data[kInvertColorIndex]) { - sk_sp invert_filter = - ColorFilter::MakeColorMatrixFilter255(invert_colors); + sk_sp invert_filter = SkColorFilters::Matrix(invert_colors); sk_sp current_filter = paint.refColorFilter(); if (current_filter) { invert_filter = invert_filter->makeComposed(current_filter); @@ -235,7 +234,7 @@ bool Paint::sync_to(DisplayListBuilder* builder, } else { ColorFilter* decoded_color_filter = tonic::DartConverter::FromDart(color_filter); - builder->setColorFilter(decoded_color_filter->filter()); + builder->setColorFilter(decoded_color_filter->filter().get()); } }