Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
BlurUtils.h
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef skgpu_BlurUtils_DEFINED
9#define skgpu_BlurUtils_DEFINED
10
11#include "include/core/SkSize.h"
12#include "include/core/SkSpan.h"
14
15#include <array>
16
17class SkBitmap;
18class SkRRect;
19class SkRuntimeEffect;
20struct SkV4;
21
22// TODO(b/): Many of these utilities could be lifted even into src/core as part of the backend
23// agnostic blur engine once that API exists.
24
25namespace skgpu {
26
27// The kernel width of a Gaussian blur of the given pixel radius, for when all pixels are sampled.
28constexpr int BlurKernelWidth(int radius) { return 2 * radius + 1; }
29
30// The kernel width of a Gaussian blur of the given pixel radius, that relies on HW bilinear
31// filtering to combine adjacent pixels.
32constexpr int BlurLinearKernelWidth(int radius) { return radius + 1; }
33
34// Any sigmas smaller than this are effectively an identity blur so can skip convolution at a higher
35// level. The value was chosen because it corresponds roughly to a radius of 1/10px, and because
36// 2*sigma^2 is slightly greater than SK_ScalarNearlyZero.
37constexpr bool BlurIsEffectivelyIdentity(float sigma) { return sigma <= 0.03f; }
38
39// Convert from a sigma Gaussian standard deviation to a pixel radius such that pixels outside the
40// radius would have an insignificant contribution to the final blurred value.
41inline int BlurSigmaRadius(float sigma) {
42 // sk_float_ceil2int is not constexpr
43 return BlurIsEffectivelyIdentity(sigma) ? 0 : sk_float_ceil2int(3.f * sigma);
44}
45
46// The maximum sigma that can be computed without downscaling is based on the number of uniforms and
47// texture samples the effects will make in a single pass. For 1D passes, the number of samples
48// is equal to `BlurLinearKernelWidth`; for 2D passes, it is equal to
49// `BlurKernelWidth(radiusX)*BlurKernelWidth(radiusY)`. This maps back to different maximum sigmas
50// depending on the approach used, as well as the ratio between the sigmas for the X and Y axes if
51// a 2D blur is performed.
52static constexpr int kMaxBlurSamples = 28;
53
54// TODO(b/297393474): Update max linear sigma to 9; it had been 4 when a full 1D kernel was used,
55// but never updated after the linear filtering optimization reduced the number of sample() calls
56// required. Keep it at 4 for now to better isolate performance changes due to switching to a
57// runtime effect and constant loop structure.
58static constexpr float kMaxLinearBlurSigma = 4.f; // -> radius = 27 -> linear kernel width = 28
59// NOTE: There is no defined kMaxBlurSigma for direct 2D blurs since it is entirely dependent on the
60// ratio between the two axes' sigmas, but generally it will be small on the order of a 5x5 kernel.
61
62// Return a runtime effect that applies a 2D Gaussian blur in a single pass. The returned effect can
63// perform arbitrarily sized blur kernels so long as the kernel area is less than kMaxBlurSamples.
64// An SkRuntimeEffect is returned to give flexibility for callers to convert it to an SkShader or
65// a GrFragmentProcessor. Callers are responsible for providing the uniform values (using the
66// appropriate API of the target effect type). The effect declares the following uniforms:
67//
68// uniform half4 kernel[7];
69// uniform half4 offsets[14];
70// uniform shader child;
71//
72// 'kernel' should be set to the output of Compute2DBlurKernel(). 'offsets' should be set to the
73// output of Compute2DBlurOffsets() with the same 'radii' passed to this function. 'child' should be
74// bound to whatever input is intended to be blurred, and can use nearest-neighbor sampling
75// (assuming it's an image).
76const SkRuntimeEffect* GetBlur2DEffect(const SkISize& radii);
77
78// Return a runtime effect that applies a 1D Gaussian blur, taking advantage of HW linear
79// interpolation to accumulate adjacent pixels with fewer samples. The returned effect can be used
80// for both X and Y axes by changing the 'dir' uniform value (see below). It can be used for all
81// 1D blurs such that BlurLinearKernelWidth(radius) is less than or equal to kMaxBlurSamples.
82// Like GetBlur2DEffect(), the caller is free to convert this to an SkShader or a
83// GrFragmentProcessor and is responsible for assigning uniforms with the appropriate API. Its
84// uniforms are declared as:
85//
86// uniform half4 offsetsAndKernel[14];
87// uniform half2 dir;
88// uniform int radius;
89// uniform shader child;
90//
91// 'offsetsAndKernel' should be set to the output of Compute1DBlurLinearKernel(). 'radius' should
92// match the radius passed to that function. 'dir' should either be the vector {1,0} or {0,1}
93// for X and Y axis passes, respectively. 'child' should be bound to whatever input is intended to
94// be blurred and must use linear sampling in order for the outer blur effect to function correctly.
95const SkRuntimeEffect* GetLinearBlur1DEffect(int radius);
96
97// Calculates a set of weights for a 2D Gaussian blur of the given sigma and radius. It is assumed
98// that the radius was from prior calls to BlurSigmaRadius(sigma.width()|height()) and is passed in
99// to avoid redundant calculations.
100//
101// The provided span is fully written. The kernel is stored in row-major order based on the provided
102// radius. Any remaining indices in the span are zero initialized. The span must have at least
103// BlurKernelWidth(radius.width())*BlurKernelWidth(radius.height()) elements.
104//
105// NOTE: These take spans because it can be useful to compute full kernels that are larger than what
106// is supported in the GPU effects.
107void Compute2DBlurKernel(SkSize sigma,
108 SkISize radius,
109 SkSpan<float> kernel);
110
111// A convenience function that packs the kMaxBlurSample scalars into SkV4's to match the required
112// type of the uniforms in GetBlur2DEffect().
113void Compute2DBlurKernel(SkSize sigma,
114 SkISize radius,
115 std::array<SkV4, kMaxBlurSamples/4>& kernel);
116
117// A convenience for the 2D case where one dimension has a sigma of 0.
118inline void Compute1DBlurKernel(float sigma, int radius, SkSpan<float> kernel) {
119 Compute2DBlurKernel(SkSize{sigma, 0.f}, SkISize{radius, 0}, kernel);
120}
121
122// Utility function to fill in 'offsets' for the effect returned by GetBlur2DEffect(). It
123// automatically fills in the elements beyond the kernel size with the last real offset to
124// maximize texture cache hits. Each offset is really an SkV2 but are packed into SkV4's to match
125// the uniform declaration, and are otherwise ordered row-major.
126void Compute2DBlurOffsets(SkISize radius, std::array<SkV4, kMaxBlurSamples/2>& offsets);
127
128// Calculates a set of weights and sampling offsets for a 1D blur that uses GPU hardware to linearly
129// combine two logical source pixel values. This assumes that 'radius' was from a prior call to
130// BlurSigmaRadius() and is passed in to avoid redundant calculations. To match std140 uniform
131// packing, the offset and kernel weight for adjacent samples are packed into a single SkV4 as
132// {offset[2*i], kernel[2*i], offset[2*i+1], kernel[2*i+1]}
133//
134// The provided array is fully written to. The calculated values are written to indices 0 through
135// BlurLinearKernelWidth(radius), with any remaining indices zero initialized. It requires the spans
136// to be the same size and have at least BlurLinearKernelWidth(radius) elements.
137//
138// NOTE: This takes an array of a constrained size because its main use is calculating uniforms for
139// an effect with a matching constraint. Knowing the size of the linear kernel means the full kernel
140// can be stored on the stack internally.
141void Compute1DBlurLinearKernel(float sigma,
142 int radius,
143 std::array<SkV4, kMaxBlurSamples/2>& offsetsAndKernel);
144
145// Calculates the integral table for an analytic rectangle blur. The integral values are stored in
146// the red channel of the provided bitmap, which will be 1D with a 1-pixel height.
147SkBitmap CreateIntegralTable(float sixSigma);
148
149// Returns the width of an integral table we will create for the given 6*sigma.
150int ComputeIntegralTableWidth(float sixSigma);
151
152// Creates a profile of a blurred circle.
153SkBitmap CreateCircleProfile(float sigma, float radius, int profileWidth);
154
155// Creates a half plane approximation profile of a blurred circle.
156SkBitmap CreateHalfPlaneProfile(int profileWidth);
157
158// Creates a blurred rounded rectangle mask. 'rrectToDraw' is the original rrect centered within
159// bounds defined by 'dimensions', which encompass the entire blurred rrect.
160SkBitmap CreateRRectBlurMask(const SkRRect& rrectToDraw, const SkISize& dimensions, float sigma);
161
162} // namespace skgpu
163
164#endif // skgpu_BlurUtils_DEFINED
#define sk_float_ceil2int(x)
constexpr bool BlurIsEffectivelyIdentity(float sigma)
Definition BlurUtils.h:37
SkBitmap CreateRRectBlurMask(const SkRRect &rrectToDraw, const SkISize &dimensions, float sigma)
static constexpr int kMaxBlurSamples
Definition BlurUtils.h:52
SkBitmap CreateIntegralTable(float sixSigma)
int ComputeIntegralTableWidth(float sixSigma)
static constexpr float kMaxLinearBlurSigma
Definition BlurUtils.h:58
void Compute1DBlurKernel(float sigma, int radius, SkSpan< float > kernel)
Definition BlurUtils.h:118
void Compute2DBlurOffsets(SkISize radius, std::array< SkV4, kMaxBlurSamples/2 > &offsets)
Definition BlurUtils.cpp:93
SkBitmap CreateCircleProfile(float sigma, float radius, int profileWidth)
const SkRuntimeEffect * GetLinearBlur1DEffect(int radius)
int BlurSigmaRadius(float sigma)
Definition BlurUtils.h:41
constexpr int BlurKernelWidth(int radius)
Definition BlurUtils.h:28
void Compute2DBlurKernel(SkSize sigma, SkISize radius, SkSpan< float > kernel)
Definition BlurUtils.cpp:35
constexpr int BlurLinearKernelWidth(int radius)
Definition BlurUtils.h:32
SkBitmap CreateHalfPlaneProfile(int profileWidth)
const SkRuntimeEffect * GetBlur2DEffect(const SkISize &radii)
void Compute1DBlurLinearKernel(float sigma, int radius, std::array< SkV4, kMaxBlurSamples/2 > &offsetsAndKernel)
Definition SkM44.h:98