diff --git a/display_list/display_list_builder.cc b/display_list/display_list_builder.cc index 970e019ff56bf..a4741ad24c470 100644 --- a/display_list/display_list_builder.cc +++ b/display_list/display_list_builder.cc @@ -225,6 +225,7 @@ void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) { break; } case DlImageFilterType::kComposeFilter: + case DlImageFilterType::kLocalMatrixFilter: case DlImageFilterType::kColorFilter: { Push(0, 0, filter); break; diff --git a/display_list/display_list_image_filter.cc b/display_list/display_list_image_filter.cc index f9eb71d35cca1..cae1321ffda34 100644 --- a/display_list/display_list_image_filter.cc +++ b/display_list/display_list_image_filter.cc @@ -27,6 +27,31 @@ std::shared_ptr DlImageFilter::From( return std::make_shared(sk_ref_sp(sk_filter)); } +std::shared_ptr DlImageFilter::makeWithLocalMatrix( + const SkMatrix& matrix) { + if (matrix.isIdentity()) { + return shared(); + } + // Matrix + switch (this->matrix_capability()) { + case MatrixCapability::kTranslate: { + if (!matrix.isTranslate()) { + // Nothing we can do at this point + return nullptr; + } + } + case MatrixCapability::kScaleTranslate: { + if (!matrix.isScaleTranslate()) { + // Nothing we can do at this point + return nullptr; + } + } + default: + break; + } + return std::make_shared(matrix, shared()); +} + SkRect* DlComposeImageFilter::map_local_bounds(const SkRect& input_bounds, SkRect& output_bounds) const { SkRect cur_bounds = input_bounds; diff --git a/display_list/display_list_image_filter.h b/display_list/display_list_image_filter.h index 5cc789e337795..4a0063b8b48cb 100644 --- a/display_list/display_list_image_filter.h +++ b/display_list/display_list_image_filter.h @@ -34,6 +34,7 @@ enum class DlImageFilterType { kMatrix, kComposeFilter, kColorFilter, + kLocalMatrixFilter, kUnknown }; @@ -41,12 +42,19 @@ class DlBlurImageFilter; class DlDilateImageFilter; class DlErodeImageFilter; class DlMatrixImageFilter; +class DlLocalMatrixImageFilter; class DlComposeImageFilter; class DlColorFilterImageFilter; class DlImageFilter : public DlAttribute { public: + enum class MatrixCapability { + kTranslate, + kScaleTranslate, + kComplex, + }; + // Return a shared_ptr holding a DlImageFilter representing the indicated // Skia SkImageFilter pointer. // @@ -82,6 +90,13 @@ class DlImageFilter // type of ImageFilter, otherwise return nullptr. virtual const DlMatrixImageFilter* asMatrix() const { return nullptr; } + virtual const DlLocalMatrixImageFilter* asLocalMatrix() const { + return nullptr; + } + + virtual std::shared_ptr makeWithLocalMatrix( + const SkMatrix& matrix); + // Return a DlComposeImageFilter pointer to this object iff it is a Compose // type of ImageFilter, otherwise return nullptr. virtual const DlComposeImageFilter* asCompose() const { return nullptr; } @@ -137,6 +152,10 @@ class DlImageFilter const SkMatrix& ctm, SkIRect& input_bounds) const = 0; + virtual MatrixCapability matrix_capability() const { + return MatrixCapability::kScaleTranslate; + } + protected: static SkVector map_vectors_affine(const SkMatrix& ctm, SkScalar x, @@ -534,6 +553,10 @@ class DlComposeImageFilter final : public DlImageFilter { inner_->skia_object()); } + MatrixCapability matrix_capability() const override { + return std::min(outer_->matrix_capability(), inner_->matrix_capability()); + } + protected: bool equals_(const DlImageFilter& other) const override { FML_DCHECK(other.type() == DlImageFilterType::kComposeFilter); @@ -606,6 +629,15 @@ class DlColorFilterImageFilter final : public DlImageFilter { return SkImageFilters::ColorFilter(color_filter_->skia_object(), nullptr); } + MatrixCapability matrix_capability() const override { + return MatrixCapability::kComplex; + } + + std::shared_ptr makeWithLocalMatrix( + const SkMatrix& matrix) override { + return shared(); + } + protected: bool equals_(const DlImageFilter& other) const override { FML_DCHECK(other.type() == DlImageFilterType::kColorFilter); @@ -617,6 +649,85 @@ class DlColorFilterImageFilter final : public DlImageFilter { std::shared_ptr color_filter_; }; +class DlLocalMatrixImageFilter final : public DlImageFilter { + public: + explicit DlLocalMatrixImageFilter(const SkMatrix& matrix, + std::shared_ptr filter) + : matrix_(matrix), image_filter_(filter) {} + explicit DlLocalMatrixImageFilter(const DlLocalMatrixImageFilter* filter) + : DlLocalMatrixImageFilter(filter->matrix_, filter->image_filter_) {} + DlLocalMatrixImageFilter(const DlLocalMatrixImageFilter& filter) + : DlLocalMatrixImageFilter(&filter) {} + std::shared_ptr shared() const override { + return std::make_shared(this); + } + + DlImageFilterType type() const override { + return DlImageFilterType::kLocalMatrixFilter; + } + size_t size() const override { return sizeof(*this); } + + const SkMatrix& matrix() const { return matrix_; } + + const DlLocalMatrixImageFilter* asLocalMatrix() const override { + return this; + } + + bool modifies_transparent_black() const override { + if (!image_filter_) { + return false; + } + return image_filter_->modifies_transparent_black(); + } + + SkRect* map_local_bounds(const SkRect& input_bounds, + SkRect& output_bounds) const override { + if (!image_filter_) { + return nullptr; + } + return image_filter_->map_local_bounds(input_bounds, output_bounds); + } + + SkIRect* map_device_bounds(const SkIRect& input_bounds, + const SkMatrix& ctm, + SkIRect& output_bounds) const override { + if (!image_filter_) { + return nullptr; + } + return image_filter_->map_device_bounds( + input_bounds, SkMatrix::Concat(ctm, matrix_), output_bounds); + } + + SkIRect* get_input_device_bounds(const SkIRect& output_bounds, + const SkMatrix& ctm, + SkIRect& input_bounds) const override { + if (!image_filter_) { + return nullptr; + } + return image_filter_->get_input_device_bounds( + output_bounds, SkMatrix::Concat(ctm, matrix_), input_bounds); + } + + sk_sp skia_object() const override { + if (!image_filter_) { + return nullptr; + } + return image_filter_->skia_object()->makeWithLocalMatrix(matrix_); + } + + protected: + bool equals_(const DlImageFilter& other) const override { + FML_DCHECK(other.type() == DlImageFilterType::kMatrix); + auto that = static_cast(&other); + return (matrix_ == that->matrix_ && + Equals(image_filter_, that->image_filter_)); + } + + private: + SkMatrix matrix_; + std::shared_ptr image_filter_; +}; + // A wrapper class for a Skia ImageFilter of unknown type. The above 4 types // are the only types that can be constructed by Flutter using the // ui.ImageFilter class so this class should be rarely used. The main use diff --git a/display_list/display_list_image_filter_unittests.cc b/display_list/display_list_image_filter_unittests.cc index 346a2f7ff56e3..f3f2507cf3b82 100644 --- a/display_list/display_list_image_filter_unittests.cc +++ b/display_list/display_list_image_filter_unittests.cc @@ -3,10 +3,14 @@ // found in the LICENSE file. #include "flutter/display_list/display_list_attributes_testing.h" +#include "flutter/display_list/display_list_blend_mode.h" #include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/display_list_color.h" +#include "flutter/display_list/display_list_color_filter.h" #include "flutter/display_list/display_list_comparable.h" #include "flutter/display_list/display_list_image_filter.h" #include "flutter/display_list/display_list_sampling_options.h" +#include "flutter/display_list/display_list_tile_mode.h" #include "flutter/display_list/types.h" #include "gtest/gtest.h" @@ -759,6 +763,89 @@ TEST(DisplayListImageFilter, UnknownContents) { ASSERT_EQ(filter.skia_object().get(), sk_filter.get()); } +TEST(DisplayListImageFilter, LocalImageFilterBounds) { + auto filter_matrix = SkMatrix::MakeAll(2.0, 0.0, 10, // + 0.5, 3.0, 15, // + 0.0, 0.0, 1); + std::vector> sk_filters{ + SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr), + SkImageFilters::ColorFilter( + SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcOver), nullptr), + SkImageFilters::Dilate(5.0, 10.0, nullptr), + SkImageFilters::MatrixTransform(filter_matrix, + ToSk(DlImageSampling::kLinear), nullptr), + SkImageFilters::Compose( + SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr), + SkImageFilters::ColorFilter( + SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcOver), + nullptr))}; + + DlBlendColorFilter dl_color_filter(DlColor::kRed(), DlBlendMode::kSrcOver); + std::vector> dl_filters{ + std::make_shared(5.0, 6.0, DlTileMode::kRepeat), + std::make_shared(dl_color_filter.shared()), + std::make_shared(5, 10), + std::make_shared(filter_matrix, + DlImageSampling::kLinear), + std::make_shared( + std::make_shared(5.0, 6.0, DlTileMode::kRepeat), + std::make_shared( + dl_color_filter.shared()))}; + + auto persp = SkMatrix::I(); + persp.setPerspY(0.001); + std::vector matrices = { + SkMatrix::Translate(10.0, 10.0), + SkMatrix::Scale(2.0, 2.0).preTranslate(10.0, 10.0), + SkMatrix::RotateDeg(45).preTranslate(5.0, 5.0), persp}; + std::vector bounds_matrices{SkMatrix::Translate(5.0, 10.0), + SkMatrix::Scale(2.0, 2.0)}; + + for (unsigned i = 0; i < sk_filters.size(); i++) { + for (unsigned j = 0; j < matrices.size(); j++) { + for (unsigned k = 0; k < bounds_matrices.size(); k++) { + auto& m = matrices[j]; + auto& bounds_matrix = bounds_matrices[k]; + auto sk_local_filter = sk_filters[i]->makeWithLocalMatrix(m); + auto dl_local_filter = dl_filters[i]->makeWithLocalMatrix(m); + if (!sk_local_filter || !dl_local_filter) { + ASSERT_TRUE(!sk_local_filter); + ASSERT_TRUE(!dl_local_filter); + continue; + } + { + auto inputBounds = SkIRect::MakeLTRB(20, 20, 80, 80); + SkIRect sk_rect, dl_rect; + sk_rect = sk_local_filter->filterBounds( + inputBounds, bounds_matrix, + SkImageFilter::MapDirection::kForward_MapDirection); + dl_local_filter->map_device_bounds(inputBounds, bounds_matrix, + dl_rect); + ASSERT_EQ(sk_rect, dl_rect); + } + { + // Test for: Know the outset bounds to get the inset bounds + // Skia have some bounds calculate error of DilateFilter and + // MatrixFilter + // Skia issue: https://bugs.chromium.org/p/skia/issues/detail?id=13444 + // flutter issue: https://github.com/flutter/flutter/issues/108693 + if (i == 2 || i == 3) { + continue; + } + auto outsetBounds = SkIRect::MakeLTRB(20, 20, 80, 80); + SkIRect sk_rect, dl_rect; + sk_rect = sk_local_filter->filterBounds( + outsetBounds, bounds_matrix, + SkImageFilter::MapDirection::kReverse_MapDirection); + dl_local_filter->get_input_device_bounds(outsetBounds, bounds_matrix, + dl_rect); + ASSERT_EQ(sk_rect, dl_rect); + } + } + } + } +} + TEST(DisplayListImageFilter, UnknownEquals) { sk_sp sk_filter = SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr); diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index a1d80528d6f09..145a2db905c46 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -412,6 +412,7 @@ static std::optional ToImageFilterProc( case flutter::DlImageFilterType::kDilate: case flutter::DlImageFilterType::kErode: case flutter::DlImageFilterType::kMatrix: + case flutter::DlImageFilterType::kLocalMatrixFilter: case flutter::DlImageFilterType::kComposeFilter: case flutter::DlImageFilterType::kColorFilter: case flutter::DlImageFilterType::kUnknown: