Flutter Impeller
solid_rrect_like_blur_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 #include <optional>
7 
14 
15 namespace impeller {
16 
17 namespace {
18 // Generous padding to make sure blurs with large sigmas are fully visible. Used
19 // to expand the geometry around the rrect. Larger sigmas have more subtle
20 // gradients so they need larger padding to avoid hard cutoffs. Sigma is
21 // maximized to 3.5 since that should cover 99.95% of all samples. 3.0 should
22 // cover 99.7% but that was seen to be not enough for large sigmas.
23 Scalar PadForSigma(Scalar sigma) {
24  Scalar scalar = std::min((1.0f / 47.6f) * sigma + 2.5f, 3.5f);
25  return sigma * scalar;
26 }
27 } // namespace
28 
30 
32 
34  rect_ = rect;
35  corner_radius_ = corner_radius;
36 }
37 
39  sigma_ = sigma;
40 }
41 
43  color_ = color.Premultiply();
44 }
45 
47  return color_;
48 }
49 
50 static Point eccentricity(Point v, double sInverse) {
51  Point vOverS = v * sInverse * 0.5;
52  Point vOverS_squared = -(vOverS * vOverS);
53  return {std::exp(vOverS_squared.x), std::exp(vOverS_squared.y)};
54 }
55 
56 static Scalar kTwoOverSqrtPi = 2.0 / std::sqrt(kPi);
57 
58 // use crate::math::compute_erf7;
60  x *= kTwoOverSqrtPi;
61  float xx = x * x;
62  x = x + (0.24295 + (0.03395 + 0.0104 * xx) * xx) * (x * xx);
63  return x / sqrt(1.0 + x * x);
64 }
65 
66 static Point NegPos(Scalar v) {
67  return {std::min(v, 0.0f), std::max(v, 0.0f)};
68 }
69 
70 bool SolidRRectLikeBlurContents::PopulateFragContext(PassContext& pass_context,
71  Scalar blurSigma,
72  Point center,
73  Point rSize,
74  Scalar radius) {
75  Scalar sigma = std::max(blurSigma * kSqrt2, 1.f);
76 
77  pass_context.center = rSize * 0.5f;
78  pass_context.minEdge = std::min(rSize.x, rSize.y);
79  double rMax = 0.5 * pass_context.minEdge;
80  double r0 = std::min(std::hypot(radius, sigma * 1.15), rMax);
81  pass_context.r1 = std::min(std::hypot(radius, sigma * 2.0), rMax);
82 
83  pass_context.exponent = 2.0 * pass_context.r1 / r0;
84 
85  pass_context.sInv = 1.0 / sigma;
86 
87  // Pull in long end (make less eccentric).
88  Point eccentricV = eccentricity(rSize, pass_context.sInv);
89  double delta = 1.25 * sigma * (eccentricV.x - eccentricV.y);
90  rSize += NegPos(delta);
91 
92  pass_context.adjust = rSize * 0.5 - pass_context.r1;
93  pass_context.exponentInv = 1.0 / pass_context.exponent;
94  pass_context.scale =
95  0.5 * computeErf7(pass_context.sInv * 0.5 *
96  (std::max(rSize.x, rSize.y) - 0.5 * radius));
97 
98  return pass_context.center.IsFinite() && //
99  pass_context.adjust.IsFinite() && //
100  std::isfinite(pass_context.minEdge) && //
101  std::isfinite(pass_context.r1) && //
102  std::isfinite(pass_context.exponent) && //
103  std::isfinite(pass_context.sInv) && //
104  std::isfinite(pass_context.exponentInv) && //
105  std::isfinite(pass_context.scale);
106 }
107 
109  const Entity& entity) const {
110  Scalar radius = PadForSigma(sigma_.sigma);
111 
112  return rect_.Expand(radius).TransformBounds(entity.GetTransform());
113 }
114 
116  const Entity& entity,
117  RenderPass& pass) const {
118  using VS = RrectLikeBlurVertexShader;
119 
120  Matrix basis_invert = entity.GetTransform().Basis().Invert();
121  Vector2 max_sigmas =
122  Vector2((basis_invert * Vector2(500.f, 0.f)).GetLength(),
123  (basis_invert * Vector2(0.f, 500.f)).GetLength());
124  Scalar max_sigma = std::min(max_sigmas.x, max_sigmas.y);
125  // Clamp the max kernel width/height to 1000 (@ 2x) to limit the extent
126  // of the blur and to kEhCloseEnough to prevent NaN calculations
127  // trying to evaluate a Gaussian distribution with a sigma of 0.
128  auto blur_sigma = std::clamp(sigma_.sigma, kEhCloseEnough, max_sigma);
129  // Increase quality by making the radius a bit bigger than the typical
130  // sigma->radius conversion we use for slower blurs.
131  Scalar blur_radius = PadForSigma(blur_sigma);
132  Rect positive_rect = rect_.GetPositive();
133  Scalar left = -blur_radius;
134  Scalar top = -blur_radius;
135  Scalar right = positive_rect.GetWidth() + blur_radius;
136  Scalar bottom = positive_rect.GetHeight() + blur_radius;
137 
138  ContentContextOptions opts = OptionsFromPassAndEntity(pass, entity);
140  Color color = color_;
141  if (entity.GetBlendMode() == BlendMode::kClear) {
142  opts.is_for_rrect_blur_clear = true;
143  color = Color::White();
144  }
145 
146  std::array<VS::PerVertexData, 4> vertices = {
147  VS::PerVertexData{Point(left, top)},
148  VS::PerVertexData{Point(right, top)},
149  VS::PerVertexData{Point(left, bottom)},
150  VS::PerVertexData{Point(right, bottom)},
151  };
152 
153  PassContext pass_context = {
154  .opts = opts,
155  };
156 
157  Scalar radius = std::min(std::clamp(corner_radius_, kEhCloseEnough,
158  positive_rect.GetWidth() * 0.5f),
159  std::clamp(corner_radius_, kEhCloseEnough,
160  positive_rect.GetHeight() * 0.5f));
161  if (!PopulateFragContext(pass_context, blur_sigma, positive_rect.GetCenter(),
162  Point(positive_rect.GetSize()), radius)) {
163  return true;
164  }
165 
166  if (!SetPassInfo(pass, renderer, pass_context)) {
167  return true;
168  }
169 
170  VS::FrameInfo frame_info;
171  frame_info.mvp = Entity::GetShaderTransform(
172  entity.GetShaderClipDepth(), pass,
173  entity.GetTransform() *
174  Matrix::MakeTranslation(positive_rect.GetOrigin()));
175 
176  auto& host_buffer = renderer.GetTransientsBuffer();
177  pass.SetVertexBuffer(CreateVertexBuffer(vertices, host_buffer));
178  VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
179 
180  if (!pass.Draw().ok()) {
181  return false;
182  }
183 
184  return true;
185 }
186 
188  const ColorFilterProc& color_filter_proc) {
189  color_ = color_filter_proc(color_);
190  return true;
191 }
192 
194  return {a.x, a.y, b.x, b.y};
195 }
196 
197 } // namespace impeller
HostBuffer & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
std::function< Color(Color)> ColorFilterProc
Definition: contents.h:35
Matrix GetShaderTransform(const RenderPass &pass) const
Definition: entity.cc:48
BlendMode GetBlendMode() const
Definition: entity.cc:101
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:44
float GetShaderClipDepth() const
Definition: entity.cc:88
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:30
virtual bool SetVertexBuffer(VertexBuffer buffer)
Specify the vertex and index buffer to use for this command.
Definition: render_pass.cc:127
virtual fml::Status Draw()
Record the currently pending command.
Definition: render_pass.cc:208
void SetShape(Rect rect, Scalar corner_radius)
std::optional< Rect > GetCoverage(const Entity &entity) const override
Get the area of the render pass that will be affected when this contents is rendered.
virtual bool SetPassInfo(RenderPass &pass, const ContentContext &renderer, PassContext &pass_context) const =0
bool Render(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
bool ApplyColorFilter(const ColorFilterProc &color_filter_proc) override
If possible, applies a color filter to this contents inputs on the CPU.
static Vector4 Concat(Vector2 &a, Vector2 &b)
int32_t x
Vector2 blur_radius
Blur radius in source pixels based on scaled_sigma.
static Scalar computeErf7(Scalar x)
Point Vector2
Definition: point.h:331
constexpr float kPi
Definition: constants.h:26
float Scalar
Definition: scalar.h:19
constexpr float kEhCloseEnough
Definition: constants.h:57
static Point NegPos(Scalar v)
TPoint< Scalar > Point
Definition: point.h:327
VertexBuffer CreateVertexBuffer(std::array< VertexType, size > input, HostBuffer &host_buffer)
Create an index-less vertex buffer from a fixed size array.
static Point eccentricity(Point v, double sInverse)
static Scalar kTwoOverSqrtPi
constexpr float kSqrt2
Definition: constants.h:47
LinePipeline::VertexShader VS
ContentContextOptions OptionsFromPassAndEntity(const RenderPass &pass, const Entity &entity)
Definition: contents.cc:34
static constexpr Color White()
Definition: color.h:264
constexpr Color Premultiply() const
Definition: color.h:212
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition: matrix.h:239
Matrix Invert() const
Definition: matrix.cc:97
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
Scalar sigma
Definition: sigma.h:33
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:622
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:476
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition: rect.h:351
constexpr TPoint< Type > GetOrigin() const
Returns the upper left corner of the rectangle as specified by the left/top or x/y values when it was...
Definition: rect.h:324
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition: rect.h:331
constexpr TRect GetPositive() const
Get a version of this rectangle that has a non-negative size.
Definition: rect.h:402
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition: rect.h:345
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition: rect.h:386