Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
111 changes: 111 additions & 0 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2760,6 +2760,117 @@ TEST_P(AiksTest, TranslucentSaveLayerWithColorAndImageFilterDrawsCorrectly) {
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_P(AiksTest, ImageFilteredSaveLayerWithUnboundedContents) {
Canvas canvas;
canvas.Scale(GetContentScale());

auto test = [&canvas](const std::shared_ptr<ImageFilter>& filter) {
auto DrawLine = [&canvas](const Point& p0, const Point& p1,
const Paint& p) {
auto path = PathBuilder{}
.AddLine(p0, p1)
.SetConvexity(Convexity::kConvex)
.TakePath();
Paint paint = p;
paint.style = Paint::Style::kStroke;
canvas.DrawPath(path, paint);
};
// Registration marks for the edge of the SaveLayer
DrawLine(Point(75, 100), Point(225, 100), {.color = Color::White()});
DrawLine(Point(75, 200), Point(225, 200), {.color = Color::White()});
DrawLine(Point(100, 75), Point(100, 225), {.color = Color::White()});
DrawLine(Point(200, 75), Point(200, 225), {.color = Color::White()});

canvas.SaveLayer({.image_filter = filter},
Rect::MakeLTRB(100, 100, 200, 200));
{
// DrawPaint to verify correct behavior when the contents are unbounded.
canvas.DrawPaint({.color = Color::Yellow()});

// Contrasting rectangle to see interior blurring
canvas.DrawRect(Rect::MakeLTRB(125, 125, 175, 175),
{.color = Color::Blue()});
}
canvas.Restore();
};

test(ImageFilter::MakeBlur(Sigma{10.0}, Sigma{10.0},
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kDecal));

canvas.Translate({200.0, 0.0});

test(ImageFilter::MakeDilate(Radius{10.0}, Radius{10.0}));

canvas.Translate({200.0, 0.0});

test(ImageFilter::MakeErode(Radius{10.0}, Radius{10.0}));

canvas.Translate({-400.0, 200.0});

auto rotate_filter =
ImageFilter::MakeMatrix(Matrix::MakeTranslation({150, 150}) *
Matrix::MakeRotationZ(Degrees{10.0}) *
Matrix::MakeTranslation({-150, -150}),
SamplerDescriptor{});
test(rotate_filter);

canvas.Translate({200.0, 0.0});

auto rgb_swap_filter = ImageFilter::MakeFromColorFilter(
*ColorFilter::MakeMatrix({.array = {
0, 1, 0, 0, 0, //
0, 0, 1, 0, 0, //
1, 0, 0, 0, 0, //
0, 0, 0, 1, 0 //
}}));
test(rgb_swap_filter);

canvas.Translate({200.0, 0.0});

test(ImageFilter::MakeCompose(*rotate_filter, *rgb_swap_filter));

canvas.Translate({-400.0, 200.0});

test(ImageFilter::MakeLocalMatrix(Matrix::MakeTranslation({25.0, 25.0}),
*rotate_filter));

canvas.Translate({200.0, 0.0});

test(ImageFilter::MakeLocalMatrix(Matrix::MakeTranslation({25.0, 25.0}),
*rgb_swap_filter));

canvas.Translate({200.0, 0.0});

test(ImageFilter::MakeLocalMatrix(
Matrix::MakeTranslation({25.0, 25.0}),
*ImageFilter::MakeCompose(*rotate_filter, *rgb_swap_filter)));

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_P(AiksTest, ImageFilteredUnboundedSaveLayerWithUnboundedContents) {
Canvas canvas;
canvas.Scale(GetContentScale());

auto blur_filter = ImageFilter::MakeBlur(Sigma{10.0}, Sigma{10.0},
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kDecal);

canvas.SaveLayer({.image_filter = blur_filter}, std::nullopt);
{
// DrawPaint to verify correct behavior when the contents are unbounded.
canvas.DrawPaint({.color = Color::Yellow()});

// Contrasting rectangle to see interior blurring
canvas.DrawRect(Rect::MakeLTRB(125, 125, 175, 175),
{.color = Color::Blue()});
}
canvas.Restore();

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_P(AiksTest, TranslucentSaveLayerImageDrawsCorrectly) {
Canvas canvas;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,15 @@ std::optional<Rect> BorderMaskBlurFilterContents::GetFilterCoverage(
Size(extent.x, extent.y));
}

std::optional<Rect> BorderMaskBlurFilterContents::GetFilterSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const {
auto transformed_blur_vector =
effect_transform.TransformDirection(Vector2(Radius{sigma_x_}.radius, 0))
.Abs() +
effect_transform.TransformDirection(Vector2(0, Radius{sigma_y_}.radius))
.Abs();
return output_limit.Expand(transformed_blur_vector);
}

} // namespace impeller
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ class BorderMaskBlurFilterContents final : public FilterContents {
const Entity& entity,
const Matrix& effect_transform) const override;

// |FilterContents|
std::optional<Rect> GetFilterSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const override;

private:
// |FilterContents|
std::optional<Entity> RenderFilter(
Expand Down
6 changes: 6 additions & 0 deletions impeller/entity/contents/filters/color_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,10 @@ std::optional<Scalar> ColorFilterContents::GetAlpha() const {
return alpha_;
}

std::optional<Rect> ColorFilterContents::GetFilterSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const {
return output_limit;
}

} // namespace impeller
5 changes: 5 additions & 0 deletions impeller/entity/contents/filters/color_filter_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ class ColorFilterContents : public FilterContents {

std::optional<Scalar> GetAlpha() const;

// |FilterContents|
std::optional<Rect> GetFilterSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const override;

private:
AbsorbOpacity absorb_opacity_ = AbsorbOpacity::kNo;
std::optional<Scalar> alpha_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,17 @@ std::optional<Entity> DirectionalGaussianBlurFilterContents::RenderFilter(
entity.GetBlendMode(), entity.GetClipDepth());
}

std::optional<Rect>
DirectionalGaussianBlurFilterContents::GetFilterSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const {
auto transform = effect_transform.Basis();
auto transformed_blur_vector =
transform.TransformDirection(blur_direction_ * Radius{blur_sigma_}.radius)
.Abs();
return output_limit.Expand(transformed_blur_vector);
}

std::optional<Rect> DirectionalGaussianBlurFilterContents::GetFilterCoverage(
const FilterInput::Vector& inputs,
const Entity& entity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents {
/// beginning of the chain.
void SetIsSecondPass(bool is_second_pass);

// |FilterContents|
std::optional<Rect> GetFilterSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const override;

// |FilterContents|
std::optional<Rect> GetFilterCoverage(
const FilterInput::Vector& inputs,
Expand Down
22 changes: 22 additions & 0 deletions impeller/entity/contents/filters/filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,28 @@ std::optional<Rect> FilterContents::GetFilterCoverage(
return result;
}

std::optional<Rect> FilterContents::GetSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const {
auto filter_input_coverage =
GetFilterSourceCoverage(effect_transform_, output_limit);

if (!filter_input_coverage.has_value()) {
return std::nullopt;
}

std::optional<Rect> inputs_coverage;
for (auto input : inputs_) {
auto input_coverage = input->GetSourceCoverage(
effect_transform, filter_input_coverage.value());
if (!input_coverage.has_value()) {
return std::nullopt;
}
inputs_coverage = Union(inputs_coverage, input_coverage.value());
}
return inputs_coverage;
}

std::optional<Entity> FilterContents::GetEntity(
const ContentContext& renderer,
const Entity& entity,
Expand Down
18 changes: 18 additions & 0 deletions impeller/entity/contents/filters/filter_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,20 @@ class FilterContents : public Contents {
// |Contents|
const FilterContents* AsFilter() const override;

/// @brief Determines the coverage of source pixels that will be needed
/// to apply this filter under the given transform and produce
/// results anywhere within the indicated coverage limit.
///
/// This is useful for subpass rendering scenarios where a filter
/// will be applied to the output of the subpass and we need to
/// determine how large of a render target to allocate in order
/// to collect all pixels that might affect the supplied output
/// coverage limit. While we might clip the rendering of the subpass,
/// we want to avoid clipping out any pixels that contribute to
/// the output limit via the filtering operation.
std::optional<Rect> GetSourceCoverage(const Matrix& effect_transform,
const Rect& output_limit) const;

virtual Matrix GetLocalTransform(const Matrix& parent_transform) const;

Matrix GetTransform(const Matrix& parent_transform) const;
Expand Down Expand Up @@ -169,6 +183,10 @@ class FilterContents : public Contents {
const Entity& entity,
const Matrix& effect_transform) const;

virtual std::optional<Rect> GetFilterSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const = 0;

/// @brief Converts zero or more filter inputs into a render instruction.
virtual std::optional<Entity> RenderFilter(
const FilterInput::Vector& inputs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ std::optional<Rect> FilterContentsFilterInput::GetCoverage(
return filter_->GetCoverage(entity);
}

std::optional<Rect> FilterContentsFilterInput::GetSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const {
return filter_->GetSourceCoverage(effect_transform, output_limit);
}

Matrix FilterContentsFilterInput::GetLocalTransform(
const Entity& entity) const {
return filter_->GetLocalTransform(entity.GetTransformation());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ class FilterContentsFilterInput final : public FilterInput {
// |FilterInput|
std::optional<Rect> GetCoverage(const Entity& entity) const override;

// |FilterInput|
std::optional<Rect> GetSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const override;

// |FilterInput|
Matrix GetLocalTransform(const Entity& entity) const override;

Expand Down
6 changes: 6 additions & 0 deletions impeller/entity/contents/filters/inputs/filter_input.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ std::optional<Rect> FilterInput::GetLocalCoverage(const Entity& entity) const {
return GetCoverage(local_entity);
}

std::optional<Rect> FilterInput::GetSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const {
return output_limit;
}

Matrix FilterInput::GetTransform(const Entity& entity) const {
return entity.GetTransformation() * GetLocalTransform(entity);
}
Expand Down
3 changes: 3 additions & 0 deletions impeller/entity/contents/filters/inputs/filter_input.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class FilterInput {

virtual std::optional<Rect> GetCoverage(const Entity& entity) const = 0;

virtual std::optional<Rect> GetSourceCoverage(const Matrix& effect_transform,
const Rect& output_limit) const;

/// @brief Get the local transform of this filter input. This transform is
/// relative to the `Entity` transform space.
virtual Matrix GetLocalTransform(const Entity& entity) const;
Expand Down
11 changes: 11 additions & 0 deletions impeller/entity/contents/filters/local_matrix_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ Matrix LocalMatrixFilterContents::GetLocalTransform(
return matrix_;
}

std::optional<Rect> LocalMatrixFilterContents::GetFilterSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const {
auto matrix = matrix_.Basis();
if (matrix.GetDeterminant() == 0.0) {
return std::nullopt;
}
auto inverse = matrix.Invert();
return output_limit.TransformBounds(inverse);
}

std::optional<Entity> LocalMatrixFilterContents::RenderFilter(
const FilterInput::Vector& inputs,
const ContentContext& renderer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ class LocalMatrixFilterContents final : public FilterContents {
// |FilterContents|
Matrix GetLocalTransform(const Matrix& parent_transform) const override;

// |FilterContents|
std::optional<Rect> GetFilterSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const override;

private:
// |FilterContents|
std::optional<Entity> RenderFilter(
Expand Down
13 changes: 13 additions & 0 deletions impeller/entity/contents/filters/matrix_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ std::optional<Entity> MatrixFilterContents::RenderFilter(
entity.GetClipDepth());
}

std::optional<Rect> MatrixFilterContents::GetFilterSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const {
auto transform = effect_transform * //
matrix_ * //
effect_transform.Invert(); //
if (transform.GetDeterminant() == 0.0) {
return std::nullopt;
}
auto inverse = transform.Invert();
return output_limit.TransformBounds(inverse);
}

std::optional<Rect> MatrixFilterContents::GetFilterCoverage(
const FilterInput::Vector& inputs,
const Entity& entity,
Expand Down
5 changes: 5 additions & 0 deletions impeller/entity/contents/filters/matrix_filter_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class MatrixFilterContents final : public FilterContents {
const Rect& coverage,
const std::optional<Rect>& coverage_hint) const override;

// |FilterContents|
std::optional<Rect> GetFilterSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const override;

Matrix matrix_;
SamplerDescriptor sampler_descriptor_ = {};
Entity::RenderingMode rendering_mode_ = Entity::RenderingMode::kDirect;
Expand Down
14 changes: 14 additions & 0 deletions impeller/entity/contents/filters/morphology_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,18 @@ std::optional<Rect> DirectionalMorphologyFilterContents::GetFilterCoverage(
return Rect(origin, Size(size.x, size.y));
}

std::optional<Rect>
DirectionalMorphologyFilterContents::GetFilterSourceCoverage(
const Matrix& effect_transform,
const Rect& output_limit) const {
auto transformed_vector =
effect_transform.TransformDirection(direction_ * radius_.radius).Abs();
switch (morph_type_) {
case FilterContents::MorphType::kDilate:
return output_limit.Expand(-transformed_vector);
case FilterContents::MorphType::kErode:
return output_limit.Expand(transformed_vector);
}
}

} // namespace impeller
Loading