Flutter Engine
The Flutter Engine
Public Member Functions | Static Public Member Functions | Static Public Attributes | Private Member Functions | List of all members
SkShaderBlurAlgorithm Class Referenceabstract

#include <SkBlurEngine.h>

Inheritance diagram for SkShaderBlurAlgorithm:
SkBlurEngine::Algorithm

Public Member Functions

float maxSigma () const override
 
bool supportsOnlyDecalTiling () const override
 
sk_sp< SkSpecialImageblur (SkSize sigma, sk_sp< SkSpecialImage > src, const SkIRect &srcRect, SkTileMode tileMode, const SkIRect &dstRect) const override
 
- Public Member Functions inherited from SkBlurEngine::Algorithm
virtual ~Algorithm ()=default
 
virtual float maxSigma () const =0
 
virtual bool supportsOnlyDecalTiling () const =0
 
virtual sk_sp< SkSpecialImageblur (SkSize sigma, sk_sp< SkSpecialImage > src, const SkIRect &srcRect, SkTileMode tileMode, const SkIRect &dstRect) const =0
 

Static Public Member Functions

static constexpr int KernelWidth (int radius)
 
static constexpr int LinearKernelWidth (int radius)
 
static const SkRuntimeEffectGetBlur2DEffect (const SkISize &radii)
 
static const SkRuntimeEffectGetLinearBlur1DEffect (int radius)
 
static void Compute2DBlurKernel (SkSize sigma, SkISize radius, SkSpan< float > kernel)
 
static void Compute2DBlurKernel (SkSize sigma, SkISize radius, std::array< SkV4, kMaxSamples/4 > &kernel)
 
static void Compute1DBlurKernel (float sigma, int radius, SkSpan< float > kernel)
 
static void Compute2DBlurOffsets (SkISize radius, std::array< SkV4, kMaxSamples/2 > &offsets)
 
static void Compute1DBlurLinearKernel (float sigma, int radius, std::array< SkV4, kMaxSamples/2 > &offsetsAndKernel)
 

Static Public Attributes

static constexpr int kMaxSamples = 28
 
static constexpr float kMaxLinearSigma = 4.f
 

Private Member Functions

virtual sk_sp< SkDevicemakeDevice (const SkImageInfo &) const =0
 

Detailed Description

The default blur implementation uses internal runtime effects to evaluate either a single 2D kernel within a shader, or performs two 1D blur passes. This algorithm is backend agnostic but must be subclassed per backend to define the SkDevice creation function.

Definition at line 112 of file SkBlurEngine.h.

Member Function Documentation

◆ blur()

sk_sp< SkSpecialImage > SkShaderBlurAlgorithm::blur ( SkSize  sigma,
sk_sp< SkSpecialImage src,
const SkIRect srcRect,
SkTileMode  tileMode,
const SkIRect dstRect 
) const
overridevirtual

Implements SkBlurEngine::Algorithm.

Definition at line 383 of file SkBlurEngine.cpp.

387 {
388 SkASSERT(sigma.width() <= kMaxLinearSigma && sigma.height() <= kMaxLinearSigma);
389
390 int radiusX = SkBlurEngine::SigmaToRadius(sigma.width());
391 int radiusY = SkBlurEngine::SigmaToRadius(sigma.height());
392 const int kernelArea = KernelWidth(radiusX) * KernelWidth(radiusY);
393 if (kernelArea <= kMaxSamples && radiusX > 0 && radiusY > 0) {
394 // Use a single-pass 2D kernel if it fits and isn't just 1D already
395 return this->evalBlur2D(sigma,
396 {radiusX, radiusY},
397 std::move(src),
398 srcRect,
399 tileMode,
400 dstRect);
401 } else {
402 // Use two passes of a 1D kernel (one per axis).
403 SkIRect intermediateSrcRect = srcRect;
404 SkIRect intermediateDstRect = dstRect;
405 if (radiusX > 0) {
406 if (radiusY > 0) {
407 // May need to maintain extra rows above and below 'dstRect' for the follow-up pass.
408 if (tileMode == SkTileMode::kRepeat || tileMode == SkTileMode::kMirror) {
409 // If the srcRect and dstRect are aligned, then we don't need extra rows since
410 // the periodic tiling on srcRect is the same for the intermediate. If they
411 // are not aligned, then outset by the Y radius.
412 const int period = srcRect.height() * (tileMode == SkTileMode::kMirror ? 2 : 1);
413 if (std::abs(dstRect.fTop - srcRect.fTop) % period != 0 ||
414 dstRect.height() != srcRect.height()) {
415 intermediateDstRect.outset(0, radiusY);
416 }
417 } else {
418 // For clamp and decal tiling, we outset by the Y radius up to what's available
419 // from the srcRect. Anything beyond that is identical to tiling the
420 // intermediate dst image directly.
421 intermediateDstRect.outset(0, radiusY);
422 intermediateDstRect.fTop = std::max(intermediateDstRect.fTop, srcRect.fTop);
423 intermediateDstRect.fBottom =
424 std::min(intermediateDstRect.fBottom, srcRect.fBottom);
425 if (intermediateDstRect.fTop >= intermediateDstRect.fBottom) {
426 return nullptr;
427 }
428 }
429 }
430
431 src = this->evalBlur1D(sigma.width(),
432 radiusX,
433 /*dir=*/{1.f, 0.f},
434 std::move(src),
435 srcRect,
436 tileMode,
437 intermediateDstRect);
438 if (!src) {
439 return nullptr;
440 }
441 intermediateSrcRect = SkIRect::MakeWH(src->width(), src->height());
442 intermediateDstRect = dstRect.makeOffset(-intermediateDstRect.left(),
443 -intermediateDstRect.top());
444 }
445
446 if (radiusY > 0) {
447 src = this->evalBlur1D(sigma.height(),
448 radiusY,
449 /*dir=*/{0.f, 1.f},
450 std::move(src),
451 intermediateSrcRect,
452 tileMode,
453 intermediateDstRect);
454 }
455
456 return src;
457 }
458}
#define SkASSERT(cond)
Definition: SkAssert.h:116
static int SigmaToRadius(float sigma)
Definition: SkBlurEngine.h:67
static constexpr float kMaxLinearSigma
Definition: SkBlurEngine.h:172
static constexpr int KernelWidth(int radius)
Definition: SkBlurEngine.h:154
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition: SkVx.h:707
Definition: SkRect.h:32
int32_t fBottom
larger y-axis bounds
Definition: SkRect.h:36
constexpr int32_t top() const
Definition: SkRect.h:120
constexpr int32_t height() const
Definition: SkRect.h:165
int32_t fTop
smaller y-axis bounds
Definition: SkRect.h:34
constexpr SkIRect makeOffset(int32_t dx, int32_t dy) const
Definition: SkRect.h:300
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
void outset(int32_t dx, int32_t dy)
Definition: SkRect.h:428
constexpr int32_t left() const
Definition: SkRect.h:113
SkScalar width() const
Definition: SkSize.h:76
SkScalar height() const
Definition: SkSize.h:77

◆ Compute1DBlurKernel()

static void SkShaderBlurAlgorithm::Compute1DBlurKernel ( float  sigma,
int  radius,
SkSpan< float >  kernel 
)
inlinestatic

Definition at line 235 of file SkBlurEngine.h.

235 {
236 Compute2DBlurKernel(SkSize{sigma, 0.f}, SkISize{radius, 0}, kernel);
237 }
static void Compute2DBlurKernel(SkSize sigma, SkISize radius, SkSpan< float > kernel)
Definition: SkSize.h:16
Definition: SkSize.h:52

◆ Compute1DBlurLinearKernel()

void SkShaderBlurAlgorithm::Compute1DBlurLinearKernel ( float  sigma,
int  radius,
std::array< SkV4, kMaxSamples/2 > &  offsetsAndKernel 
)
static

Definition at line 118 of file SkBlurEngine.cpp.

121 {
122 SkASSERT(sigma <= kMaxLinearSigma);
123 SkASSERT(radius == SkBlurEngine::SigmaToRadius(sigma));
125
126 // Given 2 adjacent gaussian points, they are blended as: Wi * Ci + Wj * Cj.
127 // The GPU will mix Ci and Cj as Ci * (1 - x) + Cj * x during sampling.
128 // Compute W', x such that W' * (Ci * (1 - x) + Cj * x) = Wi * Ci + Wj * Cj.
129 // Solving W' * x = Wj, W' * (1 - x) = Wi:
130 // W' = Wi + Wj
131 // x = Wj / (Wi + Wj)
132 auto get_new_weight = [](float* new_w, float* offset, float wi, float wj) {
133 *new_w = wi + wj;
134 *offset = wj / (wi + wj);
135 };
136
137 // Create a temporary standard kernel. The maximum blur radius that can be passed to this
138 // function is (kMaxBlurSamples-1), so make an array large enough to hold the full kernel width.
139 static constexpr int kMaxKernelWidth = KernelWidth(kMaxSamples - 1);
140 SkASSERT(KernelWidth(radius) <= kMaxKernelWidth);
141 std::array<float, kMaxKernelWidth> fullKernel;
142 Compute1DBlurKernel(sigma, radius, SkSpan<float>{fullKernel.data(), KernelWidth(radius)});
143
144 std::array<float, kMaxSamples> kernel;
145 std::array<float, kMaxSamples> offsets;
146 // Note that halfsize isn't just size / 2, but radius + 1. This is the size of the output array.
147 int halfSize = LinearKernelWidth(radius);
148 int halfRadius = halfSize / 2;
149 int lowIndex = halfRadius - 1;
150
151 // Compute1DGaussianKernel produces a full 2N + 1 kernel. Since the kernel can be mirrored,
152 // compute only the upper half and mirror to the lower half.
153
154 int index = radius;
155 if (radius & 1) {
156 // If N is odd, then use two samples.
157 // The centre texel gets sampled twice, so halve its influence for each sample.
158 // We essentially sample like this:
159 // Texel edges
160 // v v v v
161 // | | | |
162 // \-----^---/ Lower sample
163 // \---^-----/ Upper sample
164 get_new_weight(&kernel[halfRadius],
165 &offsets[halfRadius],
166 fullKernel[index] * 0.5f,
167 fullKernel[index + 1]);
168 kernel[lowIndex] = kernel[halfRadius];
169 offsets[lowIndex] = -offsets[halfRadius];
170 index++;
171 lowIndex--;
172 } else {
173 // If N is even, then there are an even number of texels on either side of the centre texel.
174 // Sample the centre texel directly.
175 kernel[halfRadius] = fullKernel[index];
176 offsets[halfRadius] = 0.0f;
177 }
178 index++;
179
180 // Every other pair gets one sample.
181 for (int i = halfRadius + 1; i < halfSize; index += 2, i++, lowIndex--) {
182 get_new_weight(&kernel[i], &offsets[i], fullKernel[index], fullKernel[index + 1]);
183 offsets[i] += static_cast<float>(index - radius);
184
185 // Mirror to lower half.
186 kernel[lowIndex] = kernel[i];
187 offsets[lowIndex] = -offsets[i];
188 }
189
190 // Zero out remaining values in the kernel
191 memset(kernel.data() + halfSize, 0, sizeof(float)*(kMaxSamples - halfSize));
192 // But copy the last valid offset into the remaining offsets, to increase the chance that
193 // over-iteration in a fragment shader will have a cache hit.
194 for (int i = halfSize; i < kMaxSamples; ++i) {
195 offsets[i] = offsets[halfSize - 1];
196 }
197
198 // Interleave into the output array to match the 1D SkSL effect
199 for (int i = 0; i < kMaxSamples / 2; ++i) {
200 offsetsAndKernel[i] = SkV4{offsets[2*i], kernel[2*i], offsets[2*i+1], kernel[2*i+1]};
201 }
202}
static constexpr int kMaxSamples
Definition: SkBlurEngine.h:166
static void Compute1DBlurKernel(float sigma, int radius, SkSpan< float > kernel)
Definition: SkBlurEngine.h:235
static constexpr int LinearKernelWidth(int radius)
Definition: SkBlurEngine.h:158
constexpr T * data() const
Definition: SkSpan_impl.h:94
list offsets
Definition: mskp_parser.py:37
SeparatedVector2 offset
Definition: SkM44.h:98

◆ Compute2DBlurKernel() [1/2]

void SkShaderBlurAlgorithm::Compute2DBlurKernel ( SkSize  sigma,
SkISize  radius,
SkSpan< float >  kernel 
)
static

Definition at line 37 of file SkBlurEngine.cpp.

39 {
40 // Callers likely had to calculate the radius prior to filling out the kernel value, which is
41 // why it's provided; but make sure it's consistent with expectations.
42 SkASSERT(SkBlurEngine::SigmaToRadius(sigma.width()) == radius.width() &&
43 SkBlurEngine::SigmaToRadius(sigma.height()) == radius.height());
44
45 // Callers are responsible for downscaling large sigmas to values that can be processed by the
46 // effects, so ensure the radius won't overflow 'kernel'
47 const int width = KernelWidth(radius.width());
48 const int height = KernelWidth(radius.height());
49 const size_t kernelSize = SkTo<size_t>(sk_64_mul(width, height));
50 SkASSERT(kernelSize <= kernel.size());
51
52 // And the definition of an identity blur should be sufficient that 2sigma^2 isn't near zero
53 // when there's a non-trivial radius.
54 const float twoSigmaSqrdX = 2.0f * sigma.width() * sigma.width();
55 const float twoSigmaSqrdY = 2.0f * sigma.height() * sigma.height();
56 SkASSERT((radius.width() == 0 || !SkScalarNearlyZero(twoSigmaSqrdX)) &&
57 (radius.height() == 0 || !SkScalarNearlyZero(twoSigmaSqrdY)));
58
59 // Setting the denominator to 1 when the radius is 0 automatically converts the remaining math
60 // to the 1D Gaussian distribution. When both radii are 0, it correctly computes a weight of 1.0
61 const float sigmaXDenom = radius.width() > 0 ? 1.0f / twoSigmaSqrdX : 1.f;
62 const float sigmaYDenom = radius.height() > 0 ? 1.0f / twoSigmaSqrdY : 1.f;
63
64 float sum = 0.0f;
65 for (int x = 0; x < width; x++) {
66 float xTerm = static_cast<float>(x - radius.width());
67 xTerm = xTerm * xTerm * sigmaXDenom;
68 for (int y = 0; y < height; y++) {
69 float yTerm = static_cast<float>(y - radius.height());
70 float xyTerm = std::exp(-(xTerm + yTerm * yTerm * sigmaYDenom));
71 // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
72 // is dropped here, since we renormalize the kernel below.
73 kernel[y * width + x] = xyTerm;
74 sum += xyTerm;
75 }
76 }
77 // Normalize the kernel
78 float scale = 1.0f / sum;
79 for (size_t i = 0; i < kernelSize; ++i) {
80 kernel[i] *= scale;
81 }
82 // Zero remainder of the array
83 memset(kernel.data() + kernelSize, 0, sizeof(float)*(kernel.size() - kernelSize));
84}
static int64_t sk_64_mul(int64_t a, int64_t b)
Definition: SkMath.h:33
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:101
constexpr size_t size() const
Definition: SkSpan_impl.h:95
double y
double x
int32_t height
int32_t width
const Scalar scale
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37

◆ Compute2DBlurKernel() [2/2]

void SkShaderBlurAlgorithm::Compute2DBlurKernel ( SkSize  sigma,
SkISize  radius,
std::array< SkV4, kMaxSamples/4 > &  kernel 
)
static

Definition at line 86 of file SkBlurEngine.cpp.

88 {
89 static_assert(sizeof(kernel) == sizeof(std::array<float, kMaxSamples>));
90 static_assert(alignof(float) == alignof(SkV4));
91 float* data = kernel[0].ptr();
93}
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63

◆ Compute2DBlurOffsets()

void SkShaderBlurAlgorithm::Compute2DBlurOffsets ( SkISize  radius,
std::array< SkV4, kMaxSamples/2 > &  offsets 
)
static

Definition at line 95 of file SkBlurEngine.cpp.

96 {
97 const int kernelArea = KernelWidth(radius.width()) * KernelWidth(radius.height());
98 SkASSERT(kernelArea <= kMaxSamples);
99
100 SkSpan<float> offsetView{offsets[0].ptr(), kMaxSamples*2};
101
102 int i = 0;
103 for (int y = -radius.height(); y <= radius.height(); ++y) {
104 for (int x = -radius.width(); x <= radius.width(); ++x) {
105 offsetView[2*i] = x;
106 offsetView[2*i+1] = y;
107 ++i;
108 }
109 }
110 SkASSERT(i == kernelArea);
111 const int lastValidOffset = 2*(kernelArea - 1);
112 for (; i < kMaxSamples; ++i) {
113 offsetView[2*i] = offsetView[lastValidOffset];
114 offsetView[2*i+1] = offsetView[lastValidOffset+1];
115 }
116}

◆ GetBlur2DEffect()

const SkRuntimeEffect * SkShaderBlurAlgorithm::GetBlur2DEffect ( const SkISize radii)
static

Definition at line 247 of file SkBlurEngine.cpp.

247 {
248 int kernelArea = KernelWidth(radii.width()) * KernelWidth(radii.height());
250 to_stablekey(kernelArea,
251 static_cast<uint32_t>(SkKnownRuntimeEffects::StableKey::k2DBlurBase)));
252}
static SkKnownRuntimeEffects::StableKey to_stablekey(int kernelWidth, uint32_t baseKey)
const SkRuntimeEffect * GetKnownRuntimeEffect(StableKey stableKey)

◆ GetLinearBlur1DEffect()

const SkRuntimeEffect * SkShaderBlurAlgorithm::GetLinearBlur1DEffect ( int  radius)
static

Definition at line 241 of file SkBlurEngine.cpp.

241 {
244 static_cast<uint32_t>(SkKnownRuntimeEffects::StableKey::k1DBlurBase)));
245}

◆ KernelWidth()

static constexpr int SkShaderBlurAlgorithm::KernelWidth ( int  radius)
inlinestaticconstexpr

Definition at line 154 of file SkBlurEngine.h.

154{ return 2 * radius + 1; }

◆ LinearKernelWidth()

static constexpr int SkShaderBlurAlgorithm::LinearKernelWidth ( int  radius)
inlinestaticconstexpr

Definition at line 158 of file SkBlurEngine.h.

158{ return radius + 1; }

◆ makeDevice()

virtual sk_sp< SkDevice > SkShaderBlurAlgorithm::makeDevice ( const SkImageInfo ) const
privatepure virtual

◆ maxSigma()

float SkShaderBlurAlgorithm::maxSigma ( ) const
inlineoverridevirtual

Implements SkBlurEngine::Algorithm.

Definition at line 114 of file SkBlurEngine.h.

114{ return kMaxLinearSigma; }

◆ supportsOnlyDecalTiling()

bool SkShaderBlurAlgorithm::supportsOnlyDecalTiling ( ) const
inlineoverridevirtual

Implements SkBlurEngine::Algorithm.

Definition at line 115 of file SkBlurEngine.h.

115{ return false; }

Member Data Documentation

◆ kMaxLinearSigma

constexpr float SkShaderBlurAlgorithm::kMaxLinearSigma = 4.f
staticconstexpr

Definition at line 172 of file SkBlurEngine.h.

◆ kMaxSamples

constexpr int SkShaderBlurAlgorithm::kMaxSamples = 28
staticconstexpr

Definition at line 166 of file SkBlurEngine.h.


The documentation for this class was generated from the following files: