| Flutter Impeller |

gaussian_blur_filter_contents.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h"
6
7 #include <cmath>
8 #include <utility>
9 #include <valarray>
10
11 #include "impeller/base/strings.h"
12 #include "impeller/base/validation.h"
13 #include "impeller/core/formats.h"
14 #include "impeller/core/sampler_descriptor.h"
15 #include "impeller/entity/contents/content_context.h"
16 #include "impeller/entity/contents/filters/filter_contents.h"
17 #include "impeller/geometry/rect.h"
18 #include "impeller/geometry/scalar.h"
19 #include "impeller/renderer/command_buffer.h"
20 #include "impeller/renderer/render_pass.h"
21 #include "impeller/renderer/render_target.h"
22 #include "impeller/renderer/sampler_library.h"
23
24 namespace impeller {
25
26 DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() =
27 default;
28
29 DirectionalGaussianBlurFilterContents::
30 ~DirectionalGaussianBlurFilterContents() = default;
31
32 void DirectionalGaussianBlurFilterContents::SetSigma(Sigma sigma) {
33 blur_sigma_ = sigma;
34 }
35
36 void DirectionalGaussianBlurFilterContents::SetSecondarySigma(Sigma sigma) {
37 secondary_blur_sigma_ = sigma;
38 }
39
40 void DirectionalGaussianBlurFilterContents::SetDirection(Vector2 direction) {
41 blur_direction_ = direction.Normalize();
42 if (blur_direction_.IsZero()) {
43 blur_direction_ = Vector2(0, 1);
44 }
45 }
46
47 void DirectionalGaussianBlurFilterContents::SetBlurStyle(BlurStyle blur_style) {
48 blur_style_ = blur_style;
49 }
50
51 void DirectionalGaussianBlurFilterContents::SetTileMode(
52 Entity::TileMode tile_mode) {
53 tile_mode_ = tile_mode;
54 }
55
56 void DirectionalGaussianBlurFilterContents::SetIsSecondPass(
57 bool is_second_pass) {
58 is_second_pass_ = is_second_pass;
59 }
60
61 std::optional<Entity> DirectionalGaussianBlurFilterContents::RenderFilter(
62 const FilterInput::Vector& inputs,
63 const ContentContext& renderer,
64 const Entity& entity,
65 const Matrix& effect_transform,
66 const Rect& coverage,
67 const std::optional<Rect>& coverage_hint) const {
68 using VS = GaussianBlurPipeline::VertexShader;
69 using FS = GaussianBlurPipeline::FragmentShader;
70
71 //----------------------------------------------------------------------------
72 /// Handle inputs.
73 ///
74
75 if (inputs.empty()) {
76 return std::nullopt;
77 }
78
79 // Limit the kernel size to 1000x1000 pixels, like Skia does.
80 auto radius = std::min(Radius{blur_sigma_}.radius, 500.0f);
81
82 auto transform = entity.GetTransformation() * effect_transform.Basis();
83 auto transformed_blur_radius =
84 transform.TransformDirection(blur_direction_ * radius);
85
86 auto transformed_blur_radius_length = transformed_blur_radius.GetLength();
87
88 // Input 0 snapshot.
89
90 std::optional<Rect> expanded_coverage_hint;
91 if (coverage_hint.has_value()) {
92 auto r =
93 Size(transformed_blur_radius_length, transformed_blur_radius_length)
94 .Abs();
95 expanded_coverage_hint =
96 is_second_pass_ ? coverage_hint
97 : Rect(coverage_hint.value().origin - r,
98 Size(coverage_hint.value().size + r * 2));
99 }
100 auto input_snapshot = inputs[0]->GetSnapshot("GaussianBlur", renderer, entity,
101 expanded_coverage_hint);
102 if (!input_snapshot.has_value()) {
103 return std::nullopt;
104 }
105
106 if (blur_sigma_.sigma < kEhCloseEnough) {
107 return Entity::FromSnapshot(
108 input_snapshot.value(), entity.GetBlendMode(),
109 entity.GetStencilDepth()); // No blur to render.
110 }
111
112 // If the radius length is < .5, the shader will take at most 1 sample,
113 // resulting in no blur.
114 if (transformed_blur_radius_length < .5) {
115 return Entity::FromSnapshot(
116 input_snapshot.value(), entity.GetBlendMode(),
117 entity.GetStencilDepth()); // No blur to render.
118 }
119
120 // A matrix that rotates the snapshot space such that the blur direction is
121 // +X.
122 auto texture_rotate = Matrix::MakeRotationZ(
123 transformed_blur_radius.Normalize().AngleTo({1, 0}));
124
125 // Converts local pass space to screen space. This is just the snapshot space
126 // rotated such that the blur direction is +X.
127 auto pass_transform = texture_rotate * input_snapshot->transform;
128
129 // The pass texture coverage, but rotated such that the blur is in the +X
130 // direction, and expanded to include the blur radius. This is used for UV
131 // projection and as a source for the pass size. Note that it doesn't matter
132 // which direction the space is rotated in when grabbing the pass size.
133 auto pass_texture_rect = Rect::MakeSize(input_snapshot->texture->GetSize())
134 .TransformBounds(pass_transform);
135 pass_texture_rect.origin.x -= transformed_blur_radius_length;
136 pass_texture_rect.size.width += transformed_blur_radius_length * 2;
137
138 // UV mapping.
139
140 auto pass_uv_project = [&texture_rotate,
141 &pass_texture_rect](Snapshot& input) {
142 auto uv_matrix = Matrix::MakeScale(1 / Vector2(input.texture->GetSize())) *
143 (texture_rotate * input.transform).Invert();
144 return pass_texture_rect.GetTransformedPoints(uv_matrix);
145 };
146
147 auto input_uvs = pass_uv_project(input_snapshot.value());
148
149 //----------------------------------------------------------------------------
150 /// Render to texture.
151 ///
152
153 ContentContext::SubpassCallback subpass_callback = [&](const ContentContext&
154 renderer,
155 RenderPass& pass) {
156 auto& host_buffer = pass.GetTransientsBuffer();
157
158 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
159 vtx_builder.AddVertices({
160 {Point(0, 0), input_uvs[0]},
161 {Point(1, 0), input_uvs[1]},
162 {Point(1, 1), input_uvs[3]},
163 {Point(0, 0), input_uvs[0]},
164 {Point(1, 1), input_uvs[3]},
165 {Point(0, 1), input_uvs[2]},
166 });
167 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
168
169 VS::FrameInfo frame_info;
170 frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
171 frame_info.texture_sampler_y_coord_scale =
172 input_snapshot->texture->GetYCoordScale();
173
174 FS::BlurInfo frag_info;
175 auto r = Radius{transformed_blur_radius_length};
176 frag_info.blur_sigma = Sigma{r}.sigma;
177 frag_info.blur_radius = std::round(r.radius);
178
179 // The blur direction is in input UV space.
180 frag_info.blur_uv_offset =
181 pass_transform.Invert().TransformDirection(Vector2(1, 0)).Normalize() /
182 Point(input_snapshot->GetCoverage().value().size);
183
184 Command cmd;
185 DEBUG_COMMAND_INFO(cmd, SPrintF("Gaussian Blur Filter (Radius=%.2f)",
186 transformed_blur_radius_length));
187 cmd.BindVertices(vtx_buffer);
188
189 auto options = OptionsFromPass(pass);
190 options.blend_mode = BlendMode::kSource;
191 auto input_descriptor = input_snapshot->sampler_descriptor;
192 switch (tile_mode_) {
193 case Entity::TileMode::kDecal:
194 if (renderer.GetDeviceCapabilities()
195 .SupportsDecalSamplerAddressMode()) {
196 input_descriptor.width_address_mode = SamplerAddressMode::kDecal;
197 input_descriptor.height_address_mode = SamplerAddressMode::kDecal;
198 }
199 break;
200 case Entity::TileMode::kClamp:
201 input_descriptor.width_address_mode = SamplerAddressMode::kClampToEdge;
202 input_descriptor.height_address_mode = SamplerAddressMode::kClampToEdge;
203 break;
204 case Entity::TileMode::kMirror:
205 input_descriptor.width_address_mode = SamplerAddressMode::kMirror;
206 input_descriptor.height_address_mode = SamplerAddressMode::kMirror;
207 break;
208 case Entity::TileMode::kRepeat:
209 input_descriptor.width_address_mode = SamplerAddressMode::kRepeat;
210 input_descriptor.height_address_mode = SamplerAddressMode::kRepeat;
211 break;
212 }
213 input_descriptor.mag_filter = MinMagFilter::kLinear;
214 input_descriptor.min_filter = MinMagFilter::kLinear;
215
216 bool has_decal_specialization =
217 tile_mode_ == Entity::TileMode::kDecal &&
218 !renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode();
219
220 if (has_decal_specialization) {
221 cmd.pipeline = renderer.GetGaussianBlurDecalPipeline(options);
222 } else {
223 cmd.pipeline = renderer.GetGaussianBlurPipeline(options);
224 }
225
226 FS::BindTextureSampler(
227 cmd, input_snapshot->texture,
228 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
229 input_descriptor));
230 VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
231 FS::BindBlurInfo(cmd, host_buffer.EmplaceUniform(frag_info));
232
233 return pass.AddCommand(std::move(cmd));
234 };
235
236 Vector2 scale;
237 auto scale_curve = [](Scalar radius) {
238 constexpr Scalar decay = 4.0; // Larger is more gradual.
239 constexpr Scalar limit = 0.95; // The maximum percentage of the scaledown.
240 const Scalar curve =
241 std::min(1.0, decay / (std::max(1.0f, radius) + decay - 1.0));
242 return (curve - 1) * limit + 1;
243 };
244 {
245 scale.x = scale_curve(transformed_blur_radius_length);
246
247 Scalar y_radius = std::abs(pass_transform.GetDirectionScale(Vector2(
248 0, !is_second_pass_ ? 1 : Radius{secondary_blur_sigma_}.radius)));
249 scale.y = scale_curve(y_radius);
250 }
251
252 Vector2 scaled_size = pass_texture_rect.size * scale;
253 ISize floored_size = ISize(scaled_size.x, scaled_size.y);
254
255 auto out_texture = renderer.MakeSubpass("Directional Gaussian Blur Filter",
256 floored_size, subpass_callback);
257
258 if (!out_texture) {
259 return std::nullopt;
260 }
261
262 SamplerDescriptor sampler_desc;
263 sampler_desc.min_filter = MinMagFilter::kLinear;
264 sampler_desc.mag_filter = MinMagFilter::kLinear;
265 sampler_desc.width_address_mode = SamplerAddressMode::kClampToEdge;
266 sampler_desc.width_address_mode = SamplerAddressMode::kClampToEdge;
267
268 return Entity::FromSnapshot(
269 Snapshot{.texture = out_texture,
270 .transform = texture_rotate.Invert() *
271 Matrix::MakeTranslation(pass_texture_rect.origin) *
272 Matrix::MakeScale((1 / scale) *
273 (scaled_size / floored_size)),
274 .sampler_descriptor = sampler_desc,
275 .opacity = input_snapshot->opacity},
276 entity.GetBlendMode(), entity.GetStencilDepth());
277 }
278
279 std::optional<Rect> DirectionalGaussianBlurFilterContents::GetFilterCoverage(
280 const FilterInput::Vector& inputs,
281 const Entity& entity,
282 const Matrix& effect_transform) const {
283 if (inputs.empty()) {
284 return std::nullopt;
285 }
286
287 auto coverage = inputs[0]->GetCoverage(entity);
288 if (!coverage.has_value()) {
289 return std::nullopt;
290 }
291
292 auto transform = inputs[0]->GetTransform(entity) * effect_transform.Basis();
293 auto transformed_blur_vector =
294 transform.TransformDirection(blur_direction_ * Radius{blur_sigma_}.radius)
295 .Abs();
296 auto extent = coverage->size + transformed_blur_vector * 2;
297 return Rect(coverage->origin - transformed_blur_vector,
298 Size(extent.x, extent.y));
299 }
300
301 } // namespace impeller
impeller::Sigma::sigma
Scalar sigma
Definition: sigma.h:32
impeller::Entity::TileMode::kClamp
@ kClamp
impeller::OptionsFromPass
ContentContextOptions OptionsFromPass(const RenderPass &pass)
Definition: contents.cc:20
DEBUG_COMMAND_INFO
#define DEBUG_COMMAND_INFO(obj, arg)
Definition: command.h:31
impeller::DirectionalGaussianBlurFilterContents::SetIsSecondPass
void SetIsSecondPass(bool is_second_pass)
Definition: gaussian_blur_filter_contents.cc:56
impeller::Entity::GetStencilDepth
uint32_t GetStencilDepth() const
Definition: entity.cc:89
impeller::TRect::size
TSize< Type > size
Definition: rect.h:24
impeller::DirectionalGaussianBlurFilterContents::SetBlurStyle
void SetBlurStyle(BlurStyle blur_style)
Definition: gaussian_blur_filter_contents.cc:47
impeller::Scalar
float Scalar
Definition: scalar.h:15
impeller::Entity::FromSnapshot
static std::optional< Entity > FromSnapshot(const std::optional< Snapshot > &snapshot, BlendMode blend_mode=BlendMode::kSourceOver, uint32_t stencil_depth=0)
Create an entity that can be used to render a given snapshot.
Definition: entity.cc:21
sampler_library.h
impeller::DirectionalGaussianBlurFilterContents::SetSecondarySigma
void SetSecondarySigma(Sigma sigma)
Definition: gaussian_blur_filter_contents.cc:36
impeller::FilterContents::BlurStyle
BlurStyle
Definition: filter_contents.h:21
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:55
impeller::Entity::TileMode::kDecal
@ kDecal
impeller::TRect::TransformBounds
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:204
formats.h
impeller::Vector2
Point Vector2
Definition: point.h:310
impeller::RenderPipelineT::FragmentShader
FragmentShader_ FragmentShader
Definition: pipeline.h:91
impeller::SamplerAddressMode::kClampToEdge
@ kClampToEdge
gaussian_blur_filter_contents.h
impeller::Size
TSize< Scalar > Size
Definition: size.h:135
impeller::Entity::TileMode::kRepeat
@ kRepeat
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:94
validation.h
impeller::Entity::GetTransformation
const Matrix & GetTransformation() const
Definition: entity.cc:49
impeller::Entity::TileMode::kMirror
@ kMirror
impeller::TPoint::IsZero
constexpr bool IsZero() const
Definition: point.h:227
impeller::Entity
Definition: entity.h:21
impeller::Matrix::Basis
constexpr Matrix Basis() const
Definition: matrix.h:222
impeller::TSize
Definition: size.h:18
impeller::Point
TPoint< Scalar > Point
Definition: point.h:306
filter_contents.h
render_pass.h
impeller::Radius
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
Definition: sigma.h:47
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
impeller::MinMagFilter::kLinear
@ kLinear
impeller::Sigma
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:31
impeller::TRect::origin
TPoint< Type > origin
Definition: rect.h:23
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:306
strings.h
impeller::Entity::TileMode
TileMode
Definition: entity.h:40
impeller::Entity::GetBlendMode
BlendMode GetBlendMode() const
Definition: entity.cc:101
impeller::TPoint::x
Type x
Definition: point.h:23
impeller::DirectionalGaussianBlurFilterContents::SetTileMode
void SetTileMode(Entity::TileMode tile_mode)
Definition: gaussian_blur_filter_contents.cc:51
scalar.h
impeller::ContentContext::SubpassCallback
std::function< bool(const ContentContext &, RenderPass &)> SubpassCallback
Definition: content_context.h:703
impeller::Matrix::TransformDirection
constexpr Vector4 TransformDirection(const Vector4 &v) const
Definition: matrix.h:431
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:136
sampler_descriptor.h
command_buffer.h
impeller::DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents
DirectionalGaussianBlurFilterContents()
content_context.h
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:208
impeller::DirectionalGaussianBlurFilterContents::SetSigma
void SetSigma(Sigma sigma)
Definition: gaussian_blur_filter_contents.cc:32
impeller::SamplerAddressMode::kMirror
@ kMirror
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:52
impeller::DirectionalGaussianBlurFilterContents::~DirectionalGaussianBlurFilterContents
~DirectionalGaussianBlurFilterContents() override
impeller::DirectionalGaussianBlurFilterContents::GetFilterCoverage
std::optional< Rect > GetFilterCoverage(const FilterInput::Vector &inputs, const Entity &entity, const Matrix &effect_transform) const override
Definition: gaussian_blur_filter_contents.cc:279
rect.h
impeller::DirectionalGaussianBlurFilterContents::SetDirection
void SetDirection(Vector2 direction)
Definition: gaussian_blur_filter_contents.cc:40
impeller::TPoint< Scalar >
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:448
render_target.h
impeller::FilterInput::Vector
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:32
impeller::TPoint::Normalize
constexpr TPoint Normalize() const
Definition: point.h:201
impeller::RenderPipelineT::VertexShader
VertexShader_ VertexShader
Definition: pipeline.h:90
impeller
Definition: aiks_context.cc:10
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:103
impeller::ContentContext
Definition: content_context.h:344
impeller::TRect
Definition: rect.h:20
impeller::BlendMode::kSource
@ kSource
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:36
impeller::SamplerAddressMode::kRepeat
@ kRepeat
impeller::SamplerAddressMode::kDecal
@ kDecal
decal sampling mode is only supported on devices that pass the Capabilities.SupportsDecalSamplerAddre...