Skip to content

Commit 68d88ae

Browse files
bderodnfield
authored andcommitted
Filters: Add local transforms (flutter#140)
1 parent 2b48a80 commit 68d88ae

9 files changed

+298
-86
lines changed

impeller/entity/contents/contents.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Contents {
4040
virtual std::optional<Rect> GetCoverage(const Entity& entity) const;
4141

4242
/// @brief Render this contents to a snapshot, respecting the entity's
43-
/// transform, path, stencil depth, blend mode, etc.
43+
/// transform, path, stencil depth, and blend mode.
4444
/// The result texture size is always the size of
4545
/// `GetCoverage(entity)`.
4646
virtual std::optional<Snapshot> RenderToSnapshot(

impeller/entity/contents/filters/border_mask_blur_filter_contents.cc

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,18 +108,21 @@ bool BorderMaskBlurFilterContents::RenderFilter(
108108
return pass.AddCommand(std::move(cmd));
109109
}
110110

111-
std::optional<Rect> BorderMaskBlurFilterContents::GetCoverage(
111+
std::optional<Rect> BorderMaskBlurFilterContents::GetFilterCoverage(
112+
const FilterInput::Vector& inputs,
112113
const Entity& entity) const {
113-
auto coverage = FilterContents::GetCoverage(entity);
114+
if (inputs.empty()) {
115+
return std::nullopt;
116+
}
117+
118+
auto coverage = inputs[0]->GetCoverage(entity);
114119
if (!coverage.has_value()) {
115120
return std::nullopt;
116121
}
117122

118-
// Technically this works with all of our current filters, but this should be
119-
// using the input[0] transform, not the entity transform!
120-
// See: https://github.com/flutter/impeller/pull/130#issuecomment-1098892423
121123
auto transformed_blur_vector =
122-
entity.GetTransformation()
124+
inputs[0]
125+
->GetTransform(entity)
123126
.TransformDirection(
124127
Vector2(Radius{sigma_x_}.radius, Radius{sigma_y_}.radius))
125128
.Abs();

impeller/entity/contents/filters/border_mask_blur_filter_contents.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ class BorderMaskBlurFilterContents final : public FilterContents {
2121

2222
void SetBlurStyle(BlurStyle blur_style);
2323

24-
// |Contents|
25-
std::optional<Rect> GetCoverage(const Entity& entity) const override;
24+
// |FilterContents|
25+
std::optional<Rect> GetFilterCoverage(const FilterInput::Vector& inputs,
26+
const Entity& entity) const override;
2627

2728
private:
2829
// |FilterContents|
@@ -31,6 +32,7 @@ class BorderMaskBlurFilterContents final : public FilterContents {
3132
const Entity& entity,
3233
RenderPass& pass,
3334
const Rect& coverage) const override;
35+
3436
Sigma sigma_x_;
3537
Sigma sigma_y_;
3638
BlurStyle blur_style_ = BlurStyle::kNormal;

impeller/entity/contents/filters/filter_contents.cc

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ std::shared_ptr<FilterContents> FilterContents::MakeBlend(
5252
new_blend->SetInputs({blend_input, *in_i});
5353
new_blend->SetBlendMode(blend_mode);
5454
if (in_i < inputs.end() - 1) {
55-
blend_input = FilterInput::Make(new_blend);
55+
blend_input = FilterInput::Make(
56+
std::static_pointer_cast<FilterContents>(new_blend));
5657
}
5758
}
5859
// new_blend will always be assigned because inputs.size() >= 2.
@@ -139,6 +140,15 @@ bool FilterContents::Render(const ContentContext& renderer,
139140
}
140141

141142
std::optional<Rect> FilterContents::GetCoverage(const Entity& entity) const {
143+
Entity entity_with_local_transform = entity;
144+
entity_with_local_transform.SetTransformation(
145+
GetTransform(entity.GetTransformation()));
146+
return GetFilterCoverage(inputs_, entity_with_local_transform);
147+
}
148+
149+
std::optional<Rect> FilterContents::GetFilterCoverage(
150+
const FilterInput::Vector& inputs,
151+
const Entity& entity) const {
142152
// The default coverage of FilterContents is just the union of its inputs'
143153
// coverage. FilterContents implementations may choose to adjust this
144154
// coverage depending on the use case.
@@ -148,7 +158,7 @@ std::optional<Rect> FilterContents::GetCoverage(const Entity& entity) const {
148158
}
149159

150160
std::optional<Rect> result;
151-
for (const auto& input : inputs_) {
161+
for (const auto& input : inputs) {
152162
auto coverage = input->GetCoverage(entity);
153163
if (!coverage.has_value()) {
154164
continue;
@@ -157,32 +167,45 @@ std::optional<Rect> FilterContents::GetCoverage(const Entity& entity) const {
157167
result = coverage;
158168
continue;
159169
}
160-
result = result->Union(result.value());
170+
result = result->Union(coverage.value());
161171
}
162172
return result;
163173
}
164174

165175
std::optional<Snapshot> FilterContents::RenderToSnapshot(
166176
const ContentContext& renderer,
167177
const Entity& entity) const {
168-
auto bounds = GetCoverage(entity);
169-
if (!bounds.has_value() || bounds->IsEmpty()) {
178+
Entity entity_with_local_transform = entity;
179+
entity_with_local_transform.SetTransformation(
180+
GetTransform(entity.GetTransformation()));
181+
182+
auto coverage = GetFilterCoverage(inputs_, entity_with_local_transform);
183+
if (!coverage.has_value() || coverage->IsEmpty()) {
170184
return std::nullopt;
171185
}
172186

173187
// Render the filter into a new texture.
174188
auto texture = renderer.MakeSubpass(
175-
ISize(bounds->size),
189+
ISize(coverage->size),
176190
[=](const ContentContext& renderer, RenderPass& pass) -> bool {
177-
return RenderFilter(inputs_, renderer, entity, pass, bounds.value());
191+
return RenderFilter(inputs_, renderer, entity_with_local_transform,
192+
pass, coverage.value());
178193
});
179194

180195
if (!texture) {
181196
return std::nullopt;
182197
}
183198

184199
return Snapshot{.texture = texture,
185-
.transform = Matrix::MakeTranslation(bounds->origin)};
200+
.transform = Matrix::MakeTranslation(coverage->origin)};
201+
}
202+
203+
Matrix FilterContents::GetLocalTransform() const {
204+
return Matrix();
205+
}
206+
207+
Matrix FilterContents::GetTransform(const Matrix& parent_transform) const {
208+
return parent_transform * GetLocalTransform();
186209
}
187210

188211
} // namespace impeller

impeller/entity/contents/filters/filter_contents.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,23 +122,31 @@ class FilterContents : public Contents {
122122
std::optional<Rect> GetCoverage(const Entity& entity) const override;
123123

124124
// |Contents|
125-
virtual std::optional<Snapshot> RenderToSnapshot(
126-
const ContentContext& renderer,
127-
const Entity& entity) const override;
125+
std::optional<Snapshot> RenderToSnapshot(const ContentContext& renderer,
126+
const Entity& entity) const override;
127+
128+
virtual Matrix GetLocalTransform() const;
129+
130+
Matrix GetTransform(const Matrix& parent_transform) const;
128131

129132
private:
130-
/// @brief Takes a set of zero or more input textures and writes to an output
131-
/// texture.
133+
virtual std::optional<Rect> GetFilterCoverage(
134+
const FilterInput::Vector& inputs,
135+
const Entity& entity) const;
136+
137+
/// @brief Takes a set of zero or more input textures and writes to an output
138+
/// texture.
132139
virtual bool RenderFilter(const FilterInput::Vector& inputs,
133140
const ContentContext& renderer,
134141
const Entity& entity,
135142
RenderPass& pass,
136-
const Rect& bounds) const = 0;
143+
const Rect& coverage) const = 0;
137144

138145
FilterInput::Vector inputs_;
139-
Rect destination_;
140146

141147
FML_DISALLOW_COPY_AND_ASSIGN(FilterContents);
148+
149+
friend FilterContentsFilterInput;
142150
};
143151

144152
} // namespace impeller

impeller/entity/contents/filters/filter_input.cc

Lines changed: 118 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,38 @@
88
#include <initializer_list>
99
#include <memory>
1010
#include <optional>
11+
#include <variant>
1112

13+
#include "fml/logging.h"
14+
#include "impeller/entity/contents/filters/filter_contents.h"
1215
#include "impeller/entity/contents/snapshot.h"
1316
#include "impeller/entity/entity.h"
1417

1518
namespace impeller {
1619

20+
/*******************************************************************************
21+
******* FilterInput
22+
******************************************************************************/
23+
1724
FilterInput::Ref FilterInput::Make(Variant input) {
18-
return std::shared_ptr<FilterInput>(new FilterInput(input));
25+
if (auto filter = std::get_if<std::shared_ptr<FilterContents>>(&input)) {
26+
return std::static_pointer_cast<FilterInput>(
27+
std::shared_ptr<FilterContentsFilterInput>(
28+
new FilterContentsFilterInput(*filter)));
29+
}
30+
31+
if (auto contents = std::get_if<std::shared_ptr<Contents>>(&input)) {
32+
return std::static_pointer_cast<FilterInput>(
33+
std::shared_ptr<ContentsFilterInput>(
34+
new ContentsFilterInput(*contents)));
35+
}
36+
37+
if (auto texture = std::get_if<std::shared_ptr<Texture>>(&input)) {
38+
return std::static_pointer_cast<FilterInput>(
39+
std::shared_ptr<TextureFilterInput>(new TextureFilterInput(*texture)));
40+
}
41+
42+
FML_UNREACHABLE();
1943
}
2044

2145
FilterInput::Vector FilterInput::Make(std::initializer_list<Variant> inputs) {
@@ -27,63 +51,118 @@ FilterInput::Vector FilterInput::Make(std::initializer_list<Variant> inputs) {
2751
return result;
2852
}
2953

30-
FilterInput::Variant FilterInput::GetInput() const {
31-
return input_;
54+
Matrix FilterInput::GetLocalTransform(const Entity& entity) const {
55+
return Matrix();
3256
}
3357

34-
std::optional<Rect> FilterInput::GetCoverage(const Entity& entity) const {
35-
if (snapshot_) {
36-
return snapshot_->GetCoverage();
37-
}
58+
Matrix FilterInput::GetTransform(const Entity& entity) const {
59+
return entity.GetTransformation() * GetLocalTransform(entity);
60+
}
3861

39-
if (auto contents = std::get_if<std::shared_ptr<Contents>>(&input_)) {
40-
return contents->get()->GetCoverage(entity);
41-
}
62+
FilterInput::~FilterInput() = default;
4263

43-
if (auto texture = std::get_if<std::shared_ptr<Texture>>(&input_)) {
44-
return entity.GetPathCoverage();
45-
}
64+
/*******************************************************************************
65+
******* FilterContentsFilterInput
66+
******************************************************************************/
4667

47-
FML_UNREACHABLE();
68+
FilterContentsFilterInput::FilterContentsFilterInput(
69+
std::shared_ptr<FilterContents> filter)
70+
: filter_(filter) {}
71+
72+
FilterContentsFilterInput::~FilterContentsFilterInput() = default;
73+
74+
FilterInput::Variant FilterContentsFilterInput::GetInput() const {
75+
return filter_;
4876
}
4977

50-
std::optional<Snapshot> FilterInput::GetSnapshot(const ContentContext& renderer,
51-
const Entity& entity) const {
52-
if (snapshot_) {
53-
return snapshot_;
78+
std::optional<Snapshot> FilterContentsFilterInput::GetSnapshot(
79+
const ContentContext& renderer,
80+
const Entity& entity) const {
81+
if (!snapshot_.has_value()) {
82+
snapshot_ = filter_->RenderToSnapshot(renderer, entity);
5483
}
55-
snapshot_ = MakeSnapshot(renderer, entity);
56-
5784
return snapshot_;
5885
}
5986

60-
FilterInput::FilterInput(Variant input) : input_(input) {}
87+
std::optional<Rect> FilterContentsFilterInput::GetCoverage(
88+
const Entity& entity) const {
89+
return filter_->GetCoverage(entity);
90+
}
6191

62-
FilterInput::~FilterInput() = default;
92+
Matrix FilterContentsFilterInput::GetLocalTransform(
93+
const Entity& entity) const {
94+
return filter_->GetLocalTransform();
95+
}
6396

64-
std::optional<Snapshot> FilterInput::MakeSnapshot(
97+
Matrix FilterContentsFilterInput::GetTransform(const Entity& entity) const {
98+
return filter_->GetTransform(entity.GetTransformation());
99+
}
100+
101+
/*******************************************************************************
102+
******* ContentsFilterInput
103+
******************************************************************************/
104+
105+
ContentsFilterInput::ContentsFilterInput(std::shared_ptr<Contents> contents)
106+
: contents_(contents) {}
107+
108+
ContentsFilterInput::~ContentsFilterInput() = default;
109+
110+
FilterInput::Variant ContentsFilterInput::GetInput() const {
111+
return contents_;
112+
}
113+
114+
std::optional<Snapshot> ContentsFilterInput::GetSnapshot(
65115
const ContentContext& renderer,
66116
const Entity& entity) const {
67-
if (auto contents = std::get_if<std::shared_ptr<Contents>>(&input_)) {
68-
return contents->get()->RenderToSnapshot(renderer, entity);
117+
if (!snapshot_.has_value()) {
118+
snapshot_ = contents_->RenderToSnapshot(renderer, entity);
69119
}
120+
return snapshot_;
121+
}
122+
123+
std::optional<Rect> ContentsFilterInput::GetCoverage(
124+
const Entity& entity) const {
125+
return contents_->GetCoverage(entity);
126+
}
127+
128+
/*******************************************************************************
129+
******* TextureFilterInput
130+
******************************************************************************/
70131

71-
if (auto texture = std::get_if<std::shared_ptr<Texture>>(&input_)) {
72-
// Rendered textures stretch to fit the entity path coverage, so we
73-
// incorporate this behavior by translating and scaling the snapshot
74-
// transform.
75-
auto path_bounds = entity.GetPath().GetBoundingBox();
76-
if (!path_bounds.has_value()) {
77-
return std::nullopt;
78-
}
79-
auto transform = entity.GetTransformation() *
80-
Matrix::MakeTranslation(path_bounds->origin) *
81-
Matrix::MakeScale(Vector2(path_bounds->size) /
82-
texture->get()->GetSize());
83-
return Snapshot{.texture = *texture, .transform = transform};
132+
TextureFilterInput::TextureFilterInput(std::shared_ptr<Texture> texture)
133+
: texture_(texture) {}
134+
135+
TextureFilterInput::~TextureFilterInput() = default;
136+
137+
FilterInput::Variant TextureFilterInput::GetInput() const {
138+
return texture_;
139+
}
140+
141+
std::optional<Snapshot> TextureFilterInput::GetSnapshot(
142+
const ContentContext& renderer,
143+
const Entity& entity) const {
144+
return Snapshot{.texture = texture_, .transform = GetTransform(entity)};
145+
}
146+
147+
std::optional<Rect> TextureFilterInput::GetCoverage(
148+
const Entity& entity) const {
149+
auto path_bounds = entity.GetPath().GetBoundingBox();
150+
if (!path_bounds.has_value()) {
151+
return std::nullopt;
84152
}
153+
return Rect::MakeSize(Size(texture_->GetSize()))
154+
.TransformBounds(GetTransform(entity));
155+
}
85156

86-
FML_UNREACHABLE();
157+
Matrix TextureFilterInput::GetLocalTransform(const Entity& entity) const {
158+
// Compute the local transform such that the texture will cover the entity
159+
// path bounding box.
160+
auto path_bounds = entity.GetPath().GetBoundingBox();
161+
if (!path_bounds.has_value()) {
162+
return Matrix();
163+
}
164+
return Matrix::MakeTranslation(path_bounds->origin) *
165+
Matrix::MakeScale(Vector2(path_bounds->size) / texture_->GetSize());
87166
}
88167

89168
} // namespace impeller

0 commit comments

Comments
 (0)