Create ImageFilter.dilate/ImageFilter.erode (flutter/engine#32334)

This commit is contained in:
fzyzcjy 2022-04-05 04:01:02 +08:00 committed by GitHub
parent a0b860283e
commit a6ca392650
11 changed files with 502 additions and 1 deletions

View File

@ -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);

View File

@ -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] = {

View File

@ -28,6 +28,8 @@ namespace flutter {
// provided as a fallback.
enum class DlImageFilterType {
kBlur,
kDilate,
kErode,
kMatrix,
kComposeFilter,
kColorFilter,
@ -35,6 +37,8 @@ enum class DlImageFilterType {
};
class DlBlurImageFilter;
class DlDilateImageFilter;
class DlErodeImageFilter;
class DlMatrixImageFilter;
class DlComposeImageFilter;
class DlColorFilterImageFilter;
@ -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; }
@ -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;
}
@ -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)

View File

@ -43,6 +43,40 @@ TEST(DisplayListImageFilter, FromSkiaBlurImageFilter) {
// We cannot recapture the blur parameters from an SkBlurImageFilter
ASSERT_EQ(filter->asBlur(), nullptr);
ASSERT_EQ(filter->asDilate(), nullptr);
ASSERT_EQ(filter->asErode(), nullptr);
ASSERT_EQ(filter->asMatrix(), nullptr);
ASSERT_EQ(filter->asCompose(), nullptr);
ASSERT_EQ(filter->asColorFilter(), nullptr);
}
TEST(DisplayListImageFilter, FromSkiaDilateImageFilter) {
sk_sp<SkImageFilter> sk_image_filter =
SkImageFilters::Dilate(5.0, 5.0, nullptr);
std::shared_ptr<DlImageFilter> filter = DlImageFilter::From(sk_image_filter);
ASSERT_EQ(filter->type(), DlImageFilterType::kUnknown);
// We cannot recapture the dilate parameters from an SkDilateImageFilter
ASSERT_EQ(filter->asBlur(), nullptr);
ASSERT_EQ(filter->asDilate(), nullptr);
ASSERT_EQ(filter->asErode(), nullptr);
ASSERT_EQ(filter->asMatrix(), nullptr);
ASSERT_EQ(filter->asCompose(), nullptr);
ASSERT_EQ(filter->asColorFilter(), nullptr);
}
TEST(DisplayListImageFilter, FromSkiaErodeImageFilter) {
sk_sp<SkImageFilter> sk_image_filter =
SkImageFilters::Erode(5.0, 5.0, nullptr);
std::shared_ptr<DlImageFilter> filter = DlImageFilter::From(sk_image_filter);
ASSERT_EQ(filter->type(), DlImageFilterType::kUnknown);
// We cannot recapture the erode parameters from an SkErodeImageFilter
ASSERT_EQ(filter->asBlur(), nullptr);
ASSERT_EQ(filter->asDilate(), nullptr);
ASSERT_EQ(filter->asErode(), nullptr);
ASSERT_EQ(filter->asMatrix(), nullptr);
ASSERT_EQ(filter->asCompose(), nullptr);
ASSERT_EQ(filter->asColorFilter(), nullptr);
@ -57,6 +91,8 @@ TEST(DisplayListImageFilter, FromSkiaMatrixImageFilter) {
// We cannot recapture the blur parameters from an SkMatrixImageFilter
ASSERT_EQ(filter->asBlur(), nullptr);
ASSERT_EQ(filter->asDilate(), nullptr);
ASSERT_EQ(filter->asErode(), nullptr);
ASSERT_EQ(filter->asMatrix(), nullptr);
ASSERT_EQ(filter->asCompose(), nullptr);
ASSERT_EQ(filter->asColorFilter(), nullptr);
@ -75,6 +111,8 @@ TEST(DisplayListImageFilter, FromSkiaComposeImageFilter) {
// We cannot recapture the blur parameters from an SkComposeImageFilter
ASSERT_EQ(filter->asBlur(), nullptr);
ASSERT_EQ(filter->asDilate(), nullptr);
ASSERT_EQ(filter->asErode(), nullptr);
ASSERT_EQ(filter->asMatrix(), nullptr);
ASSERT_EQ(filter->asCompose(), nullptr);
ASSERT_EQ(filter->asColorFilter(), nullptr);
@ -96,6 +134,8 @@ TEST(DisplayListImageFilter, FromSkiaColorFilterImageFilter) {
ASSERT_EQ(*filter->asColorFilter()->color_filter(), dl_color_filter);
ASSERT_EQ(filter->asBlur(), nullptr);
ASSERT_EQ(filter->asDilate(), nullptr);
ASSERT_EQ(filter->asErode(), nullptr);
ASSERT_EQ(filter->asMatrix(), nullptr);
ASSERT_EQ(filter->asCompose(), nullptr);
ASSERT_NE(filter->asColorFilter(), nullptr);
@ -145,6 +185,88 @@ TEST(DisplayListImageFilter, BlurNotEquals) {
TestNotEquals(filter1, filter4, "Tile Mode differs");
}
TEST(DisplayListImageFilter, DilateConstructor) {
DlDilateImageFilter filter(5.0, 6.0);
}
TEST(DisplayListImageFilter, DilateShared) {
DlDilateImageFilter filter(5.0, 6.0);
ASSERT_NE(filter.shared().get(), &filter);
ASSERT_EQ(*filter.shared(), filter);
}
TEST(DisplayListImageFilter, DilateAsDilate) {
DlDilateImageFilter filter(5.0, 6.0);
ASSERT_NE(filter.asDilate(), nullptr);
ASSERT_EQ(filter.asDilate(), &filter);
}
TEST(DisplayListImageFilter, DilateContents) {
DlDilateImageFilter filter(5.0, 6.0);
ASSERT_EQ(filter.radius_x(), 5.0);
ASSERT_EQ(filter.radius_y(), 6.0);
}
TEST(DisplayListImageFilter, DilateEquals) {
DlDilateImageFilter filter1(5.0, 6.0);
DlDilateImageFilter filter2(5.0, 6.0);
TestEquals(filter1, filter2);
}
TEST(DisplayListImageFilter, DilateNotEquals) {
DlDilateImageFilter filter1(5.0, 6.0);
DlDilateImageFilter filter2(7.0, 6.0);
DlDilateImageFilter filter3(5.0, 8.0);
TestNotEquals(filter1, filter2, "Radius X differs");
TestNotEquals(filter1, filter3, "Radius Y differs");
}
TEST(DisplayListImageFilter, ErodeConstructor) {
DlErodeImageFilter filter(5.0, 6.0);
}
TEST(DisplayListImageFilter, ErodeShared) {
DlErodeImageFilter filter(5.0, 6.0);
ASSERT_NE(filter.shared().get(), &filter);
ASSERT_EQ(*filter.shared(), filter);
}
TEST(DisplayListImageFilter, ErodeAsErode) {
DlErodeImageFilter filter(5.0, 6.0);
ASSERT_NE(filter.asErode(), nullptr);
ASSERT_EQ(filter.asErode(), &filter);
}
TEST(DisplayListImageFilter, ErodeContents) {
DlErodeImageFilter filter(5.0, 6.0);
ASSERT_EQ(filter.radius_x(), 5.0);
ASSERT_EQ(filter.radius_y(), 6.0);
}
TEST(DisplayListImageFilter, ErodeEquals) {
DlErodeImageFilter filter1(5.0, 6.0);
DlErodeImageFilter filter2(5.0, 6.0);
TestEquals(filter1, filter2);
}
TEST(DisplayListImageFilter, ErodeNotEquals) {
DlErodeImageFilter filter1(5.0, 6.0);
DlErodeImageFilter filter2(7.0, 6.0);
DlErodeImageFilter filter3(5.0, 8.0);
TestNotEquals(filter1, filter2, "Radius X differs");
TestNotEquals(filter1, filter3, "Radius Y differs");
}
TEST(DisplayListImageFilter, MatrixConstructor) {
DlMatrixImageFilter filter(SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //

View File

@ -149,6 +149,12 @@ static const DlBlurImageFilter TestBlurImageFilter3(5.0,
static const DlBlurImageFilter TestBlurImageFilter4(5.0,
5.0,
DlTileMode::kDecal);
static const DlDilateImageFilter TestDilateImageFilter1(5.0, 5.0);
static const DlDilateImageFilter TestDilateImageFilter2(6.0, 5.0);
static const DlDilateImageFilter TestDilateImageFilter3(5.0, 6.0);
static const DlErodeImageFilter TestErodeImageFilter1(5.0, 5.0);
static const DlErodeImageFilter TestErodeImageFilter2(6.0, 5.0);
static const DlErodeImageFilter TestErodeImageFilter3(5.0, 6.0);
static const DlMatrixImageFilter TestMatrixImageFilter1(SkMatrix::RotateDeg(45),
NearestSampling);
static const DlMatrixImageFilter TestMatrixImageFilter2(SkMatrix::RotateDeg(85),
@ -391,6 +397,12 @@ std::vector<DisplayListInvocationGroup> allGroups = {
{0, 32, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(&TestBlurImageFilter2);}},
{0, 32, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(&TestBlurImageFilter3);}},
{0, 32, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(&TestBlurImageFilter4);}},
{0, 24, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(&TestDilateImageFilter1);}},
{0, 24, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(&TestDilateImageFilter2);}},
{0, 24, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(&TestDilateImageFilter3);}},
{0, 24, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(&TestErodeImageFilter1);}},
{0, 24, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(&TestErodeImageFilter2);}},
{0, 24, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(&TestErodeImageFilter3);}},
{0, 80, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(&TestMatrixImageFilter1);}},
{0, 80, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(&TestMatrixImageFilter2);}},
{0, 80, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(&TestMatrixImageFilter3);}},

View File

@ -3224,6 +3224,22 @@ abstract class ImageFilter {
return _GaussianBlurImageFilter(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode);
}
/// Creates an image filter that dilates each input pixel's channel values
/// to the max value within the given radii along the x and y axes.
factory ImageFilter.dilate({ double radiusX = 0.0, double radiusY = 0.0 }) {
assert(radiusX != null);
assert(radiusY != null);
return _DilateImageFilter(radiusX: radiusX, radiusY: radiusY);
}
/// Create a filter that erodes each input pixel's channel values
/// to the minimum channel value within the given radii along the x and y axes.
factory ImageFilter.erode({ double radiusX = 0.0, double radiusY = 0.0 }) {
assert(radiusX != null);
assert(radiusY != null);
return _ErodeImageFilter(radiusX: radiusX, radiusY: radiusY);
}
/// Creates an image filter that applies a matrix transformation.
///
/// For example, applying a positive scale matrix (see [Matrix4.diagonal3])
@ -3327,6 +3343,64 @@ class _GaussianBlurImageFilter implements ImageFilter {
int get hashCode => hashValues(sigmaX, sigmaY);
}
class _DilateImageFilter implements ImageFilter {
_DilateImageFilter({ required this.radiusX, required this.radiusY });
final double radiusX;
final double radiusY;
late final _ImageFilter nativeFilter = _ImageFilter.dilate(this);
@override
_ImageFilter _toNativeImageFilter() => nativeFilter;
@override
String get _shortDescription => 'dilate($radiusX, $radiusY)';
@override
String toString() => 'ImageFilter.dilate($radiusX, $radiusY)';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is _DilateImageFilter
&& other.radiusX == radiusX
&& other.radiusY == radiusY;
}
@override
int get hashCode => hashValues(radiusX, radiusY);
}
class _ErodeImageFilter implements ImageFilter {
_ErodeImageFilter({ required this.radiusX, required this.radiusY });
final double radiusX;
final double radiusY;
late final _ImageFilter nativeFilter = _ImageFilter.erode(this);
@override
_ImageFilter _toNativeImageFilter() => nativeFilter;
@override
String get _shortDescription => 'erode($radiusX, $radiusY)';
@override
String toString() => 'ImageFilter.erode($radiusX, $radiusY)';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is _ErodeImageFilter
&& other.radiusX == radiusX
&& other.radiusY == radiusY;
}
@override
int get hashCode => hashValues(radiusX, radiusY);
}
class _ComposeImageFilter implements ImageFilter {
_ComposeImageFilter({ required this.innerFilter, required this.outerFilter });
@ -3374,6 +3448,26 @@ class _ImageFilter extends NativeFieldWrapperClass1 {
}
void _initBlur(double sigmaX, double sigmaY, int tileMode) native 'ImageFilter_initBlur';
/// Creates an image filter that dilates each input pixel's channel values
/// to the max value within the given radii along the x and y axes.
_ImageFilter.dilate(_DilateImageFilter filter)
: assert(filter != null),
creator = filter { // ignore: prefer_initializing_formals
_constructor();
_initDilate(filter.radiusX, filter.radiusY);
}
void _initDilate(double radiusX, double radiusY) native 'ImageFilter_initDilate';
/// Create a filter that erodes each input pixel's channel values
/// to the minimum channel value within the given radii along the x and y axes.
_ImageFilter.erode(_ErodeImageFilter filter)
: assert(filter != null),
creator = filter { // ignore: prefer_initializing_formals
_constructor();
_initErode(filter.radiusX, filter.radiusY);
}
void _initErode(double radiusX, double radiusY) native 'ImageFilter_initErode';
/// Creates an image filter that applies a matrix transformation.
///
/// For example, applying a positive scale matrix (see [Matrix4.diagonal3])

View File

@ -22,6 +22,8 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, ImageFilter);
#define FOR_EACH_BINDING(V) \
V(ImageFilter, initBlur) \
V(ImageFilter, initDilate) \
V(ImageFilter, initErode) \
V(ImageFilter, initMatrix) \
V(ImageFilter, initColorFilter) \
V(ImageFilter, initComposeFilter)
@ -74,6 +76,14 @@ void ImageFilter::initBlur(double sigma_x,
std::make_shared<DlBlurImageFilter>(sigma_x, sigma_y, ToDl(tile_mode));
}
void ImageFilter::initDilate(double radius_x, double radius_y) {
filter_ = std::make_shared<DlDilateImageFilter>(radius_x, radius_y);
}
void ImageFilter::initErode(double radius_x, double radius_y) {
filter_ = std::make_shared<DlErodeImageFilter>(radius_x, radius_y);
}
void ImageFilter::initMatrix(const tonic::Float64List& matrix4,
int filterQualityIndex) {
auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex);

View File

@ -30,6 +30,8 @@ class ImageFilter : public RefCountedDartWrappable<ImageFilter> {
static SkFilterMode FilterModeFromIndex(int index);
void initBlur(double sigma_x, double sigma_y, SkTileMode tile_mode);
void initDilate(double radius_x, double radius_y);
void initErode(double radius_x, double radius_y);
void initMatrix(const tonic::Float64List& matrix4, int filter_quality_index);
void initColorFilter(ColorFilter* colorFilter);
void initComposeFilter(ImageFilter* outer, ImageFilter* inner);

View File

@ -405,6 +405,20 @@ class ImageFilter {
return engine.EngineImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode);
}
// ignore: avoid_unused_constructor_parameters
factory ImageFilter.dilate({ double radiusX = 0.0, double radiusY = 0.0 }) {
// TODO(fzyzcjy): implement dilate. https://github.com/flutter/flutter/issues/101085
throw UnimplementedError(
'ImageFilter.dilate not implemented for web platform.');
}
// ignore: avoid_unused_constructor_parameters
factory ImageFilter.erode({ double radiusX = 0.0, double radiusY = 0.0 }) {
// TODO(fzyzcjy): implement erode. https://github.com/flutter/flutter/issues/101085
throw UnimplementedError(
'ImageFilter.erode not implemented for web platform.');
}
factory ImageFilter.matrix(Float64List matrix4, {FilterQuality filterQuality = FilterQuality.low}) {
if (matrix4.length != 16)
throw ArgumentError('"matrix4" must have 16 entries.');

View File

@ -394,6 +394,18 @@ void main() {
oldLayer: oldLayer as ImageFilterEngineLayer?,
);
});
testNoSharing((SceneBuilder builder, EngineLayer? oldLayer) {
return builder.pushImageFilter(
ImageFilter.dilate(radiusX: 10.0, radiusY: 10.0),
oldLayer: oldLayer as ImageFilterEngineLayer?,
);
});
testNoSharing((SceneBuilder builder, EngineLayer? oldLayer) {
return builder.pushImageFilter(
ImageFilter.erode(radiusX: 10.0, radiusY: 10.0),
oldLayer: oldLayer as ImageFilterEngineLayer?,
);
});
testNoSharing((SceneBuilder builder, EngineLayer? oldLayer) {
return builder.pushImageFilter(
ImageFilter.matrix(Float64List.fromList(<double>[

View File

@ -74,6 +74,12 @@ void main() {
ImageFilter makeBlur(double sigmaX, double sigmaY, [TileMode tileMode = TileMode.clamp]) =>
ImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode);
ImageFilter makeDilate(double radiusX, double radiusY) =>
ImageFilter.dilate(radiusX: radiusX, radiusY: radiusY);
ImageFilter makeErode(double radiusX, double radiusY) =>
ImageFilter.erode(radiusX: radiusX, radiusY: radiusY);
ImageFilter makeScale(double scX, double scY,
[double trX = 0.0, double trY = 0.0,
FilterQuality quality = FilterQuality.low]) {
@ -105,6 +111,12 @@ void main() {
makeBlur(10.0, 10.0, TileMode.decal),
makeBlur(10.0, 20.0),
makeBlur(20.0, 20.0),
makeDilate(10.0, 20.0),
makeDilate(20.0, 20.0),
makeDilate(20.0, 10.0),
makeErode(10.0, 20.0),
makeErode(20.0, 20.0),
makeErode(20.0, 10.0),
makeScale(10.0, 10.0),
makeScale(10.0, 20.0),
makeScale(20.0, 10.0),
@ -170,6 +182,24 @@ void main() {
checkBytes(bytes, greenCenterBlurred, greenSideBlurred, greenCornerBlurred);
});
test('ImageFilter - dilate', () async {
final Paint paint = Paint()
..color = green
..imageFilter = makeDilate(1.0, 1.0);
final Uint32List bytes = await getBytesForPaint(paint);
checkBytes(bytes, green.value, green.value, green.value);
});
test('ImageFilter - erode', () async {
final Paint paint = Paint()
..color = green
..imageFilter = makeErode(1.0, 1.0);
final Uint32List bytes = await getBytesForPaint(paint);
checkBytes(bytes, 0, 0, 0);
});
test('ImageFilter - matrix', () async {
final Paint paint = Paint()
..color = green