52 const float twoSigmaSqrdX = 2.0f * sigma.
width() * sigma.
width();
53 const float twoSigmaSqrdY = 2.0f * sigma.
height() * sigma.
height();
59 const float sigmaXDenom = radius.
width() > 0 ? 1.0f / twoSigmaSqrdX : 1.f;
60 const float sigmaYDenom = radius.
height() > 0 ? 1.0f / twoSigmaSqrdY : 1.f;
64 float xTerm =
static_cast<float>(
x - radius.
width());
65 xTerm = xTerm * xTerm * sigmaXDenom;
67 float yTerm =
static_cast<float>(
y - radius.
height());
68 float xyTerm = std::exp(-(xTerm + yTerm * yTerm * sigmaYDenom));
76 float scale = 1.0f / sum;
77 for (
size_t i = 0; i < kernelSize; ++i) {
81 memset(kernel.
data() + kernelSize, 0,
sizeof(
float)*(kernel.
size() - kernelSize));
86 std::array<SkV4, kMaxBlurSamples/4>& kernel) {
87 static_assert(
sizeof(kernel) ==
sizeof(std::array<float, kMaxBlurSamples>));
88 static_assert(
alignof(float) ==
alignof(
SkV4));
89 float* data = kernel[0].ptr();
103 offsetView[2*i+1] =
y;
108 const int lastValidOffset = 2*(kernelArea - 1);
110 offsetView[2*i] = offsetView[lastValidOffset];
111 offsetView[2*i+1] = offsetView[lastValidOffset+1];
117 std::array<SkV4, kMaxBlurSamples/2>& offsetsAndKernel) {
128 auto get_new_weight = [](
float* new_w,
float*
offset,
float wi,
float wj) {
137 std::array<float, kMaxKernelWidth> fullKernel;
140 std::array<float, kMaxBlurSamples> kernel;
141 std::array<float, kMaxBlurSamples> offsets;
144 int halfRadius = halfSize / 2;
145 int lowIndex = halfRadius - 1;
160 get_new_weight(&kernel[halfRadius],
161 &offsets[halfRadius],
162 fullKernel[index] * 0.5f,
163 fullKernel[index + 1]);
164 kernel[lowIndex] = kernel[halfRadius];
165 offsets[lowIndex] = -offsets[halfRadius];
171 kernel[halfRadius] = fullKernel[index];
172 offsets[halfRadius] = 0.0f;
177 for (
int i = halfRadius + 1; i < halfSize; index += 2, i++, lowIndex--) {
178 get_new_weight(&kernel[i], &offsets[i], fullKernel[index], fullKernel[index + 1]);
179 offsets[i] +=
static_cast<float>(index - radius);
182 kernel[lowIndex] = kernel[i];
183 offsets[lowIndex] = -offsets[i];
187 memset(kernel.data() + halfSize, 0,
sizeof(
float)*(
kMaxBlurSamples - halfSize));
191 offsets[i] = offsets[halfSize - 1];
196 offsetsAndKernel[i] =
SkV4{offsets[2*i], kernel[2*i], offsets[2*i+1], kernel[2*i+1]};
202 switch(kernelWidth) {
204 case 2: [[fallthrough]];
205 case 3: [[fallthrough]];
207 case 5: [[fallthrough]];
208 case 6: [[fallthrough]];
209 case 7: [[fallthrough]];
211 case 9: [[fallthrough]];
212 case 10: [[fallthrough]];
213 case 11: [[fallthrough]];
215 case 13: [[fallthrough]];
216 case 14: [[fallthrough]];
217 case 15: [[fallthrough]];
219 case 17: [[fallthrough]];
220 case 18: [[fallthrough]];
221 case 19: [[fallthrough]];
224 case 21: [[fallthrough]];
225 case 22: [[fallthrough]];
226 case 23: [[fallthrough]];
227 case 24: [[fallthrough]];
228 case 25: [[fallthrough]];
229 case 26: [[fallthrough]];
230 case 27: [[fallthrough]];
238 return GetKnownRuntimeEffect(
240 static_cast<uint32_t
>(SkKnownRuntimeEffects::StableKey::k1DBlurBase)));
245 return GetKnownRuntimeEffect(
247 static_cast<uint32_t
>(SkKnownRuntimeEffects::StableKey::k2DBlurBase)));
267 *
table.getAddr8(0, 0) = 255;
268 const float invWidth = 1.f /
width;
269 for (
int i = 1; i <
width - 1; ++i) {
270 float x = (i + 0.5f) * invWidth;
272 float integral = 0.5f * (std::erf(
x) + 1.f);
277 table.setImmutable();
295 int minWidth = 2 * ((
int)std::ceil(sixSigma));
307 const float invSigma = 1.0f / sigma;
308 const float b = -0.5f * invSigma * invSigma;
312 for (
int i = 0; i < halfKernelSize; ++i) {
313 float value = expf(t * t *
b);
315 halfKernel[i] =
value;
324 float* summedHalfKernel,
330 for (
int i = 0; i < halfKernelSize; ++i) {
331 halfKernel[i] /= tot;
332 sum += halfKernel[i];
333 summedHalfKernel[i] = sum;
344 const float* summedHalfKernelTable) {
346 for (
int i = 0; i < numSteps; ++i,
x += 1.0f) {
347 if (x < -circleR || x > circleR) {
351 float y = sqrtf(circleR * circleR -
x *
x);
358 results[i] = (
y + 0.5f) * summedHalfKernelTable[0];
359 }
else if (yInt >= halfKernelSize - 1) {
362 float yFrac =
y - yInt;
363 results[i] = (1.0f - yFrac) * summedHalfKernelTable[yInt] +
364 yFrac * summedHalfKernelTable[yInt + 1];
375 const float* halfKernel,
377 const float* yKernelEvaluations) {
380 float x = evalX - halfKernelSize;
381 for (
int i = 0; i < halfKernelSize; ++i,
x += 1.0f) {
382 if (x < -circleR || x > circleR) {
385 float verticalEval = yKernelEvaluations[i];
386 acc += verticalEval * halfKernel[halfKernelSize - i - 1];
388 for (
int i = 0; i < halfKernelSize; ++i,
x += 1.0f) {
389 if (x < -circleR || x > circleR) {
392 float verticalEval = yKernelEvaluations[i + halfKernelSize];
393 acc += verticalEval * halfKernel[i];
413 uint8_t* profile =
bitmap.getAddr8(0, 0);
415 const int numSteps = profileWidth;
420 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1;
423 const int numYSteps = numSteps + 2 * halfKernelSize;
426 float* halfKernel = bulkAlloc.
get();
427 float* summedKernel = bulkAlloc.
get() + halfKernelSize;
428 float* yEvals = bulkAlloc.
get() + 2 * halfKernelSize;
431 float firstX = -halfKernelSize + 0.5f;
432 apply_kernel_in_y(yEvals, numYSteps, firstX, radius, halfKernelSize, summedKernel);
434 for (
int i = 0; i < numSteps - 1; ++i) {
435 float evalX = i + 0.5f;
436 profile[i] =
eval_at(evalX, radius, halfKernel, halfKernelSize, yEvals + i);
439 profile[numSteps - 1] = 0;
453 uint8_t* profile =
bitmap.getAddr8(0, 0);
456 const float sigma = profileWidth / 6.0f;
457 const int halfKernelSize = profileWidth / 2;
465 for (
int i = 0; i < halfKernelSize; ++i) {
466 halfKernel[halfKernelSize - i - 1] /= tot;
467 sum += halfKernel[halfKernelSize - i - 1];
472 for (
int i = 0; i < halfKernelSize; ++i) {
473 sum += halfKernel[i];
477 profile[profileWidth - 1] = 0;
489static uint8_t
eval_V(
float top,
int y,
const uint8_t* integral,
int integralSize,
float sixSigma) {
494 float fT = (top -
y - 0.5f) * (integralSize / sixSigma);
497 }
else if (fT >= integralSize - 1) {
502 float frac = fT - lower;
506 return integral[lower] * (1.0f - frac) + integral[lower + 1] * frac;
512 const std::vector<float>& topVec,
515 const uint8_t* integral,
523 int xSampleLoc =
x - (kernelSize / 2);
524 for (
int i = 0; i < kernelSize; ++i, ++xSampleLoc) {
525 if (xSampleLoc < 0 || xSampleLoc >= (
int)topVec.size()) {
529 accum += kernel[i] *
eval_V(topVec[xSampleLoc],
y, integral, integralSize, sixSigma);
547 const int halfWidthPlus1 = (dimensions.
width() / 2) + 1;
548 const int halfHeightPlus1 = (dimensions.
height() / 2) + 1;
550 std::unique_ptr<float[]> kernel(
new float[kernelSize]);
554 if (integral.
empty()) {
563 std::vector<float> topVec;
564 topVec.reserve(dimensions.
width());
565 for (
int x = 0;
x < dimensions.
width(); ++
x) {
567 topVec.push_back(-1);
570 float xDist = rrectToDraw.
rect().
fLeft + radii.
fX -
x - 0.5f;
571 float h = sqrtf(radii.
fX * radii.
fX - xDist * xDist);
573 topVec.push_back(rrectToDraw.
rect().
fTop + radii.
fX -
h + 3 * sigma);
575 topVec.push_back(rrectToDraw.
rect().
fTop + 3 * sigma);
580 for (
int y = 0;
y < halfHeightPlus1; ++
y) {
581 uint8_t* scanline =
result.getAddr8(0,
y);
583 for (
int x = 0;
x < halfWidthPlus1; ++
x) {
592 scanline[dimensions.
width() -
x - 1] = scanline[
x];
static U8CPU SkUnitScalarClampToByte(SkScalar x)
static bool SkIsFinite(T x, Pack... values)
#define sk_float_round2int(x)
static int SkNextPow2(int value)
static int64_t sk_64_mul(int64_t a, int64_t b)
static constexpr int32_t SK_MaxS32
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
#define SkScalarCeilToInt(x)
#define SkScalarFloorToInt(x)
#define SK_ScalarRoot2Over2
constexpr uint8_t SkToU8(S x)
Type::kYUV Type::kRGBA() int(0.7 *637)
uint8_t * getAddr8(int x, int y) const
SkVector getSimpleRadii() const
const SkRect & rect() const
constexpr T * data() const
constexpr size_t size() const
constexpr bool BlurIsEffectivelyIdentity(float sigma)
static uint8_t eval_H(int x, int y, const std::vector< float > &topVec, const float *kernel, int kernelSize, const uint8_t *integral, int integralSize, float sixSigma)
SkBitmap CreateRRectBlurMask(const SkRRect &rrectToDraw, const SkISize &dimensions, float sigma)
static constexpr int kMaxBlurSamples
SkBitmap CreateIntegralTable(float sixSigma)
int ComputeIntegralTableWidth(float sixSigma)
static constexpr float kMaxLinearBlurSigma
void Compute1DBlurKernel(float sigma, int radius, SkSpan< float > kernel)
void Compute2DBlurOffsets(SkISize radius, std::array< SkV4, kMaxBlurSamples/2 > &offsets)
static uint8_t eval_at(float evalX, float circleR, const float *halfKernel, int halfKernelSize, const float *yKernelEvaluations)
SkBitmap CreateCircleProfile(float sigma, float radius, int profileWidth)
static SkKnownRuntimeEffects::StableKey to_stablekey(int kernelWidth, uint32_t baseKey)
static void apply_kernel_in_y(float *results, int numSteps, float firstX, float circleR, int halfKernelSize, const float *summedHalfKernelTable)
const SkRuntimeEffect * GetLinearBlur1DEffect(int radius)
int BlurSigmaRadius(float sigma)
static uint8_t eval_V(float top, int y, const uint8_t *integral, int integralSize, float sixSigma)
constexpr int BlurKernelWidth(int radius)
static void make_half_kernel_and_summed_table(float *halfKernel, float *summedHalfKernel, int halfKernelSize, float sigma)
void Compute2DBlurKernel(SkSize sigma, SkISize radius, SkSpan< float > kernel)
constexpr int BlurLinearKernelWidth(int radius)
SkBitmap CreateHalfPlaneProfile(int profileWidth)
static float make_unnormalized_half_kernel(float *halfKernel, int halfKernelSize, float sigma)
const SkRuntimeEffect * GetBlur2DEffect(const SkISize &radii)
void Compute1DBlurLinearKernel(float sigma, int radius, std::array< SkV4, kMaxBlurSamples/2 > &offsetsAndKernel)
constexpr int32_t width() const
constexpr int32_t height() const
static SkImageInfo MakeA8(int width, int height)
SkScalar fLeft
smaller x-axis bounds
SkScalar fRight
larger x-axis bounds
SkScalar fTop
smaller y-axis bounds