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  // Clamp the max kernel width/height to 1000 to limit the extent
132  // of the blur and to kEhCloseEnough to prevent NaN calculations
133  // trying to evaluate a Guassian distribution with a sigma of 0.
134  Scalar blur_sigma = std::clamp(sigma_.sigma, kEhCloseEnough, 250.0f);
135  // Increase quality by making the radius a bit bigger than the typical
136  // sigma->radius conversion we use for slower blurs.
137  Scalar blur_radius = PadForSigma(blur_sigma);
138  Rect positive_rect = rect_->GetPositive();
139  Scalar left = -blur_radius;
140  Scalar top = -blur_radius;
141  Scalar right = positive_rect.GetWidth() + blur_radius;
142  Scalar bottom = positive_rect.GetHeight() + blur_radius;
143 
144  std::array<VS::PerVertexData, 4> vertices = {
145  VS::PerVertexData{Point(left, top)},
146  VS::PerVertexData{Point(right, top)},
147  VS::PerVertexData{Point(left, bottom)},
148  VS::PerVertexData{Point(right, bottom)},
149  };
150 
151  ContentContextOptions opts = OptionsFromPassAndEntity(pass, entity);
153  Color color = color_;
154  if (entity.GetBlendMode() == BlendMode::kClear) {
155  opts.is_for_rrect_blur_clear = true;
156  color = Color::White();
157  }
158 
159  VS::FrameInfo frame_info;
160  frame_info.mvp = Entity::GetShaderTransform(
161  entity.GetShaderClipDepth(), pass,
162  entity.GetTransform() *
163  Matrix::MakeTranslation(positive_rect.GetOrigin()));
164 
165  FS::FragInfo frag_info;
166  frag_info.color = color;
167  Scalar radius = std::min(std::clamp(corner_radii_.width, kEhCloseEnough,
168  positive_rect.GetWidth() * 0.5f),
169  std::clamp(corner_radii_.height, kEhCloseEnough,
170  positive_rect.GetHeight() * 0.5f));
171  if (!SetupFragInfo(frag_info, blur_sigma, positive_rect.GetCenter(),
172  Point(positive_rect.GetSize()), radius)) {
173  return true;
174  }
175 
176  auto& host_buffer = renderer.GetTransientsBuffer();
177  pass.SetCommandLabel("RRect Shadow");
178  pass.SetPipeline(renderer.GetRRectBlurPipeline(opts));
179  pass.SetVertexBuffer(CreateVertexBuffer(vertices, host_buffer));
180 
181  VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
182  FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
183 
184  if (!pass.Draw().ok()) {
185  return false;
186  }
187 
188  return true;
189 }
190 
192  const ColorFilterProc& color_filter_proc) {
193  color_ = color_filter_proc(color_);
194  return true;
195 }
196 
197 } // 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:114
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)
constexpr float kPi
Definition: constants.h:26
float Scalar
Definition: scalar.h:18
constexpr float kEhCloseEnough
Definition: constants.h:56
static Point NegPos(Scalar v)
GlyphAtlasPipeline::VertexShader VS
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 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
GlyphAtlasPipeline::FragmentShader FS
ContentContextOptions OptionsFromPassAndEntity(const RenderPass &pass, const Entity &entity)
Definition: contents.cc:34
static constexpr Color White()
Definition: color.h:263
constexpr Color Premultiply() const
Definition: color.h:211
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
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