Flutter Impeller
gaussian_blur_filter_contents.h
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 #ifndef FLUTTER_IMPELLER_ENTITY_CONTENTS_FILTERS_GAUSSIAN_BLUR_FILTER_CONTENTS_H_
6 #define FLUTTER_IMPELLER_ENTITY_CONTENTS_FILTERS_GAUSSIAN_BLUR_FILTER_CONTENTS_H_
7 
8 #include <optional>
13 
14 namespace impeller {
15 
16 // Comes from gaussian.frag.
17 static constexpr int32_t kGaussianBlurMaxKernelSize = 50;
18 
19 static_assert(sizeof(GaussianBlurPipeline::FragmentShader::KernelSamples) ==
21 
26  int step_size;
28 };
29 
30 struct KernelSample {
32  float coefficient;
33 };
34 
35 /// A larger mirror of GaussianBlurPipeline::FragmentShader::KernelSamples.
36 ///
37 /// This is a mirror of GaussianBlurPipeline::FragmentShader::KernelSamples that
38 /// can hold 2x the max kernel size since it will get reduced with the lerp
39 /// hack.
40 struct KernelSamples {
41  static constexpr int kMaxKernelSize = kGaussianBlurMaxKernelSize * 2;
44 };
45 
47  GaussianBlurPipeline::FragmentShader::KernelSamples kernel_samples;
48  int32_t sample_count;
49 };
50 
52 
53 /// This will shrink the size of a kernel by roughly half by sampling between
54 /// samples and relying on linear interpolation between the samples.
56 
57 /// Performs a bidirectional Gaussian blur.
58 //
59 // ## Implementation notes
60 //
61 // The blur is implemented as a three-pass process:
62 // 1. A downsampling pass. This is used for performance optimization on large
63 // blurs.
64 // 2. A Y-direction blur pass (in canvas coordinates).
65 // 3. An X-direction blur pass (in canvas coordinates).
66 //
67 // ### Lerp Hack
68 //
69 // The blur passes use a "lerp hack" to optimize the number of texture
70 // samples. This technique takes advantage of the hardware's free linear
71 // interpolation during texture sampling. It allows for the use of a kernel
72 // that is half the size of what would be mathematically required, achieving an
73 // equivalent effect as long as the sampling points are correctly aligned.
74 //
75 // ### Bounded vs. Unbounded Blur
76 //
77 // The behavior of the blur changes depending on whether the `bounds` parameter
78 // is set.
79 //
80 // - Unbounded (standard) blur: The blur kernel samples all pixels, and
81 // the `tile_mode` determines how to handle edges.
82 //
83 // - Bounded blur: This mode is used to implement iOS-style blurs where
84 // the blur effect is constrained to a specific rectangle. The process is
85 // modified as follows:
86 // - During the downsampling pass, pixels outside the `bounds` are treated
87 // as transparent black.
88 // - The two blur passes proceed normally, but thanks to the lerp hack and
89 // linear interpolation, when a sample falls between an in-bounds pixel
90 // and an out-of-bounds (transparent black) pixel, the out-of-bounds
91 // pixel contributes nothing to the result.
92 // - The final pass includes an opaque unpremultiply step to counteract the
93 // varying alpha introduced by the weights.
95  public:
96  explicit GaussianBlurFilterContents(Scalar sigma_x,
97  Scalar sigma_y,
98  Entity::TileMode tile_mode,
99  std::optional<Rect> bounds,
100  BlurStyle mask_blur_style,
101  const Geometry* mask_geometry = nullptr);
102 
103  std::optional<Rect> GetBounds() const { return bounds_; }
104  Scalar GetSigmaX() const { return sigma_.x; }
105  Scalar GetSigmaY() const { return sigma_.y; }
106 
107  // |FilterContents|
108  std::optional<Rect> GetFilterSourceCoverage(
109  const Matrix& effect_transform,
110  const Rect& output_limit) const override;
111 
112  // |FilterContents|
113  std::optional<Rect> GetFilterCoverage(
114  const FilterInput::Vector& inputs,
115  const Entity& entity,
116  const Matrix& effect_transform) const override;
117 
118  /// Given a sigma (standard deviation) calculate the blur radius (1/2 the
119  /// kernel size).
120  static Scalar CalculateBlurRadius(Scalar sigma);
121 
122  /// Calculate the UV coordinates for rendering the filter_input.
123  /// @param filter_input The FilterInput that should be rendered.
124  /// @param entity The associated entity for the filter_input.
125  /// @param source_rect The rect in source coordinates to convert to uvs.
126  /// @param texture_size The rect to convert in source coordinates.
127  static Quad CalculateUVs(const std::shared_ptr<FilterInput>& filter_input,
128  const Entity& entity,
129  const Rect& source_rect,
130  const ISize& texture_size);
131 
132  /// Calculate the scale factor for the downsample pass given a sigma value.
133  ///
134  /// Visible for testing.
135  static Scalar CalculateScale(Scalar sigma);
136 
137  /// Scales down the sigma value to match Skia's behavior.
138  ///
139  /// effective_blur_radius = CalculateBlurRadius(ScaleSigma(sigma_));
140  ///
141  /// This function was calculated by observing Skia's behavior. Its blur at
142  /// 500 seemed to be 0.15. Since we clamp at 500 I solved the quadratic
143  /// equation that puts the minima there and a f(0)=1.
144  static Scalar ScaleSigma(Scalar sigma);
145 
146  private:
147  // |FilterContents|
148  std::optional<Entity> RenderFilter(
149  const FilterInput::Vector& input_textures,
150  const ContentContext& renderer,
151  const Entity& entity,
152  const Matrix& effect_transform,
153  const Rect& coverage,
154  const std::optional<Rect>& coverage_hint) const override;
155 
156  const Vector2 sigma_ = Vector2(0.0, 0.0);
157  const Entity::TileMode tile_mode_;
158  const std::optional<Rect> bounds_ = std::nullopt;
159  const BlurStyle mask_blur_style_;
160  const Geometry* mask_geometry_ = nullptr;
161 };
162 
163 } // namespace impeller
164 
165 #endif // FLUTTER_IMPELLER_ENTITY_CONTENTS_FILTERS_GAUSSIAN_BLUR_FILTER_CONTENTS_H_
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:33
Performs a bidirectional Gaussian blur.
GaussianBlurFilterContents(Scalar sigma_x, Scalar sigma_y, Entity::TileMode tile_mode, std::optional< Rect > bounds, BlurStyle mask_blur_style, const Geometry *mask_geometry=nullptr)
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...
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...
static Quad CalculateUVs(const std::shared_ptr< FilterInput > &filter_input, const Entity &entity, const Rect &source_rect, const ISize &texture_size)
LerpHackResult LerpHackKernelSamples(const KernelSamples &parameters)
Point Vector2
Definition: point.h:430
static constexpr int32_t kGaussianBlurMaxKernelSize
float Scalar
Definition: scalar.h:19
KernelSamples GenerateBlurInfo(BlurParameters parameters)
std::array< Point, 4 > Quad
Definition: point.h:431
KernelSample samples[kMaxKernelSize]
GaussianBlurPipeline::FragmentShader::KernelSamples kernel_samples
A 4x4 matrix using column-major storage.
Definition: matrix.h:37