Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions display_list/display_list_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,20 @@ void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) {
new (pod) DlBlurImageFilter(blur_filter);
break;
}
case DlImageFilterType::kDilate: {
const DlDilateImageFilter* dilate_filter = filter->asDilate();
FML_DCHECK(dilate_filter);
void* pod = Push<SetPodImageFilterOp>(dilate_filter->size(), 0);
new (pod) DlDilateImageFilter(dilate_filter);
break;
}
case DlImageFilterType::kErode: {
const DlErodeImageFilter* erode_filter = filter->asErode();
FML_DCHECK(erode_filter);
void* pod = Push<SetPodImageFilterOp>(erode_filter->size(), 0);
new (pod) DlErodeImageFilter(erode_filter);
break;
}
case DlImageFilterType::kMatrix: {
const DlMatrixImageFilter* matrix_filter = filter->asMatrix();
FML_DCHECK(matrix_filter);
Expand Down
59 changes: 59 additions & 0 deletions display_list/display_list_canvas_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,65 @@ class CanvasCompareTester {
}
}

{
// Being able to see a dilate requires some non-default attributes,
// like a non-trivial stroke width and a shader rather than a color
// (for drawPaint) so we create a new environment for these tests.
RenderEnvironment dilate_env = RenderEnvironment::MakeN32();
CvSetup cv_dilate_setup = [=](SkCanvas*, SkPaint& p) {
p.setShader(testImageColorSource.skia_object());
p.setStrokeWidth(5.0);
};
DlRenderer dl_dilate_setup = [=](DisplayListBuilder& b) {
b.setColorSource(&testImageColorSource);
b.setStrokeWidth(5.0);
};
dilate_env.init_ref(cv_dilate_setup, testP.cv_renderer(),
dl_dilate_setup);
DlDilateImageFilter filter_5(5.0, 5.0);
RenderWith(testP, dilate_env, tolerance,
CaseParameters(
"ImageFilter == Dilate 5",
[=](SkCanvas* cv, SkPaint& p) {
cv_dilate_setup(cv, p);
p.setImageFilter(filter_5.skia_object());
},
[=](DisplayListBuilder& b) {
dl_dilate_setup(b);
b.setImageFilter(&filter_5);
}));
}

{
// Being able to see an erode requires some non-default attributes,
// like a non-trivial stroke width and a shader rather than a color
// (for drawPaint) so we create a new environment for these tests.
RenderEnvironment erode_env = RenderEnvironment::MakeN32();
CvSetup cv_erode_setup = [=](SkCanvas*, SkPaint& p) {
p.setShader(testImageColorSource.skia_object());
p.setStrokeWidth(6.0);
};
DlRenderer dl_erode_setup = [=](DisplayListBuilder& b) {
b.setColorSource(&testImageColorSource);
b.setStrokeWidth(6.0);
};
erode_env.init_ref(cv_erode_setup, testP.cv_renderer(), dl_erode_setup);
// do not erode too much, because some tests assert there are enough
// pixels that are changed.
DlErodeImageFilter filter_1(1.0, 1.0);
RenderWith(testP, erode_env, tolerance,
CaseParameters(
"ImageFilter == Erode 1",
[=](SkCanvas* cv, SkPaint& p) {
cv_erode_setup(cv, p);
p.setImageFilter(filter_1.skia_object());
},
[=](DisplayListBuilder& b) {
dl_erode_setup(b);
b.setImageFilter(&filter_1);
}));
}

{
// clang-format off
constexpr float rotate_color_matrix[20] = {
Expand Down
134 changes: 133 additions & 1 deletion display_list/display_list_image_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,17 @@ namespace flutter {
// provided as a fallback.
enum class DlImageFilterType {
kBlur,
kDilate,
kErode,
kMatrix,
kComposeFilter,
kColorFilter,
kUnknown
};

class DlBlurImageFilter;
class DlDilateImageFilter;
class DlErodeImageFilter;
class DlMatrixImageFilter;
class DlComposeImageFilter;
class DlColorFilterImageFilter;
Expand Down Expand Up @@ -64,6 +68,14 @@ class DlImageFilter
// type of ImageFilter, otherwise return nullptr.
virtual const DlBlurImageFilter* asBlur() const { return nullptr; }

// Return a DlDilateImageFilter pointer to this object iff it is a Dilate
// type of ImageFilter, otherwise return nullptr.
virtual const DlDilateImageFilter* asDilate() const { return nullptr; }

// Return a DlErodeImageFilter pointer to this object iff it is an Erode
// type of ImageFilter, otherwise return nullptr.
virtual const DlErodeImageFilter* asErode() const { return nullptr; }

// Return a DlMatrixImageFilter pointer to this object iff it is a Matrix
// type of ImageFilter, otherwise return nullptr.
virtual const DlMatrixImageFilter* asMatrix() const { return nullptr; }
Expand Down Expand Up @@ -140,7 +152,7 @@ class DlBlurImageFilter final : public DlImageFilter {
SkIRect* map_device_bounds(const SkIRect& input_bounds,
const SkMatrix& ctm,
SkIRect& output_bounds) const override {
SkVector device_sigma = ctm.mapVector(sigma_x_, sigma_y_);
SkVector device_sigma = ctm.mapVector(sigma_x_ * 3, sigma_y_ * 3);
if (!SkScalarIsFinite(device_sigma.fX)) {
device_sigma.fX = 0;
}
Expand Down Expand Up @@ -174,6 +186,126 @@ class DlBlurImageFilter final : public DlImageFilter {
DlTileMode tile_mode_;
};

class DlDilateImageFilter final : public DlImageFilter {
public:
DlDilateImageFilter(SkScalar radius_x, SkScalar radius_y)
: radius_x_(radius_x), radius_y_(radius_y) {}
explicit DlDilateImageFilter(const DlDilateImageFilter* filter)
: DlDilateImageFilter(filter->radius_x_, filter->radius_y_) {}
explicit DlDilateImageFilter(const DlDilateImageFilter& filter)
: DlDilateImageFilter(&filter) {}

std::shared_ptr<DlImageFilter> shared() const override {
return std::make_shared<DlDilateImageFilter>(this);
}

DlImageFilterType type() const override { return DlImageFilterType::kDilate; }
size_t size() const override { return sizeof(*this); }

const DlDilateImageFilter* asDilate() const override { return this; }

bool modifies_transparent_black() const override { return false; }

SkRect* map_local_bounds(const SkRect& input_bounds,
SkRect& output_bounds) const override {
output_bounds = input_bounds.makeOutset(radius_x_, radius_y_);
return &output_bounds;
}

SkIRect* map_device_bounds(const SkIRect& input_bounds,
const SkMatrix& ctm,
SkIRect& output_bounds) const override {
SkVector device_radius = ctm.mapVector(radius_x_, radius_y_);
if (!SkScalarIsFinite(device_radius.fX)) {
device_radius.fX = 0;
}
if (!SkScalarIsFinite(device_radius.fY)) {
device_radius.fY = 0;
}
output_bounds = input_bounds.makeOutset(ceil(abs(device_radius.fX)),
ceil(abs(device_radius.fY)));
return &output_bounds;
}

SkScalar radius_x() const { return radius_x_; }
SkScalar radius_y() const { return radius_y_; }

sk_sp<SkImageFilter> skia_object() const override {
return SkImageFilters::Dilate(radius_x_, radius_y_, nullptr);
}

protected:
bool equals_(const DlImageFilter& other) const override {
FML_DCHECK(other.type() == DlImageFilterType::kDilate);
auto that = static_cast<const DlDilateImageFilter*>(&other);
return (radius_x_ == that->radius_x_ && radius_y_ == that->radius_y_);
}

private:
SkScalar radius_x_;
SkScalar radius_y_;
};

class DlErodeImageFilter final : public DlImageFilter {
public:
DlErodeImageFilter(SkScalar radius_x, SkScalar radius_y)
: radius_x_(radius_x), radius_y_(radius_y) {}
explicit DlErodeImageFilter(const DlErodeImageFilter* filter)
: DlErodeImageFilter(filter->radius_x_, filter->radius_y_) {}
explicit DlErodeImageFilter(const DlErodeImageFilter& filter)
: DlErodeImageFilter(&filter) {}

std::shared_ptr<DlImageFilter> shared() const override {
return std::make_shared<DlErodeImageFilter>(this);
}

DlImageFilterType type() const override { return DlImageFilterType::kErode; }
size_t size() const override { return sizeof(*this); }

const DlErodeImageFilter* asErode() const override { return this; }

bool modifies_transparent_black() const override { return false; }

SkRect* map_local_bounds(const SkRect& input_bounds,
SkRect& output_bounds) const override {
output_bounds = input_bounds.makeOutset(radius_x_, radius_y_);
return &output_bounds;
}

SkIRect* map_device_bounds(const SkIRect& input_bounds,
const SkMatrix& ctm,
SkIRect& output_bounds) const override {
SkVector device_radius = ctm.mapVector(radius_x_, radius_y_);
if (!SkScalarIsFinite(device_radius.fX)) {
device_radius.fX = 0;
}
if (!SkScalarIsFinite(device_radius.fY)) {
device_radius.fY = 0;
}
output_bounds = input_bounds.makeOutset(ceil(abs(device_radius.fX)),
ceil(abs(device_radius.fY)));
return &output_bounds;
}

SkScalar radius_x() const { return radius_x_; }
SkScalar radius_y() const { return radius_y_; }

sk_sp<SkImageFilter> skia_object() const override {
return SkImageFilters::Erode(radius_x_, radius_y_, nullptr);
}

protected:
bool equals_(const DlImageFilter& other) const override {
FML_DCHECK(other.type() == DlImageFilterType::kErode);
auto that = static_cast<const DlErodeImageFilter*>(&other);
return (radius_x_ == that->radius_x_ && radius_y_ == that->radius_y_);
}

private:
SkScalar radius_x_;
SkScalar radius_y_;
};

class DlMatrixImageFilter final : public DlImageFilter {
public:
DlMatrixImageFilter(const SkMatrix& matrix, const SkSamplingOptions& sampling)
Expand Down
Loading