Skip to content

Commit

Permalink
[Impeller] Aiks image filters; allow setting effect transforms after …
Browse files Browse the repository at this point in the history
…FilterContents instantiation. (flutter#45530)

These are API improvements on the way to solving
flutter/flutter#131182.

* Remove `effect_transform` and `is_subpass` from the static
`FilterContents` factories. These are for internal use only.
* Replace `impeller::Paint` filter procs with Aiks `ImageFilter`
factories similar to `ColorFilter`. This gives Aiks a simple interface
for constructing filters and prevents possible state cloning bugs when
copying filters into `EntityPass`.
* Allow for setting filter inputs, setting the effect transform, and
enabling subpass mode on a filter chain after it has been instantiated.
This will allow us to sample coverage in `EntityPass` without rendering
any snapshots using rect filter inputs.
  • Loading branch information
bdero authored Sep 8, 2023
1 parent 7953945 commit 932027f
Show file tree
Hide file tree
Showing 23 changed files with 687 additions and 267 deletions.
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,8 @@ ORIGIN: ../../../flutter/impeller/aiks/color_source.cc + ../../../flutter/LICENS
ORIGIN: ../../../flutter/impeller/aiks/color_source.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/image.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/image.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/image_filter.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/image_filter.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/paint.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/paint.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/aiks/paint_pass_delegate.cc + ../../../flutter/LICENSE
Expand Down Expand Up @@ -3773,6 +3775,8 @@ FILE: ../../../flutter/impeller/aiks/color_source.cc
FILE: ../../../flutter/impeller/aiks/color_source.h
FILE: ../../../flutter/impeller/aiks/image.cc
FILE: ../../../flutter/impeller/aiks/image.h
FILE: ../../../flutter/impeller/aiks/image_filter.cc
FILE: ../../../flutter/impeller/aiks/image_filter.h
FILE: ../../../flutter/impeller/aiks/paint.cc
FILE: ../../../flutter/impeller/aiks/paint.h
FILE: ../../../flutter/impeller/aiks/paint_pass_delegate.cc
Expand Down
2 changes: 2 additions & 0 deletions impeller/aiks/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ impeller_component("aiks") {
"color_source.h",
"image.cc",
"image.h",
"image_filter.cc",
"image_filter.h",
"paint.cc",
"paint.h",
"paint_pass_delegate.cc",
Expand Down
173 changes: 64 additions & 109 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "impeller/aiks/aiks_playground.h"
#include "impeller/aiks/canvas.h"
#include "impeller/aiks/image.h"
#include "impeller/aiks/image_filter.h"
#include "impeller/aiks/paint_pass_delegate.h"
#include "impeller/aiks/testing/context_spy.h"
#include "impeller/core/capture.h"
Expand Down Expand Up @@ -2110,12 +2111,9 @@ TEST_P(AiksTest, PaintWithFilters) {

ASSERT_TRUE(paint.HasColorFilter());

paint.image_filter = [](const FilterInput::Ref& input,
const Matrix& effect_transform, bool is_subpass) {
return FilterContents::MakeGaussianBlur(
input, Sigma(1.0), Sigma(1.0), FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp, effect_transform);
};
paint.image_filter = ImageFilter::MakeBlur(Sigma(1.0), Sigma(1.0),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp);

ASSERT_TRUE(paint.HasColorFilter());

Expand All @@ -2141,12 +2139,9 @@ TEST_P(AiksTest, OpacityPeepHoleApplicationTest) {
ASSERT_FALSE(delegate->CanCollapseIntoParentPass(entity_pass.get()));

paint.color_filter = nullptr;
paint.image_filter = [](const FilterInput::Ref& input,
const Matrix& effect_transform, bool is_subpass) {
return FilterContents::MakeGaussianBlur(
input, Sigma(1.0), Sigma(1.0), FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp, effect_transform);
};
paint.image_filter = ImageFilter::MakeBlur(Sigma(1.0), Sigma(1.0),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp);

// Paint has image filter, can't elide.
delegate = std::make_shared<OpacityPeepholePassDelegate>(paint);
Expand Down Expand Up @@ -2313,11 +2308,9 @@ TEST_P(AiksTest, CollapsedDrawPaintInSubpassBackdropFilter) {
canvas.DrawPaint(
{.color = Color::Yellow(), .blend_mode = BlendMode::kSource});
canvas.SaveLayer({}, {},
[](const FilterInput::Ref& input,
const Matrix& effect_transform, bool is_subpass) {
return FilterContents::MakeGaussianBlur(input, Sigma(20.0),
Sigma(20.0));
});
ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kDecal));
canvas.DrawPaint(
{.color = Color::CornflowerBlue(), .blend_mode = BlendMode::kSourceOver});

Expand Down Expand Up @@ -2515,12 +2508,8 @@ TEST_P(AiksTest, TranslucentSaveLayerWithBlendImageFilterDrawsCorrectly) {

canvas.SaveLayer({
.color = Color::Black().WithAlpha(0.5),
.image_filter =
[](FilterInput::Ref input, const Matrix& effect_transform,
bool is_subpass) {
return ColorFilterContents::MakeBlend(
BlendMode::kDestinationOver, {std::move(input)}, Color::Red());
},
.image_filter = ImageFilter::MakeFromColorFilter(
*ColorFilter::MakeBlend(BlendMode::kDestinationOver, Color::Red())),
});

canvas.DrawRect(Rect::MakeXYWH(100, 500, 300, 300), {.color = Color::Blue()});
Expand Down Expand Up @@ -2589,17 +2578,14 @@ TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixImageFilterDrawsCorrectly) {

canvas.SaveLayer({
.color = Color::Black().WithAlpha(0.5),
.image_filter =
[](FilterInput::Ref input, const Matrix& effect_transform,
bool is_subpass) {
return ColorFilterContents::MakeColorMatrix({std::move(input)},
{.array = {
1, 0, 0, 0, 0, //
0, 1, 0, 0, 0, //
0, 0, 1, 0, 0, //
0, 0, 0, 2, 0 //
}});
},
.image_filter = ImageFilter::MakeFromColorFilter(
*ColorFilter::MakeMatrix({.array =
{
1, 0, 0, 0, 0, //
0, 1, 0, 0, 0, //
0, 0, 1, 0, 0, //
0, 0, 0, 2, 0 //
}})),
});
canvas.DrawImage(image, {100, 500}, {});
canvas.Restore();
Expand All @@ -2616,17 +2602,14 @@ TEST_P(AiksTest,

canvas.SaveLayer({
.color = Color::Black().WithAlpha(0.5),
.image_filter =
[](FilterInput::Ref input, const Matrix& effect_transform,
bool is_subpass) {
return ColorFilterContents::MakeColorMatrix(
{std::move(input)}, {.array = {
1, 0, 0, 0, 0, //
0, 1, 0, 0, 0, //
0, 0.2, 1, 0, 0, //
0, 0, 0, 0.5, 0 //
}});
},
.image_filter = ImageFilter::MakeFromColorFilter(
*ColorFilter::MakeMatrix({.array =
{
1, 0, 0, 0, 0, //
0, 1, 0, 0, 0, //
0, 0.2, 1, 0, 0, //
0, 0, 0, 0.5, 0 //
}})),
.color_filter =
ColorFilter::MakeBlend(BlendMode::kModulate, Color::Green()),
});
Expand Down Expand Up @@ -2758,13 +2741,9 @@ TEST_P(AiksTest, CanRenderBackdropBlurInteractive) {
canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()});
canvas.ClipRRect(Rect::MakeLTRB(a.x, a.y, b.x, b.y), 20);
canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
[](const FilterInput::Ref& input,
const Matrix& effect_transform, bool is_subpass) {
return FilterContents::MakeGaussianBlur(
input, Sigma(20.0), Sigma(20.0),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp, effect_transform);
});
ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp));
canvas.Restore();

return canvas.EndRecordingAsPicture();
Expand All @@ -2781,13 +2760,9 @@ TEST_P(AiksTest, CanRenderBackdropBlur) {
canvas.DrawCircle({180, 120}, 100, {.color = Color::OrangeRed()});
canvas.ClipRRect(Rect::MakeLTRB(75, 50, 375, 275), 20);
canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
[](const FilterInput::Ref& input,
const Matrix& effect_transform, bool is_subpass) {
return FilterContents::MakeGaussianBlur(
input, Sigma(30.0), Sigma(30.0),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp, effect_transform);
});
ImageFilter::MakeBlur(Sigma(30.0), Sigma(30.0),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp));
canvas.Restore();

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
Expand All @@ -2797,13 +2772,9 @@ TEST_P(AiksTest, CanRenderBackdropBlurHugeSigma) {
Canvas canvas;
canvas.DrawCircle({400, 400}, 300, {.color = Color::Green()});
canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
[](const FilterInput::Ref& input,
const Matrix& effect_transform, bool is_subpass) {
return FilterContents::MakeGaussianBlur(
input, Sigma(999999), Sigma(999999),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp, effect_transform);
});
ImageFilter::MakeBlur(Sigma(999999), Sigma(999999),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp));
canvas.Restore();

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
Expand All @@ -2816,14 +2787,9 @@ TEST_P(AiksTest, CanRenderClippedBlur) {
{400, 400}, 200,
{
.color = Color::Green(),
.image_filter =
[](const FilterInput::Ref& input, const Matrix& effect_transform,
bool is_subpass) {
return FilterContents::MakeGaussianBlur(
input, Sigma(20), Sigma(20),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp, effect_transform);
},
.image_filter = ImageFilter::MakeBlur(
Sigma(20.0), Sigma(20.0), FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp),
});
canvas.Restore();

Expand Down Expand Up @@ -3127,11 +3093,9 @@ TEST_P(AiksTest, CanCanvasDrawPictureWithAdvancedBlend) {
TEST_P(AiksTest, CanCanvasDrawPictureWithBackdropFilter) {
Canvas subcanvas;
subcanvas.SaveLayer({}, {},
[](const FilterInput::Ref& input,
const Matrix& effect_transform, bool is_subpass) {
return FilterContents::MakeGaussianBlur(
input, Sigma(20.0), Sigma(20.0));
});
ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kDecal));
auto image = std::make_shared<Image>(CreateTextureForFixture("kalimba.jpg"));
Paint paint;
paint.color = Color::Red().WithAlpha(0.5);
Expand Down Expand Up @@ -3206,17 +3170,12 @@ TEST_P(AiksTest, MatrixSaveLayerFilter) {
.blend_mode = BlendMode::kPlus});
// Should render a second circle, centered on the bottom-right-most edge of
// the circle.
canvas.SaveLayer({.image_filter =
[](const FilterInput::Ref& input,
const Matrix& effect_transform, bool is_subpass) {
Matrix matrix =
Matrix::MakeTranslation(
Vector2(1, 1) * (100 + 100 * k1OverSqrt2)) *
Matrix::MakeScale(Vector2(1, 1) * 0.2) *
Matrix::MakeTranslation(Vector2(-100, -100));
return FilterContents::MakeMatrixFilter(
input, matrix, {}, Matrix(), true);
}},
canvas.SaveLayer({.image_filter = ImageFilter::MakeMatrix(
Matrix::MakeTranslation(Vector2(1, 1) *
(200 + 100 * k1OverSqrt2)) *
Matrix::MakeScale(Vector2(1, 1) * 0.2) *
Matrix::MakeTranslation(Vector2(-200, -200)),
SamplerDescriptor{})},
std::nullopt);
canvas.DrawCircle(Point(200, 200), 100,
{.color = Color::Green().WithAlpha(0.5),
Expand All @@ -3238,17 +3197,13 @@ TEST_P(AiksTest, MatrixBackdropFilter) {
.blend_mode = BlendMode::kPlus});
// Should render a second circle, centered on the bottom-right-most edge of
// the circle.
canvas.SaveLayer({}, std::nullopt,
[](const FilterInput::Ref& input,
const Matrix& effect_transform, bool is_subpass) {
Matrix matrix =
Matrix::MakeTranslation(Vector2(1, 1) *
(100 + 100 * k1OverSqrt2)) *
Matrix::MakeScale(Vector2(1, 1) * 0.2) *
Matrix::MakeTranslation(Vector2(-100, -100));
return FilterContents::MakeMatrixFilter(
input, matrix, {}, Matrix(), true);
});
canvas.SaveLayer(
{}, std::nullopt,
ImageFilter::MakeMatrix(
Matrix::MakeTranslation(Vector2(1, 1) * (100 + 100 * k1OverSqrt2)) *
Matrix::MakeScale(Vector2(1, 1) * 0.2) *
Matrix::MakeTranslation(Vector2(-100, -100)),
SamplerDescriptor{}));
canvas.Restore();
}
canvas.Restore();
Expand Down Expand Up @@ -3329,14 +3284,14 @@ TEST_P(AiksTest, PipelineBlendSingleParameter) {
canvas.Translate(Point(100, 100));
canvas.DrawCircle(Point(200, 200), 200, {.color = Color::Blue()});
canvas.ClipRect(Rect(100, 100, 200, 200));
canvas.DrawCircle(
Point(200, 200), 200,
{.color = Color::Green(),
.blend_mode = BlendMode::kSourceOver,
.image_filter = [](const FilterInput::Ref& input,
const Matrix& effect_transform, bool is_subpass) {
return ColorFilterContents::MakeBlend(BlendMode::kSource, {input});
}});
canvas.DrawCircle(Point(200, 200), 200,
{
.color = Color::Green(),
.blend_mode = BlendMode::kSourceOver,
.image_filter = ImageFilter::MakeFromColorFilter(
*ColorFilter::MakeBlend(BlendMode::kDestination,
Color::White())),
});
canvas.Restore();
}

Expand Down
18 changes: 15 additions & 3 deletions impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <utility>

#include "flutter/fml/logging.h"
#include "impeller/aiks/image_filter.h"
#include "impeller/aiks/paint_pass_delegate.h"
#include "impeller/entity/contents/atlas_contents.h"
#include "impeller/entity/contents/clip_contents.h"
Expand Down Expand Up @@ -57,7 +58,7 @@ void Canvas::Save() {

void Canvas::Save(bool create_subpass,
BlendMode blend_mode,
EntityPass::BackdropFilterProc backdrop_filter) {
const std::shared_ptr<ImageFilter>& backdrop_filter) {
auto entry = CanvasStackEntry{};
entry.xformation = xformation_stack_.back().xformation;
entry.cull_rect = xformation_stack_.back().cull_rect;
Expand All @@ -67,7 +68,18 @@ void Canvas::Save(bool create_subpass,
auto subpass = std::make_unique<EntityPass>();
subpass->SetEnableOffscreenCheckerboard(
debug_options.offscreen_texture_checkerboard);
subpass->SetBackdropFilter(std::move(backdrop_filter));
if (backdrop_filter) {
EntityPass::BackdropFilterProc backdrop_filter_proc =
[backdrop_filter = backdrop_filter->Clone()](
const FilterInput::Ref& input, const Matrix& effect_transform,
bool is_subpass) {
auto filter = backdrop_filter->WrapInput(input);
filter->SetEffectTransform(effect_transform);
filter->SetIsForSubpass(is_subpass);
return filter;
};
subpass->SetBackdropFilter(backdrop_filter_proc);
}
subpass->SetBlendMode(blend_mode);
current_pass_ = GetCurrentPass().AddSubpass(std::move(subpass));
current_pass_->SetTransformation(xformation_stack_.back().xformation);
Expand Down Expand Up @@ -518,7 +530,7 @@ size_t Canvas::GetStencilDepth() const {

void Canvas::SaveLayer(const Paint& paint,
std::optional<Rect> bounds,
const Paint::ImageFilterProc& backdrop_filter) {
const std::shared_ptr<ImageFilter>& backdrop_filter) {
Save(true, paint.blend_mode, backdrop_filter);

auto& new_layer_pass = GetCurrentPass();
Expand Down
5 changes: 3 additions & 2 deletions impeller/aiks/canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "flutter/fml/macros.h"
#include "impeller/aiks/image.h"
#include "impeller/aiks/image_filter.h"
#include "impeller/aiks/paint.h"
#include "impeller/aiks/picture.h"
#include "impeller/core/sampler_descriptor.h"
Expand Down Expand Up @@ -68,7 +69,7 @@ class Canvas {

void SaveLayer(const Paint& paint,
std::optional<Rect> bounds = std::nullopt,
const Paint::ImageFilterProc& backdrop_filter = nullptr);
const std::shared_ptr<ImageFilter>& backdrop_filter = nullptr);

bool Restore();

Expand Down Expand Up @@ -180,7 +181,7 @@ class Canvas {

void Save(bool create_subpass,
BlendMode = BlendMode::kSourceOver,
EntityPass::BackdropFilterProc backdrop_filter = nullptr);
const std::shared_ptr<ImageFilter>& backdrop_filter = nullptr);

void RestoreClip();

Expand Down
Loading

0 comments on commit 932027f

Please sign in to comment.