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 
6 
7 #include <cmath>
8 #include <utility>
9 #include <valarray>
10 
11 #include "impeller/base/strings.h"
13 #include "impeller/core/formats.h"
17 #include "impeller/geometry/rect.h"
23 
24 namespace impeller {
25 
27  default;
28 
31 
33  blur_sigma_ = sigma;
34 }
35 
37  secondary_blur_sigma_ = sigma;
38 }
39 
41  blur_direction_ = direction.Normalize();
42  if (blur_direction_.IsZero()) {
43  blur_direction_ = Vector2(0, 1);
44  }
45 }
46 
48  blur_style_ = blur_style;
49 }
50 
52  Entity::TileMode tile_mode) {
53  tile_mode_ = tile_mode;
54 }
55 
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 {
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_) {
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;
201  input_descriptor.width_address_mode = SamplerAddressMode::kClampToEdge;
202  input_descriptor.height_address_mode = SamplerAddressMode::kClampToEdge;
203  break;
205  input_descriptor.width_address_mode = SamplerAddressMode::kMirror;
206  input_descriptor.height_address_mode = SamplerAddressMode::kMirror;
207  break;
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 
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...