Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkBlurMask.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2006 The Android Open Source Project
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
9
13#include "include/core/SkRect.h"
19#include "src/base/SkMathPriv.h"
21
22#include <cmath>
23#include <cstring>
24#include <utility>
25
26class SkRRect;
27
28using namespace skia_private;
29
30// This constant approximates the scaling done in the software path's
31// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
32// IMHO, it actually should be 1: we blur "less" than we should do
33// according to the CSS and canvas specs, simply because Safari does the same.
34// Firefox used to do the same too, until 4.0 where they fixed it. So at some
35// point we should probably get rid of these scaling constants and rebaseline
36// all the blur tests.
37static const SkScalar kBLUR_SIGMA_SCALE = 0.57735f;
38
40 return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
41}
42
44 return sigma > 0.5f ? (sigma - 0.5f) / kBLUR_SIGMA_SCALE : 0.0f;
45}
46
47
48template <typename AlphaIter>
49static void merge_src_with_blur(uint8_t dst[], int dstRB,
50 AlphaIter src, int srcRB,
51 const uint8_t blur[], int blurRB,
52 int sw, int sh) {
53 dstRB -= sw;
54 blurRB -= sw;
55 while (--sh >= 0) {
56 AlphaIter rowSrc(src);
57 for (int x = sw - 1; x >= 0; --x) {
58 *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*rowSrc)));
59 ++dst;
60 ++rowSrc;
61 ++blur;
62 }
63 dst += dstRB;
64 src >>= srcRB;
65 blur += blurRB;
66 }
67}
68
69template <typename AlphaIter>
70static void clamp_solid_with_orig(uint8_t dst[], int dstRowBytes,
71 AlphaIter src, int srcRowBytes,
72 int sw, int sh) {
73 int x;
74 while (--sh >= 0) {
75 AlphaIter rowSrc(src);
76 for (x = sw - 1; x >= 0; --x) {
77 int s = *rowSrc;
78 int d = *dst;
79 *dst = SkToU8(s + d - SkMulDiv255Round(s, d));
80 ++dst;
81 ++rowSrc;
82 }
83 dst += dstRowBytes - sw;
84 src >>= srcRowBytes;
85 }
86}
87
88template <typename AlphaIter>
89static void clamp_outer_with_orig(uint8_t dst[], int dstRowBytes,
90 AlphaIter src, int srcRowBytes,
91 int sw, int sh) {
92 int x;
93 while (--sh >= 0) {
94 AlphaIter rowSrc(src);
95 for (x = sw - 1; x >= 0; --x) {
96 int srcValue = *rowSrc;
97 if (srcValue) {
98 *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - srcValue)));
99 }
100 ++dst;
101 ++rowSrc;
102 }
103 dst += dstRowBytes - sw;
104 src >>= srcRowBytes;
105 }
106}
107///////////////////////////////////////////////////////////////////////////////
108
109// we use a local function to wrap the class static method to work around
110// a bug in gcc98
111void SkMask_FreeImage(uint8_t* image);
115
116bool SkBlurMask::BoxBlur(SkMaskBuilder* dst, const SkMask& src, SkScalar sigma, SkBlurStyle style,
117 SkIPoint* margin) {
118 if (src.fFormat != SkMask::kBW_Format &&
119 src.fFormat != SkMask::kA8_Format &&
120 src.fFormat != SkMask::kARGB32_Format &&
121 src.fFormat != SkMask::kLCD16_Format)
122 {
123 return false;
124 }
125
126 SkMaskBlurFilter blurFilter{sigma, sigma};
127 if (blurFilter.hasNoBlur()) {
128 // If there is no effective blur most styles will just produce the original mask.
129 // However, kOuter_SkBlurStyle will produce an empty mask.
130 if (style == kOuter_SkBlurStyle) {
131 dst->image() = nullptr;
132 dst->bounds() = SkIRect::MakeEmpty();
133 dst->rowBytes() = dst->fBounds.width();
134 dst->format() = SkMask::kA8_Format;
135 if (margin != nullptr) {
136 // This filter will disregard the src.fImage completely.
137 // The margin is actually {-(src.fBounds.width() / 2), -(src.fBounds.height() / 2)}
138 // but it is not clear if callers will fall over with negative margins.
139 *margin = SkIPoint{0,0};
140 }
141 return true;
142 }
143 return false;
144 }
145 const SkIPoint border = blurFilter.blur(src, dst);
146 // If src.fImage is null, then this call is only to calculate the border.
147 if (src.fImage != nullptr && dst->fImage == nullptr) {
148 return false;
149 }
150
151 if (margin != nullptr) {
152 *margin = border;
153 }
154
155 if (src.fImage == nullptr) {
156 if (style == kInner_SkBlurStyle) {
157 dst->bounds() = src.fBounds; // restore trimmed bounds
158 dst->rowBytes() = dst->fBounds.width();
159 }
160 return true;
161 }
162
163 switch (style) {
165 break;
166 case kSolid_SkBlurStyle: {
167 auto dstStart = &dst->image()[border.x() + border.y() * dst->fRowBytes];
168 switch (src.fFormat) {
171 dstStart, dst->fRowBytes,
172 SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes,
173 src.fBounds.width(), src.fBounds.height());
174 break;
177 dstStart, dst->fRowBytes,
178 SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes,
179 src.fBounds.width(), src.fBounds.height());
180 break;
182 const uint32_t* srcARGB = reinterpret_cast<const uint32_t*>(src.fImage);
184 dstStart, dst->fRowBytes,
185 SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes,
186 src.fBounds.width(), src.fBounds.height());
187 } break;
189 const uint16_t* srcLCD = reinterpret_cast<const uint16_t*>(src.fImage);
191 dstStart, dst->fRowBytes,
192 SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes,
193 src.fBounds.width(), src.fBounds.height());
194 } break;
195 default:
196 SK_ABORT("Unhandled format.");
197 }
198 } break;
199 case kOuter_SkBlurStyle: {
200 auto dstStart = &dst->image()[border.x() + border.y() * dst->fRowBytes];
201 switch (src.fFormat) {
204 dstStart, dst->fRowBytes,
205 SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes,
206 src.fBounds.width(), src.fBounds.height());
207 break;
210 dstStart, dst->fRowBytes,
211 SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes,
212 src.fBounds.width(), src.fBounds.height());
213 break;
215 const uint32_t* srcARGB = reinterpret_cast<const uint32_t*>(src.fImage);
217 dstStart, dst->fRowBytes,
218 SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes,
219 src.fBounds.width(), src.fBounds.height());
220 } break;
222 const uint16_t* srcLCD = reinterpret_cast<const uint16_t*>(src.fImage);
224 dstStart, dst->fRowBytes,
225 SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes,
226 src.fBounds.width(), src.fBounds.height());
227 } break;
228 default:
229 SK_ABORT("Unhandled format.");
230 }
231 } break;
232 case kInner_SkBlurStyle: {
233 // now we allocate the "real" dst, mirror the size of src
234 SkMaskBuilder blur = std::move(*dst);
235 SkAutoMaskFreeImage autoFreeBlurMask(blur.image());
236
237 *dst = SkMaskBuilder(nullptr, src.fBounds, src.fBounds.width(), blur.format());
238 size_t dstSize = dst->computeImageSize();
239 if (0 == dstSize) {
240 return false; // too big to allocate, abort
241 }
242 dst->image() = SkMaskBuilder::AllocImage(dstSize);
243
244 auto blurStart = &blur.image()[border.x() + border.y() * blur.fRowBytes];
245 switch (src.fFormat) {
248 dst->image(), dst->fRowBytes,
249 SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes,
250 blurStart, blur.fRowBytes,
251 src.fBounds.width(), src.fBounds.height());
252 break;
255 dst->image(), dst->fRowBytes,
256 SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes,
257 blurStart, blur.fRowBytes,
258 src.fBounds.width(), src.fBounds.height());
259 break;
261 const uint32_t* srcARGB = reinterpret_cast<const uint32_t*>(src.fImage);
263 dst->image(), dst->fRowBytes,
264 SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes,
265 blurStart, blur.fRowBytes,
266 src.fBounds.width(), src.fBounds.height());
267 } break;
269 const uint16_t* srcLCD = reinterpret_cast<const uint16_t*>(src.fImage);
271 dst->image(), dst->fRowBytes,
272 SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes,
273 blurStart, blur.fRowBytes,
274 src.fBounds.width(), src.fBounds.height());
275 } break;
276 default:
277 SK_ABORT("Unhandled format.");
278 }
279 } break;
280 }
281
282 return true;
283}
284
285/* Convolving a box with itself three times results in a piecewise
286 quadratic function:
287
288 0 x <= -1.5
289 9/8 + 3/2 x + 1/2 x^2 -1.5 < x <= -.5
290 3/4 - x^2 -.5 < x <= .5
291 9/8 - 3/2 x + 1/2 x^2 0.5 < x <= 1.5
292 0 1.5 < x
293
294 Mathematica:
295
296 g[x_] := Piecewise [ {
297 {9/8 + 3/2 x + 1/2 x^2 , -1.5 < x <= -.5},
298 {3/4 - x^2 , -.5 < x <= .5},
299 {9/8 - 3/2 x + 1/2 x^2 , 0.5 < x <= 1.5}
300 }, 0]
301
302 To get the profile curve of the blurred step function at the rectangle
303 edge, we evaluate the indefinite integral, which is piecewise cubic:
304
305 0 x <= -1.5
306 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3 -1.5 < x <= -0.5
307 1/2 + 3/4 x - 1/3 x^3 -.5 < x <= .5
308 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3 .5 < x <= 1.5
309 1 1.5 < x
310
311 in Mathematica code:
312
313 gi[x_] := Piecewise[ {
314 { 0 , x <= -1.5 },
315 { 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3, -1.5 < x <= -0.5 },
316 { 1/2 + 3/4 x - 1/3 x^3 , -.5 < x <= .5},
317 { 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3, .5 < x <= 1.5}
318 },1]
319*/
320
321static float gaussianIntegral(float x) {
322 if (x > 1.5f) {
323 return 0.0f;
324 }
325 if (x < -1.5f) {
326 return 1.0f;
327 }
328
329 float x2 = x*x;
330 float x3 = x2*x;
331
332 if ( x > 0.5f ) {
333 return 0.5625f - (x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
334 }
335 if ( x > -0.5f ) {
336 return 0.5f - (0.75f * x - x3 / 3.0f);
337 }
338 return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x);
339}
340
341/* ComputeBlurProfile fills in an array of floating
342 point values between 0 and 255 for the profile signature of
343 a blurred half-plane with the given blur radius. Since we're
344 going to be doing screened multiplications (i.e., 1 - (1-x)(1-y))
345 all the time, we actually fill in the profile pre-inverted
346 (already done 255-x).
347*/
348
349void SkBlurMask::ComputeBlurProfile(uint8_t* profile, int size, SkScalar sigma) {
350 SkASSERT(SkScalarCeilToInt(6*sigma) == size);
351
352 int center = size >> 1;
353
354 float invr = 1.f/(2*sigma);
355
356 profile[0] = 255;
357 for (int x = 1 ; x < size ; ++x) {
358 float scaled_x = (center - x - .5f) * invr;
359 float gi = gaussianIntegral(scaled_x);
360 profile[x] = 255 - (uint8_t) (255.f * gi);
361 }
362}
363
364// TODO MAYBE: Maintain a profile cache to avoid recomputing this for
365// commonly used radii. Consider baking some of the most common blur radii
366// directly in as static data?
367
368// Implementation adapted from Michael Herf's approach:
369// http://stereopsis.com/shadowrect/
370
371uint8_t SkBlurMask::ProfileLookup(const uint8_t *profile, int loc,
372 int blurredWidth, int sharpWidth) {
373 // how far are we from the original edge?
374 int dx = SkAbs32(((loc << 1) + 1) - blurredWidth) - sharpWidth;
375 int ox = dx >> 1;
376 if (ox < 0) {
377 ox = 0;
378 }
379
380 return profile[ox];
381}
382
383void SkBlurMask::ComputeBlurredScanline(uint8_t *pixels, const uint8_t *profile,
384 unsigned int width, SkScalar sigma) {
385
386 unsigned int profile_size = SkScalarCeilToInt(6*sigma);
387 skia_private::AutoTMalloc<uint8_t> horizontalScanline(width);
388
389 unsigned int sw = width - profile_size;
390 // nearest odd number less than the profile size represents the center
391 // of the (2x scaled) profile
392 int center = ( profile_size & ~1 ) - 1;
393
394 int w = sw - center;
395
396 for (unsigned int x = 0 ; x < width ; ++x) {
397 if (profile_size <= sw) {
398 pixels[x] = ProfileLookup(profile, x, width, w);
399 } else {
400 float span = float(sw)/(2*sigma);
401 float giX = 1.5f - (x+.5f)/(2*sigma);
402 pixels[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
403 }
404 }
405}
406
408 const SkRect &src, SkBlurStyle style,
409 SkIPoint *margin, SkMaskBuilder::CreateMode createMode) {
410 int profileSize = SkScalarCeilToInt(6*sigma);
411 if (profileSize <= 0) {
412 return false; // no blur to compute
413 }
414
415 int pad = profileSize/2;
416 if (margin) {
417 margin->set( pad, pad );
418 }
419
420 dst->bounds().setLTRB(SkScalarRoundToInt(src.fLeft - pad),
421 SkScalarRoundToInt(src.fTop - pad),
422 SkScalarRoundToInt(src.fRight + pad),
423 SkScalarRoundToInt(src.fBottom + pad));
424
425 dst->rowBytes() = dst->fBounds.width();
426 dst->format() = SkMask::kA8_Format;
427 dst->image() = nullptr;
428
429 int sw = SkScalarFloorToInt(src.width());
430 int sh = SkScalarFloorToInt(src.height());
431
433 if (style == kInner_SkBlurStyle) {
434 dst->bounds() = src.round(); // restore trimmed bounds
435 dst->rowBytes() = sw;
436 }
437 return true;
438 }
439
440 AutoTMalloc<uint8_t> profile(profileSize);
441
442 ComputeBlurProfile(profile, profileSize, sigma);
443
444 size_t dstSize = dst->computeImageSize();
445 if (0 == dstSize) {
446 return false; // too big to allocate, abort
447 }
448
449 uint8_t* dp = SkMaskBuilder::AllocImage(dstSize);
450 dst->image() = dp;
451
452 int dstHeight = dst->fBounds.height();
453 int dstWidth = dst->fBounds.width();
454
455 uint8_t *outptr = dp;
456
457 AutoTMalloc<uint8_t> horizontalScanline(dstWidth);
458 AutoTMalloc<uint8_t> verticalScanline(dstHeight);
459
460 ComputeBlurredScanline(horizontalScanline, profile, dstWidth, sigma);
461 ComputeBlurredScanline(verticalScanline, profile, dstHeight, sigma);
462
463 for (int y = 0 ; y < dstHeight ; ++y) {
464 for (int x = 0 ; x < dstWidth ; x++) {
465 unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], verticalScanline[y]);
466 *(outptr++) = maskval;
467 }
468 }
469
470 if (style == kInner_SkBlurStyle) {
471 // now we allocate the "real" dst, mirror the size of src
472 size_t srcSize = (size_t)(src.width() * src.height());
473 if (0 == srcSize) {
474 return false; // too big to allocate, abort
475 }
476 dst->image() = SkMaskBuilder::AllocImage(srcSize);
477 for (int y = 0 ; y < sh ; y++) {
478 uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad;
479 uint8_t *inner_scanline = dst->image() + y*sw;
480 memcpy(inner_scanline, blur_scanline, sw);
481 }
483
484 dst->bounds() = src.round(); // restore trimmed bounds
485 dst->rowBytes() = sw;
486
487 } else if (style == kOuter_SkBlurStyle) {
488 for (int y = pad ; y < dstHeight-pad ; y++) {
489 uint8_t *dst_scanline = dp + y*dstWidth + pad;
490 memset(dst_scanline, 0, sw);
491 }
492 } else if (style == kSolid_SkBlurStyle) {
493 for (int y = pad ; y < dstHeight-pad ; y++) {
494 uint8_t *dst_scanline = dp + y*dstWidth + pad;
495 memset(dst_scanline, 0xff, sw);
496 }
497 }
498 // normal and solid styles are the same for analytic rect blurs, so don't
499 // need to handle solid specially.
500
501 return true;
502}
503
505 const SkRRect &src, SkBlurStyle style,
506 SkIPoint *margin, SkMaskBuilder::CreateMode createMode) {
507 // Temporary for now -- always fail, should cause caller to fall back
508 // to old path. Plumbing just to land API and parallelize effort.
509
510 return false;
511}
512
513// The "simple" blur is a direct implementation of separable convolution with a discrete
514// gaussian kernel. It's "ground truth" in a sense; too slow to be used, but very
515// useful for correctness comparisons.
516
518 SkBlurStyle style, SkIPoint* margin) {
519
520 if (src.fFormat != SkMask::kA8_Format) {
521 return false;
522 }
523
524 float variance = sigma * sigma;
525
526 int windowSize = SkScalarCeilToInt(sigma*6);
527 // round window size up to nearest odd number
528 windowSize |= 1;
529
530 AutoTMalloc<float> gaussWindow(windowSize);
531
532 int halfWindow = windowSize >> 1;
533
534 gaussWindow[halfWindow] = 1;
535
536 float windowSum = 1;
537 for (int x = 1 ; x <= halfWindow ; ++x) {
538 float gaussian = expf(-x*x / (2*variance));
539 gaussWindow[halfWindow + x] = gaussWindow[halfWindow-x] = gaussian;
540 windowSum += 2*gaussian;
541 }
542
543 // leave the filter un-normalized for now; we will divide by the normalization
544 // sum later;
545
546 int pad = halfWindow;
547 if (margin) {
548 margin->set( pad, pad );
549 }
550
551 dst->bounds() = src.fBounds;
552 dst->bounds().outset(pad, pad);
553
554 dst->rowBytes() = dst->fBounds.width();
555 dst->format() = SkMask::kA8_Format;
556 dst->image() = nullptr;
557
558 if (src.fImage) {
559
560 size_t dstSize = dst->computeImageSize();
561 if (0 == dstSize) {
562 return false; // too big to allocate, abort
563 }
564
565 int srcWidth = src.fBounds.width();
566 int srcHeight = src.fBounds.height();
567 int dstWidth = dst->fBounds.width();
568
569 const uint8_t* srcPixels = src.fImage;
570 uint8_t* dstPixels = SkMaskBuilder::AllocImage(dstSize);
571 SkAutoMaskFreeImage autoFreeDstPixels(dstPixels);
572
573 // do the actual blur. First, make a padded copy of the source.
574 // use double pad so we never have to check if we're outside anything
575
576 int padWidth = srcWidth + 4*pad;
577 int padHeight = srcHeight;
578 int padSize = padWidth * padHeight;
579
580 AutoTMalloc<uint8_t> padPixels(padSize);
581 memset(padPixels, 0, padSize);
582
583 for (int y = 0 ; y < srcHeight; ++y) {
584 uint8_t* padptr = padPixels + y * padWidth + 2*pad;
585 const uint8_t* srcptr = srcPixels + y * srcWidth;
586 memcpy(padptr, srcptr, srcWidth);
587 }
588
589 // blur in X, transposing the result into a temporary floating point buffer.
590 // also double-pad the intermediate result so that the second blur doesn't
591 // have to do extra conditionals.
592
593 int tmpWidth = padHeight + 4*pad;
594 int tmpHeight = padWidth - 2*pad;
595 int tmpSize = tmpWidth * tmpHeight;
596
597 AutoTMalloc<float> tmpImage(tmpSize);
598 memset(tmpImage, 0, tmpSize*sizeof(tmpImage[0]));
599
600 for (int y = 0 ; y < padHeight ; ++y) {
601 uint8_t *srcScanline = padPixels + y*padWidth;
602 for (int x = pad ; x < padWidth - pad ; ++x) {
603 float *outPixel = tmpImage + (x-pad)*tmpWidth + y + 2*pad; // transposed output
604 uint8_t *windowCenter = srcScanline + x;
605 for (int i = -pad ; i <= pad ; ++i) {
606 *outPixel += gaussWindow[pad+i]*windowCenter[i];
607 }
608 *outPixel /= windowSum;
609 }
610 }
611
612 // blur in Y; now filling in the actual desired destination. We have to do
613 // the transpose again; these transposes guarantee that we read memory in
614 // linear order.
615
616 for (int y = 0 ; y < tmpHeight ; ++y) {
617 float *srcScanline = tmpImage + y*tmpWidth;
618 for (int x = pad ; x < tmpWidth - pad ; ++x) {
619 float *windowCenter = srcScanline + x;
620 float finalValue = 0;
621 for (int i = -pad ; i <= pad ; ++i) {
622 finalValue += gaussWindow[pad+i]*windowCenter[i];
623 }
624 finalValue /= windowSum;
625 uint8_t *outPixel = dstPixels + (x-pad)*dstWidth + y; // transposed output
626 int integerPixel = int(finalValue + 0.5f);
627 *outPixel = SkTPin(SkClampPos(integerPixel), 0, 255);
628 }
629 }
630
631 dst->image() = dstPixels;
632 switch (style) {
634 break;
635 case kSolid_SkBlurStyle: {
637 dstPixels + pad*dst->fRowBytes + pad, dst->fRowBytes,
638 SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes,
639 srcWidth, srcHeight);
640 } break;
641 case kOuter_SkBlurStyle: {
643 dstPixels + pad*dst->fRowBytes + pad, dst->fRowBytes,
644 SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes,
645 srcWidth, srcHeight);
646 } break;
647 case kInner_SkBlurStyle: {
648 // now we allocate the "real" dst, mirror the size of src
649 size_t srcSize = src.computeImageSize();
650 if (0 == srcSize) {
651 return false; // too big to allocate, abort
652 }
653 dst->image() = SkMaskBuilder::AllocImage(srcSize);
654 merge_src_with_blur(dst->image(), src.fRowBytes,
655 SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes,
656 dstPixels + pad*dst->fRowBytes + pad,
657 dst->fRowBytes, srcWidth, srcHeight);
658 SkMaskBuilder::FreeImage(dstPixels);
659 } break;
660 }
661 autoFreeDstPixels.release();
662 }
663
664 if (style == kInner_SkBlurStyle) {
665 dst->bounds() = src.fBounds; // restore trimmed bounds
666 dst->rowBytes() = src.fRowBytes;
667 }
668
669 return true;
670}
static float gaussian(int x, SkScalar sigma)
Definition BlurTest.cpp:223
#define SK_ABORT(message,...)
Definition SkAssert.h:70
#define SkASSERT(cond)
Definition SkAssert.h:116
static void merge_src_with_blur(uint8_t dst[], int dstRB, AlphaIter src, int srcRB, const uint8_t blur[], int blurRB, int sw, int sh)
static const SkScalar kBLUR_SIGMA_SCALE
static void clamp_solid_with_orig(uint8_t dst[], int dstRowBytes, AlphaIter src, int srcRowBytes, int sw, int sh)
static void clamp_outer_with_orig(uint8_t dst[], int dstRowBytes, AlphaIter src, int srcRowBytes, int sw, int sh)
static float gaussianIntegral(float x)
void SkMask_FreeImage(uint8_t *image)
SkBlurStyle
Definition SkBlurTypes.h:11
@ kOuter_SkBlurStyle
nothing inside, fuzzy outside
Definition SkBlurTypes.h:14
@ kSolid_SkBlurStyle
solid inside, fuzzy outside
Definition SkBlurTypes.h:13
@ kInner_SkBlurStyle
fuzzy inside, nothing outside
Definition SkBlurTypes.h:15
@ kNormal_SkBlurStyle
fuzzy inside and outside
Definition SkBlurTypes.h:12
#define SkAlphaMul(value, alpha256)
Definition SkColorPriv.h:34
static unsigned SkAlpha255To256(U8CPU alpha)
Definition SkColorPriv.h:24
std::unique_ptr< uint8_t, SkFunctionObject< SkMaskBuilder::FreeImage > > SkAutoMaskFreeImage
Definition SkMask.h:316
static int SkClampPos(int value)
Definition SkMathPriv.h:30
static U8CPU SkMulDiv255Round(U16CPU a, U16CPU b)
Definition SkMath.h:73
static int32_t SkAbs32(int32_t value)
Definition SkSafe32.h:41
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SkScalarCeilToInt(x)
Definition SkScalar.h:36
#define SkScalarFloorToInt(x)
Definition SkScalar.h:35
static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, SkBlitter *blitter)
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition SkTPin.h:19
constexpr uint8_t SkToU8(S x)
Definition SkTo.h:22
static SkScalar center(float pos0, float pos1)
Type::kYUV Type::kRGBA() int(0.7 *637)
static void ComputeBlurProfile(uint8_t *profile, int size, SkScalar sigma)
static void ComputeBlurredScanline(uint8_t *pixels, const uint8_t *profile, unsigned int width, SkScalar sigma)
static bool BlurRect(SkScalar sigma, SkMaskBuilder *dst, const SkRect &src, SkBlurStyle, SkIPoint *margin=nullptr, SkMaskBuilder::CreateMode createMode=SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode)
static bool BlurRRect(SkScalar sigma, SkMaskBuilder *dst, const SkRRect &src, SkBlurStyle, SkIPoint *margin=nullptr, SkMaskBuilder::CreateMode createMode=SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode)
static SkScalar SK_SPI ConvertSigmaToRadius(SkScalar sigma)
static SkScalar SK_SPI ConvertRadiusToSigma(SkScalar radius)
static bool BlurGroundTruth(SkScalar sigma, SkMaskBuilder *dst, const SkMask &src, SkBlurStyle, SkIPoint *margin=nullptr)
static bool BoxBlur(SkMaskBuilder *dst, const SkMask &src, SkScalar sigma, SkBlurStyle style, SkIPoint *margin=nullptr)
static uint8_t ProfileLookup(const uint8_t *profile, int loc, int blurredWidth, int sharpWidth)
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
sk_sp< SkImage > image
Definition examples.cpp:29
float SkScalar
Definition extension.cpp:12
struct MyStruct s
double y
double x
SkScalar w
int32_t width
constexpr int32_t y() const
void set(int32_t x, int32_t y)
constexpr int32_t x() const
static constexpr SkIRect MakeEmpty()
Definition SkRect.h:45
static void FreeImage(void *image)
Definition SkMask.cpp:57
@ kJustComputeBounds_CreateMode
compute bounds and return
Definition SkMask.h:299
Format & format()
Definition SkMask.h:239
static uint8_t * AllocImage(size_t bytes, AllocType=kUninit_Alloc)
Definition SkMask.cpp:45
uint8_t *& image()
Definition SkMask.h:236
const uint32_t fRowBytes
Definition SkMask.h:43
@ kA8_Format
8bits per pixel mask (e.g. antialiasing)
Definition SkMask.h:28
@ kLCD16_Format
565 alpha for r/g/b
Definition SkMask.h:31
@ kARGB32_Format
SkPMColor.
Definition SkMask.h:30
@ kBW_Format
1bit per pixel mask (e.g. monochrome)
Definition SkMask.h:27