Flutter Impeller
solid_rrect_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 
33 void SolidRRectBlurContents::SetRRect(std::optional<Rect> rect,
34  Size corner_radii) {
35  rect_ = rect;
36  corner_radii_ = corner_radii;
37 }
38 
40  sigma_ = sigma;
41 }
42 
44  color_ = color.Premultiply();
45 }
46 
48  return color_;
49 }
50 
51 static Point eccentricity(Point v, double sInverse) {
52  Point vOverS = v * sInverse * 0.5;
53  Point vOverS_squared = -(vOverS * vOverS);
54  return {std::exp(vOverS_squared.x), std::exp(vOverS_squared.y)};
55 }
56 
57 static Scalar kTwoOverSqrtPi = 2.0 / std::sqrt(kPi);
58 
59 // use crate::math::compute_erf7;
61  x *= kTwoOverSqrtPi;
62  float xx = x * x;
63  x = x + (0.24295 + (0.03395 + 0.0104 * xx) * xx) * (x * xx);
64  return x / sqrt(1.0 + x * x);
65 }
66 
67 static Point NegPos(Scalar v) {
68  return {std::min(v, 0.0f), std::max(v, 0.0f)};
69 }
70 
71 static bool SetupFragInfo(
72  RRectBlurPipeline::FragmentShader::FragInfo& frag_info,
73  Scalar blurSigma,
74  Point center,
75  Point rSize,
76  Scalar radius) {
77  Scalar sigma = std::max(blurSigma * kSqrt2, 1.f);
78 
79  frag_info.center = rSize * 0.5f;
80  frag_info.minEdge = std::min(rSize.x, rSize.y);
81  double rMax = 0.5 * frag_info.minEdge;
82  double r0 = std::min(std::hypot(radius, sigma * 1.15), rMax);
83  frag_info.r1 = std::min(std::hypot(radius, sigma * 2.0), rMax);
84 
85  frag_info.exponent = 2.0 * frag_info.r1 / r0;
86 
87  frag_info.sInv = 1.0 / sigma;
88 
89  // Pull in long end (make less eccentric).
90  Point eccentricV = eccentricity(rSize, frag_info.sInv);
91  double delta = 1.25 * sigma * (eccentricV.x - eccentricV.y);
92  rSize += NegPos(delta);
93 
94  frag_info.adjust = rSize * 0.5 - frag_info.r1;
95  frag_info.exponentInv = 1.0 / frag_info.exponent;
96  frag_info.scale =
97  0.5 * computeErf7(frag_info.sInv * 0.5 *
98  (std::max(rSize.x, rSize.y) - 0.5 * radius));
99 
100  return frag_info.center.IsFinite() && //
101  frag_info.adjust.IsFinite() && //
102  std::isfinite(frag_info.minEdge) && //
103  std::isfinite(frag_info.r1) && //
104  std::isfinite(frag_info.exponent) && //
105  std::isfinite(frag_info.sInv) && //
106  std::isfinite(frag_info.exponentInv) && //
107  std::isfinite(frag_info.scale);
108 }
109 
111  const Entity& entity) const {
112  if (!rect_.has_value()) {
113  return std::nullopt;
114  }
115 
116  Scalar radius = PadForSigma(sigma_.sigma);
117 
118  return rect_->Expand(radius).TransformBounds(entity.GetTransform());
119 }
120 
122  const Entity& entity,
123  RenderPass& pass) const {
124  if (!rect_.has_value()) {
125  return true;
126  }
127 
130 
131  Matrix basis_invert = entity.GetTransform().Basis().Invert();
132  Vector2 max_sigmas =
133  Vector2((basis_invert * Vector2(500.f, 0.f)).GetLength(),
134  (basis_invert * Vector2(0.f, 500.f)).GetLength());
135  Scalar max_sigma = std::min(max_sigmas.x, max_sigmas.y);
136  // Clamp the max kernel width/height to 1000 (@ 2x) to limit the extent
137  // of the blur and to kEhCloseEnough to prevent NaN calculations
138  // trying to evaluate a Gaussian distribution with a sigma of 0.
139  auto blur_sigma = std::clamp(sigma_.sigma, kEhCloseEnough, max_sigma);
140  // Increase quality by making the radius a bit bigger than the typical
141  // sigma->radius conversion we use for slower blurs.
142  Scalar blur_radius = PadForSigma(blur_sigma);
143  Rect positive_rect = rect_->GetPositive();
144  Scalar left = -blur_radius;
145  Scalar top = -blur_radius;
146  Scalar right = positive_rect.GetWidth() + blur_radius;
147  Scalar bottom = positive_rect.GetHeight() + blur_radius;
148 
149  std::array<VS::PerVertexData, 4> vertices = {
150  VS::PerVertexData{Point(left, top)},
151  VS::PerVertexData{Point(right, top)},
152  VS::PerVertexData{Point(left, bottom)},
153  VS::PerVertexData{Point(right, bottom)},
154  };
155 
156  ContentContextOptions opts = OptionsFromPassAndEntity(pass, entity);
158  Color color = color_;
159  if (entity.GetBlendMode() == BlendMode::kClear) {
160  opts.is_for_rrect_blur_clear = true;
161  color = Color::White();
162  }
163 
164  VS::FrameInfo frame_info;
165  frame_info.mvp = Entity::GetShaderTransform(
166  entity.GetShaderClipDepth(), pass,
167  entity.GetTransform() *
168  Matrix::MakeTranslation(positive_rect.GetOrigin()));
169 
170  FS::FragInfo frag_info;
171  frag_info.color = color;
172  Scalar radius = std::min(std::clamp(corner_radii_.width, kEhCloseEnough,
173  positive_rect.GetWidth() * 0.5f),
174  std::clamp(corner_radii_.height, kEhCloseEnough,
175  positive_rect.GetHeight() * 0.5f));
176  if (!SetupFragInfo(frag_info, blur_sigma, positive_rect.GetCenter(),
177  Point(positive_rect.GetSize()), radius)) {
178  return true;
179  }
180 
181  auto& host_buffer = renderer.GetTransientsBuffer();
182  pass.SetCommandLabel("RRect Shadow");
183  pass.SetPipeline(renderer.GetRRectBlurPipeline(opts));
184  pass.SetVertexBuffer(CreateVertexBuffer(vertices, host_buffer));
185 
186  VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
187  FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
188 
189  if (!pass.Draw().ok()) {
190  return false;
191  }
192 
193  return true;
194 }
195 
197  const ColorFilterProc& color_filter_proc) {
198  color_ = color_filter_proc(color_);
199  return true;
200 }
201 
202 } // namespace impeller
HostBuffer & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
PipelineRef GetRRectBlurPipeline(ContentContextOptions opts) const
std::function< Color(Color)> ColorFilterProc
Definition: contents.h:35
Matrix GetShaderTransform(const RenderPass &pass) const
Get the vertex shader transform used for drawing this Entity.
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 void SetPipeline(PipelineRef pipeline)
The pipeline to use for this command.
Definition: render_pass.cc:86
virtual fml::Status Draw()
Record the currently pending command.
Definition: render_pass.cc:208
virtual void SetCommandLabel(std::string_view label)
The debugging label to use for the command.
Definition: render_pass.cc:97
FragmentShader_ FragmentShader
Definition: pipeline.h:120
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.
bool ApplyColorFilter(const ColorFilterProc &color_filter_proc) override
If possible, applies a color filter to this contents inputs on the CPU.
bool Render(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
void SetRRect(std::optional< Rect > rect, Size corner_radii={})
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:18
constexpr float kEhCloseEnough
Definition: constants.h:57
static Point NegPos(Scalar v)
TPoint< Scalar > Point
Definition: point.h:327
LinePipeline::FragmentShader FS
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 bool SetupFragInfo(RRectBlurPipeline::FragmentShader::FragInfo &frag_info, Scalar blurSigma, Point center, Point rSize, Scalar radius)
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 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 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
Type height
Definition: size.h:29
Type width
Definition: size.h:28