Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 3a02a99

Browse files
committed
[Impeller] Fix mask blurs and the Gaussian blur coverage hint.
1 parent c3f57b7 commit 3a02a99

File tree

9 files changed

+118
-43
lines changed

9 files changed

+118
-43
lines changed

impeller/aiks/aiks_unittests.cc

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3271,5 +3271,40 @@ TEST_P(AiksTest, PipelineBlendSingleParameter) {
32713271
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
32723272
}
32733273

3274+
TEST_P(AiksTest, ClippedBlurFilterRendersCorrectlyInteractive) {
3275+
auto callback = [&](AiksContext& renderer, RenderTarget& render_target) {
3276+
auto point = IMPELLER_PLAYGROUND_POINT(Point(400, 400), 20, Color::Green());
3277+
3278+
Canvas canvas;
3279+
canvas.Translate(point - Point(400, 400));
3280+
Paint paint;
3281+
paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
3282+
.style = FilterContents::BlurStyle::kNormal,
3283+
.sigma = Radius{120 * 3},
3284+
};
3285+
paint.color = Color::Red();
3286+
PathBuilder builder{};
3287+
builder.AddRect(Rect::MakeLTRB(0, 0, 800, 800));
3288+
canvas.DrawPath(builder.TakePath(), paint);
3289+
return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
3290+
};
3291+
ASSERT_TRUE(OpenPlaygroundHere(callback));
3292+
}
3293+
3294+
TEST_P(AiksTest, ClippedBlurFilterRendersCorrectly) {
3295+
Canvas canvas;
3296+
canvas.Translate(Point(0, -400));
3297+
Paint paint;
3298+
paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
3299+
.style = FilterContents::BlurStyle::kNormal,
3300+
.sigma = Radius{120 * 3},
3301+
};
3302+
paint.color = Color::Red();
3303+
PathBuilder builder{};
3304+
builder.AddRect(Rect::MakeLTRB(0, 0, 800, 800));
3305+
canvas.DrawPath(builder.TakePath(), paint);
3306+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
3307+
}
3308+
32743309
} // namespace testing
32753310
} // namespace impeller

impeller/aiks/canvas.cc

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ void Canvas::DrawImageRect(const std::shared_ptr<Image>& image,
492492
Entity entity;
493493
entity.SetBlendMode(paint.blend_mode);
494494
entity.SetStencilDepth(GetStencilDepth());
495-
entity.SetContents(paint.WithFilters(contents, false));
495+
entity.SetContents(paint.WithFilters(contents));
496496
entity.SetTransformation(GetCurrentTransformation());
497497

498498
GetCurrentPass().AddEntity(entity);
@@ -561,8 +561,13 @@ void Canvas::DrawTextFrame(const TextFrame& text_frame,
561561
color_text_contents->SetColorSourceContents(
562562
paint.color_source.GetContents(paint));
563563

564-
entity.SetContents(
565-
paint.WithFilters(std::move(color_text_contents), false));
564+
// TODO(bdero): This mask blur application is a hack. It will always wind up
565+
// doing a gaussian blur that affects the color source itself
566+
// instead of just the mask. The color filter text support
567+
// needs to be reworked in order to interact correctly with
568+
// mask filters.
569+
entity.SetContents(paint.WithFilters(
570+
paint.WithMaskBlur(std::move(color_text_contents), true)));
566571

567572
GetCurrentPass().AddEntity(entity);
568573
return;
@@ -573,7 +578,13 @@ void Canvas::DrawTextFrame(const TextFrame& text_frame,
573578
entity.SetTransformation(GetCurrentTransformation() *
574579
Matrix::MakeTranslation(position));
575580

576-
entity.SetContents(paint.WithFilters(std::move(text_contents), true));
581+
// TODO(bdero): This mask blur application is a hack. It will always wind up
582+
// doing a gaussian blur that affects the color source itself
583+
// instead of just the mask. The color filter text support
584+
// needs to be reworked in order to interact correctly with
585+
// mask filters.
586+
entity.SetContents(
587+
paint.WithFilters(paint.WithMaskBlur(std::move(text_contents), true)));
577588

578589
GetCurrentPass().AddEntity(entity);
579590
}
@@ -682,7 +693,7 @@ void Canvas::DrawAtlas(const std::shared_ptr<Image>& atlas,
682693
entity.SetTransformation(GetCurrentTransformation());
683694
entity.SetStencilDepth(GetStencilDepth());
684695
entity.SetBlendMode(paint.blend_mode);
685-
entity.SetContents(paint.WithFilters(contents, false));
696+
entity.SetContents(paint.WithFilters(contents));
686697

687698
GetCurrentPass().AddEntity(entity);
688699
}

impeller/aiks/paint.cc

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,32 @@ std::shared_ptr<Contents> Paint::CreateContentsForEntity(const Path& path,
3434
std::shared_ptr<Contents> Paint::CreateContentsForGeometry(
3535
std::shared_ptr<Geometry> geometry) const {
3636
auto contents = color_source.GetContents(*this);
37+
38+
// Attempt to apply the color filter on the CPU first.
39+
// Note: This is not just an optimization; some color sources rely on
40+
// CPU-applied color filters to behave properly.
41+
bool needs_color_filter = !!color_filter;
42+
if (color_filter &&
43+
contents->ApplyColorFilter(color_filter->GetCPUColorFilterProc())) {
44+
needs_color_filter = false;
45+
}
46+
3747
contents->SetGeometry(std::move(geometry));
3848
if (mask_blur_descriptor.has_value()) {
39-
return mask_blur_descriptor->CreateMaskBlur(contents);
49+
// If there's a mask blur and we need to apply the color filter on the GPU,
50+
// we need to be careful to only apply the color filter to the source
51+
// colors. CreateMaskBlur is able to handle this case.
52+
return mask_blur_descriptor->CreateMaskBlur(
53+
contents, needs_color_filter ? color_filter : nullptr);
4054
}
55+
4156
return contents;
4257
}
4358

4459
std::shared_ptr<Contents> Paint::WithFilters(
45-
std::shared_ptr<Contents> input,
46-
std::optional<bool> is_solid_color) const {
47-
bool is_solid_color_val = is_solid_color.value_or(color_source.GetType() ==
48-
ColorSource::Type::kColor);
60+
std::shared_ptr<Contents> input) const {
4961
input = WithColorFilter(input, /*absorb_opacity=*/true);
5062
input = WithInvertFilter(input);
51-
input = WithMaskBlur(input, is_solid_color_val);
5263
input = WithImageFilter(input, Matrix(), /*is_subpass=*/false);
5364
return input;
5465
}
@@ -128,7 +139,16 @@ std::shared_ptr<Contents> Paint::WithInvertFilter(
128139
}
129140

130141
std::shared_ptr<FilterContents> Paint::MaskBlurDescriptor::CreateMaskBlur(
131-
std::shared_ptr<ColorSourceContents> color_source_contents) const {
142+
std::shared_ptr<ColorSourceContents> color_source_contents,
143+
const std::shared_ptr<ColorFilter>& color_filter) const {
144+
// If it's a solid color and there is no color filter, then we can just get
145+
// away with doing one Gaussian blur.
146+
if (color_source_contents->IsSolidColor() && !color_filter) {
147+
return FilterContents::MakeGaussianBlur(
148+
FilterInput::Make(color_source_contents), sigma, sigma, style,
149+
Entity::TileMode::kDecal, Matrix());
150+
}
151+
132152
/// 1. Create an opaque white mask of the original geometry.
133153

134154
auto mask = std::make_shared<SolidColorContents>();
@@ -152,11 +172,20 @@ std::shared_ptr<FilterContents> Paint::MaskBlurDescriptor::CreateMaskBlur(
152172
color_source_contents->SetGeometry(
153173
Geometry::MakeRect(*expanded_local_bounds));
154174

155-
/// 4. Composite the color source and mask together.
175+
std::shared_ptr<Contents> color_contents = color_source_contents;
176+
177+
/// 4. Apply the user set color filter on the GPU, if applicable.
178+
179+
if (color_filter) {
180+
color_contents = color_filter->WrapWithGPUColorFilter(
181+
FilterInput::Make(color_source_contents), true);
182+
}
183+
184+
/// 5. Composite the color source with the blurred mask.
156185

157186
return ColorFilterContents::MakeBlend(
158-
BlendMode::kSourceIn, {FilterInput::Make(blurred_mask),
159-
FilterInput::Make(color_source_contents)});
187+
BlendMode::kSourceIn,
188+
{FilterInput::Make(blurred_mask), FilterInput::Make(color_contents)});
160189
}
161190

162191
std::shared_ptr<FilterContents> Paint::MaskBlurDescriptor::CreateMaskBlur(

impeller/aiks/paint.h

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ struct Paint {
4242
Sigma sigma;
4343

4444
std::shared_ptr<FilterContents> CreateMaskBlur(
45-
std::shared_ptr<ColorSourceContents> color_source_contents) const;
45+
std::shared_ptr<ColorSourceContents> color_source_contents,
46+
const std::shared_ptr<ColorFilter>& color_filter) const;
4647

4748
std::shared_ptr<FilterContents> CreateMaskBlur(
4849
const FilterInput::Ref& input,
@@ -67,18 +68,10 @@ struct Paint {
6768

6869
/// @brief Wrap this paint's configured filters to the given contents.
6970
/// @param[in] input The contents to wrap with paint's filters.
70-
/// @param[in] is_solid_color Affects mask blurring behavior. If false, use
71-
/// the image border for mask blurring. If true,
72-
/// do a Gaussian blur to achieve the mask
73-
/// blurring effect for arbitrary paths. If unset,
74-
/// use the current paint configuration to infer
75-
/// the result.
7671
/// @return The filter-wrapped contents. If there are no filters that need
7772
/// to be wrapped for the current paint configuration, the
7873
/// original contents is returned.
79-
std::shared_ptr<Contents> WithFilters(
80-
std::shared_ptr<Contents> input,
81-
std::optional<bool> is_solid_color = std::nullopt) const;
74+
std::shared_ptr<Contents> WithFilters(std::shared_ptr<Contents> input) const;
8275

8376
/// @brief Wrap this paint's configured filters to the given contents of
8477
/// subpass target.
@@ -101,10 +94,10 @@ struct Paint {
10194
/// @brief Whether this paint has a color filter that can apply opacity
10295
bool HasColorFilter() const;
10396

104-
private:
10597
std::shared_ptr<Contents> WithMaskBlur(std::shared_ptr<Contents> input,
10698
bool is_solid_color) const;
10799

100+
private:
108101
std::shared_ptr<Contents> WithImageFilter(std::shared_ptr<Contents> input,
109102
const Matrix& effect_transform,
110103
bool is_subpass) const;

impeller/entity/contents/color_source_contents.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ const Matrix& ColorSourceContents::GetInverseEffectTransform() const {
3838
return inverse_matrix_;
3939
}
4040

41+
bool ColorSourceContents::IsSolidColor() const {
42+
return false;
43+
}
44+
4145
std::optional<Rect> ColorSourceContents::GetCoverage(
4246
const Entity& entity) const {
4347
return geometry_->GetCoverage(entity.GetTransformation());

impeller/entity/contents/color_source_contents.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ class ColorSourceContents : public Contents {
9090
///
9191
Scalar GetOpacityFactor() const;
9292

93+
virtual bool IsSolidColor() const;
94+
9395
// |Contents|
9496
std::optional<Rect> GetCoverage(const Entity& entity) const override;
9597

impeller/entity/contents/filters/gaussian_blur_filter_contents.cc

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,19 @@ std::optional<Entity> DirectionalGaussianBlurFilterContents::RenderFilter(
104104
// Limit the kernel size to 1000x1000 pixels, like Skia does.
105105
auto radius = std::min(Radius{blur_sigma_}.radius, 500.0f);
106106

107+
auto transform = entity.GetTransformation() * effect_transform.Basis();
108+
auto transformed_blur_radius =
109+
transform.TransformDirection(blur_direction_ * radius);
110+
111+
auto transformed_blur_radius_length = transformed_blur_radius.GetLength();
112+
107113
// Input 0 snapshot.
108114

109115
std::optional<Rect> expanded_coverage_hint;
110116
if (coverage_hint.has_value()) {
111-
auto r = Size(radius, radius).Abs();
117+
auto r =
118+
Size(transformed_blur_radius_length, transformed_blur_radius_length)
119+
.Abs();
112120
expanded_coverage_hint =
113121
is_first_pass ? Rect(coverage_hint.value().origin - r,
114122
Size(coverage_hint.value().size + r * 2))
@@ -126,12 +134,6 @@ std::optional<Entity> DirectionalGaussianBlurFilterContents::RenderFilter(
126134
entity.GetStencilDepth()); // No blur to render.
127135
}
128136

129-
auto transform = entity.GetTransformation() * effect_transform.Basis();
130-
auto transformed_blur_radius =
131-
transform.TransformDirection(blur_direction_ * radius);
132-
133-
auto transformed_blur_radius_length = transformed_blur_radius.GetLength();
134-
135137
// If the radius length is < .5, the shader will take at most 1 sample,
136138
// resulting in no blur.
137139
if (transformed_blur_radius_length < .5) {
@@ -158,16 +160,6 @@ std::optional<Entity> DirectionalGaussianBlurFilterContents::RenderFilter(
158160
pass_texture_rect.origin.x -= transformed_blur_radius_length;
159161
pass_texture_rect.size.width += transformed_blur_radius_length * 2;
160162

161-
// Crop the pass texture with the rotated coverage hint if one was given.
162-
if (expanded_coverage_hint.has_value()) {
163-
auto maybe_pass_texture_rect = pass_texture_rect.Intersection(
164-
expanded_coverage_hint->TransformBounds(texture_rotate));
165-
if (!maybe_pass_texture_rect.has_value()) {
166-
return std::nullopt;
167-
}
168-
pass_texture_rect = *maybe_pass_texture_rect;
169-
}
170-
171163
// Source override snapshot.
172164

173165
auto source = source_override_ ? source_override_ : inputs[0];
@@ -335,6 +327,8 @@ std::optional<Entity> DirectionalGaussianBlurFilterContents::RenderFilter(
335327
SamplerDescriptor sampler_desc;
336328
sampler_desc.min_filter = MinMagFilter::kLinear;
337329
sampler_desc.mag_filter = MinMagFilter::kLinear;
330+
sampler_desc.width_address_mode = SamplerAddressMode::kClampToEdge;
331+
sampler_desc.width_address_mode = SamplerAddressMode::kClampToEdge;
338332

339333
return Entity::FromSnapshot(
340334
Snapshot{.texture = out_texture,

impeller/entity/contents/solid_color_contents.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ Color SolidColorContents::GetColor() const {
2424
return color_.WithAlpha(color_.alpha * GetOpacityFactor());
2525
}
2626

27+
bool SolidColorContents::IsSolidColor() const {
28+
return true;
29+
}
30+
2731
bool SolidColorContents::IsOpaque() const {
2832
return GetColor().IsOpaque();
2933
}

impeller/entity/contents/solid_color_contents.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ class SolidColorContents final : public ColorSourceContents {
3434

3535
Color GetColor() const;
3636

37+
// |ColorSourceContents|
38+
bool IsSolidColor() const override;
39+
3740
// |Contents|
3841
bool IsOpaque() const override;
3942

0 commit comments

Comments
 (0)