Flutter Impeller
directional_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 
6 
7 #include <cmath>
8 #include <utility>
9 
10 #include "impeller/base/strings.h"
11 #include "impeller/core/formats.h"
15 #include "impeller/geometry/rect.h"
21 
22 namespace impeller {
23 
24 // This function was calculated by observing Skia's behavior. Its blur at 500
25 // seemed to be 0.15. Since we clamp at 500 I solved the quadratic equation
26 // that puts the minima there and a f(0)=1.
28  // Limit the kernel size to 1000x1000 pixels, like Skia does.
29  Scalar clamped = std::min(sigma.sigma, 500.0f);
30  Scalar scalar = 1.0 - 3.4e-3 * clamped + 3.4e-06 * clamped * clamped;
31  return Sigma(clamped * scalar);
32 }
33 
35  default;
36 
39 
41  blur_sigma_ = sigma;
42 }
43 
45  secondary_blur_sigma_ = sigma;
46 }
47 
49  blur_direction_ = direction.Normalize();
50  if (blur_direction_.IsZero()) {
51  blur_direction_ = Vector2(0, 1);
52  }
53 }
54 
56  blur_style_ = blur_style;
57 }
58 
60  Entity::TileMode tile_mode) {
61  tile_mode_ = tile_mode;
62 }
63 
65  bool is_second_pass) {
66  is_second_pass_ = is_second_pass;
67 }
68 
69 std::optional<Entity> DirectionalGaussianBlurFilterContents::RenderFilter(
70  const FilterInput::Vector& inputs,
71  const ContentContext& renderer,
72  const Entity& entity,
73  const Matrix& effect_transform,
74  const Rect& coverage,
75  const std::optional<Rect>& coverage_hint) const {
78 
79  //----------------------------------------------------------------------------
80  /// Handle inputs.
81  ///
82 
83  if (inputs.empty()) {
84  return std::nullopt;
85  }
86 
87  auto radius = Radius{ScaleSigma(blur_sigma_)}.radius;
88 
89  auto transform = entity.GetTransform() * effect_transform.Basis();
90  auto transformed_blur_radius =
91  transform.TransformDirection(blur_direction_ * radius);
92 
93  auto transformed_blur_radius_length = transformed_blur_radius.GetLength();
94 
95  // Input 0 snapshot.
96 
97  std::optional<Rect> expanded_coverage_hint;
98  if (coverage_hint.has_value()) {
99  auto r =
100  Point(transformed_blur_radius_length, transformed_blur_radius_length)
101  .Abs();
102  expanded_coverage_hint =
103  is_second_pass_ ? coverage_hint : coverage_hint->Expand(r);
104  }
105  auto input_snapshot = inputs[0]->GetSnapshot("GaussianBlur", renderer, entity,
106  expanded_coverage_hint);
107  if (!input_snapshot.has_value()) {
108  return std::nullopt;
109  }
110 
111  if (blur_sigma_.sigma < kEhCloseEnough) {
112  return Entity::FromSnapshot(input_snapshot.value(), entity.GetBlendMode(),
113  entity.GetClipDepth()); // No blur to render.
114  }
115 
116  // If the radius length is < .5, the shader will take at most 1 sample,
117  // resulting in no blur.
118  if (transformed_blur_radius_length < .5) {
119  return Entity::FromSnapshot(input_snapshot.value(), entity.GetBlendMode(),
120  entity.GetClipDepth()); // No blur to render.
121  }
122 
123  // A matrix that rotates the snapshot space such that the blur direction is
124  // +X.
125  auto texture_rotate = Matrix::MakeRotationZ(
126  transformed_blur_radius.Normalize().AngleTo({1, 0}));
127 
128  // Converts local pass space to screen space. This is just the snapshot space
129  // rotated such that the blur direction is +X.
130  auto pass_transform = texture_rotate * input_snapshot->transform;
131 
132  // The pass texture coverage, but rotated such that the blur is in the +X
133  // direction, and expanded to include the blur radius. This is used for UV
134  // projection and as a source for the pass size. Note that it doesn't matter
135  // which direction the space is rotated in when grabbing the pass size.
136  auto pass_texture_rect = Rect::MakeSize(input_snapshot->texture->GetSize())
137  .TransformBounds(pass_transform)
138  .Expand(transformed_blur_radius_length, 0);
139 
140  // UV mapping.
141 
142  auto pass_uv_project = [&texture_rotate,
143  &pass_texture_rect](Snapshot& input) {
144  auto uv_matrix = Matrix::MakeScale(1 / Vector2(input.texture->GetSize())) *
145  (texture_rotate * input.transform).Invert();
146  return pass_texture_rect.GetTransformedPoints(uv_matrix);
147  };
148 
149  auto input_uvs = pass_uv_project(input_snapshot.value());
150 
151  //----------------------------------------------------------------------------
152  /// Render to texture.
153  ///
154 
155  ContentContext::SubpassCallback subpass_callback = [&](const ContentContext&
156  renderer,
157  RenderPass& pass) {
158  auto& host_buffer = pass.GetTransientsBuffer();
159 
160  VertexBufferBuilder<VS::PerVertexData> vtx_builder;
161  vtx_builder.AddVertices({
162  {Point(0, 0), input_uvs[0]},
163  {Point(1, 0), input_uvs[1]},
164  {Point(0, 1), input_uvs[2]},
165  {Point(1, 1), input_uvs[3]},
166  });
167 
168  VS::FrameInfo frame_info;
169  frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
170  frame_info.texture_sampler_y_coord_scale =
171  input_snapshot->texture->GetYCoordScale();
172 
173  FS::BlurInfo frag_info;
174  auto r = Radius{transformed_blur_radius_length};
175  frag_info.blur_sigma = Sigma{r}.sigma;
176  frag_info.blur_radius = std::round(r.radius);
177  frag_info.step_size = 2.0;
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().GetSize());
183 
184  Command cmd;
185  DEBUG_COMMAND_INFO(cmd, SPrintF("Gaussian Blur Filter (Radius=%.2f)",
186  transformed_blur_radius_length));
187  cmd.BindVertices(vtx_builder.CreateVertexBuffer(host_buffer));
188 
189  auto options = OptionsFromPass(pass);
190  options.primitive_type = PrimitiveType::kTriangleStrip;
191  options.blend_mode = BlendMode::kSource;
192  auto input_descriptor = input_snapshot->sampler_descriptor;
193  switch (tile_mode_) {
195  if (renderer.GetDeviceCapabilities()
196  .SupportsDecalSamplerAddressMode()) {
197  input_descriptor.width_address_mode = SamplerAddressMode::kDecal;
198  input_descriptor.height_address_mode = SamplerAddressMode::kDecal;
199  }
200  break;
202  input_descriptor.width_address_mode = SamplerAddressMode::kClampToEdge;
203  input_descriptor.height_address_mode = SamplerAddressMode::kClampToEdge;
204  break;
206  input_descriptor.width_address_mode = SamplerAddressMode::kMirror;
207  input_descriptor.height_address_mode = SamplerAddressMode::kMirror;
208  break;
210  input_descriptor.width_address_mode = SamplerAddressMode::kRepeat;
211  input_descriptor.height_address_mode = SamplerAddressMode::kRepeat;
212  break;
213  }
214  input_descriptor.mag_filter = MinMagFilter::kLinear;
215  input_descriptor.min_filter = MinMagFilter::kLinear;
216 
217  bool has_decal_specialization =
218  tile_mode_ == Entity::TileMode::kDecal &&
219  !renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode();
220 
221  if (has_decal_specialization) {
222  cmd.pipeline = renderer.GetGaussianBlurDecalPipeline(options);
223  } else {
224  cmd.pipeline = renderer.GetGaussianBlurPipeline(options);
225  }
226 
227  FS::BindTextureSampler(
228  cmd, input_snapshot->texture,
229  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
230  input_descriptor));
231  VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
232  FS::BindBlurInfo(cmd, host_buffer.EmplaceUniform(frag_info));
233 
234  return pass.AddCommand(std::move(cmd));
235  };
236 
237  Vector2 scale;
238  auto scale_curve = [](Scalar radius) {
239  constexpr Scalar decay = 4.0; // Larger is more gradual.
240  constexpr Scalar limit = 0.95; // The maximum percentage of the scaledown.
241  const Scalar curve =
242  std::min(1.0, decay / (std::max(1.0f, radius) + decay - 1.0));
243  return (curve - 1) * limit + 1;
244  };
245  {
246  scale.x = scale_curve(transformed_blur_radius_length);
247 
248  Scalar y_radius = std::abs(pass_transform.GetDirectionScale(Vector2(
249  0, !is_second_pass_ ? 1 : Radius{secondary_blur_sigma_}.radius)));
250  scale.y = scale_curve(y_radius);
251  }
252 
253  Vector2 scaled_size = pass_texture_rect.GetSize() * scale;
254  ISize floored_size = ISize(scaled_size.x, scaled_size.y);
255 
256  fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
257  "Directional Gaussian Blur Filter", floored_size, subpass_callback);
258 
259  if (!render_target.ok()) {
260  return std::nullopt;
261  }
262 
263  SamplerDescriptor sampler_desc;
264  sampler_desc.min_filter = MinMagFilter::kLinear;
265  sampler_desc.mag_filter = MinMagFilter::kLinear;
266  sampler_desc.width_address_mode = SamplerAddressMode::kClampToEdge;
267  sampler_desc.width_address_mode = SamplerAddressMode::kClampToEdge;
268 
269  return Entity::FromSnapshot(
270  Snapshot{
271  .texture = render_target.value().GetRenderTargetTexture(),
272  .transform =
273  texture_rotate.Invert() *
274  Matrix::MakeTranslation(pass_texture_rect.GetOrigin()) *
275  Matrix::MakeScale((1 / scale) * (scaled_size / floored_size)),
276  .sampler_descriptor = sampler_desc,
277  .opacity = input_snapshot->opacity},
278  entity.GetBlendMode(), entity.GetClipDepth());
279 }
280 
281 std::optional<Rect>
283  const Matrix& effect_transform,
284  const Rect& output_limit) const {
285  auto transform = effect_transform.Basis();
286  auto transformed_blur_vector =
287  transform.TransformDirection(blur_direction_ * Radius{blur_sigma_}.radius)
288  .Abs();
289  return output_limit.Expand(transformed_blur_vector);
290 }
291 
293  const FilterInput::Vector& inputs,
294  const Entity& entity,
295  const Matrix& effect_transform) const {
296  if (inputs.empty()) {
297  return std::nullopt;
298  }
299 
300  auto coverage = inputs[0]->GetCoverage(entity);
301  if (!coverage.has_value()) {
302  return std::nullopt;
303  }
304 
305  auto transform = inputs[0]->GetTransform(entity) * effect_transform.Basis();
306  auto transformed_blur_vector =
307  transform
308  .TransformDirection(blur_direction_ *
309  Radius{ScaleSigma(blur_sigma_)}.radius)
310  .Abs();
311  return coverage->Expand(transformed_blur_vector);
312 }
313 
314 } // namespace impeller
impeller::Sigma::sigma
Scalar sigma
Definition: sigma.h:33
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:28
impeller::DirectionalGaussianBlurFilterContents::SetIsSecondPass
void SetIsSecondPass(bool is_second_pass)
Definition: directional_gaussian_blur_filter_contents.cc:64
impeller::DirectionalGaussianBlurFilterContents::SetBlurStyle
void SetBlurStyle(BlurStyle blur_style)
Definition: directional_gaussian_blur_filter_contents.cc:55
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::Entity::GetTransform
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:49
impeller::DirectionalGaussianBlurFilterContents::SetSecondarySigma
void SetSecondarySigma(Sigma sigma)
Definition: directional_gaussian_blur_filter_contents.cc:44
impeller::FilterContents::BlurStyle
BlurStyle
Definition: filter_contents.h:23
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:56
impeller::Entity::TileMode::kDecal
@ kDecal
impeller::BlendMode::kSource
@ kSource
impeller::TRect::TransformBounds
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:264
impeller::ScaleSigma
Sigma ScaleSigma(Sigma sigma)
Definition: directional_gaussian_blur_filter_contents.cc:27
formats.h
impeller::Vector2
Point Vector2
Definition: point.h:312
impeller::RenderPipelineT::FragmentShader
FragmentShader_ FragmentShader
Definition: pipeline.h:93
impeller::SamplerAddressMode::kClampToEdge
@ kClampToEdge
impeller::Entity::TileMode::kRepeat
@ kRepeat
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Entity::TileMode::kMirror
@ kMirror
impeller::DirectionalGaussianBlurFilterContents::GetFilterSourceCoverage
std::optional< Rect > GetFilterSourceCoverage(const Matrix &effect_transform, const Rect &output_limit) const override
Internal utility method for |GetSourceCoverage| that computes the inverse effect of this transform on...
Definition: directional_gaussian_blur_filter_contents.cc:282
impeller::TPoint::IsZero
constexpr bool IsZero() const
Definition: point.h:229
impeller::Entity
Definition: entity.h:21
impeller::Matrix::Basis
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition: matrix.h:224
impeller::PrimitiveType::kTriangleStrip
@ kTriangleStrip
impeller::Point
TPoint< Scalar > Point
Definition: point.h:308
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:48
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:32
strings.h
impeller::Entity::TileMode
TileMode
Definition: entity.h:40
impeller::Entity::GetBlendMode
BlendMode GetBlendMode() const
Definition: entity.cc:105
impeller::DirectionalGaussianBlurFilterContents::SetTileMode
void SetTileMode(Entity::TileMode tile_mode)
Definition: directional_gaussian_blur_filter_contents.cc:59
scalar.h
impeller::ContentContext::SubpassCallback
std::function< bool(const ContentContext &, RenderPass &)> SubpassCallback
Definition: content_context.h:708
impeller::Matrix::TransformDirection
constexpr Vector4 TransformDirection(const Vector4 &v) const
Definition: matrix.h:433
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:138
impeller::Entity::GetClipDepth
uint32_t GetClipDepth() const
Definition: entity.cc:93
sampler_descriptor.h
command_buffer.h
impeller::TPoint::Abs
constexpr TPoint Abs() const
Definition: point.h:211
impeller::DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents
DirectionalGaussianBlurFilterContents()
content_context.h
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:209
impeller::DirectionalGaussianBlurFilterContents::SetSigma
void SetSigma(Sigma sigma)
Set sigma (stddev) used for 'direction_'.
Definition: directional_gaussian_blur_filter_contents.cc:40
impeller::SamplerAddressMode::kMirror
@ kMirror
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:44
directional_gaussian_blur_filter_contents.h
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
Internal utility method for |GetLocalCoverage| that computes the output coverage of this filter acros...
Definition: directional_gaussian_blur_filter_contents.cc:292
rect.h
impeller::DirectionalGaussianBlurFilterContents::SetDirection
void SetDirection(Vector2 direction)
Definition: directional_gaussian_blur_filter_contents.cc:48
impeller::TPoint< Scalar >
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:459
render_target.h
impeller::FilterInput::Vector
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:33
impeller::Entity::FromSnapshot
static std::optional< Entity > FromSnapshot(const std::optional< Snapshot > &snapshot, BlendMode blend_mode=BlendMode::kSourceOver, uint32_t clip_depth=0)
Create an entity that can be used to render a given snapshot.
Definition: entity.cc:21
impeller::TPoint::Normalize
constexpr TPoint Normalize() const
Definition: point.h:203
impeller::RenderPipelineT::VertexShader
VertexShader_ VertexShader
Definition: pipeline.h:92
impeller
Definition: aiks_context.cc:10
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::ContentContext
Definition: content_context.h:332
impeller::TRect
Definition: rect.h:22
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::TRect::Expand
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition: rect.h:373
vertex_buffer_builder.h
impeller::SamplerAddressMode::kRepeat
@ kRepeat
impeller::SamplerAddressMode::kDecal
@ kDecal
decal sampling mode is only supported on devices that pass the Capabilities.SupportsDecalSamplerAddre...