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
8
9 #include "SkBlurMask.h"
10 #include "SkMaskBlurFilter.h"
11 #include "SkMath.h"
12 #include "SkTemplates.h"
13 #include "SkEndian.h"
14
15
16 // This constant approximates the scaling done in the software path's
17 // "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
18 // IMHO, it actually should be 1: we blur "less" than we should do
19 // according to the CSS and canvas specs, simply because Safari does the same.
20 // Firefox used to do the same too, until 4.0 where they fixed it. So at some
21 // point we should probably get rid of these scaling constants and rebaseline
22 // all the blur tests.
23 static const SkScalar kBLUR_SIGMA_SCALE = 0.57735f;
24
ConvertRadiusToSigma(SkScalar radius)25 SkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar radius) {
26 return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
27 }
28
ConvertSigmaToRadius(SkScalar sigma)29 SkScalar SkBlurMask::ConvertSigmaToRadius(SkScalar sigma) {
30 return sigma > 0.5f ? (sigma - 0.5f) / kBLUR_SIGMA_SCALE : 0.0f;
31 }
32
33 #define UNROLL_SEPARABLE_LOOPS
34
35 /**
36 * This function performs a box blur in X, of the given radius. If the
37 * "transpose" parameter is true, it will transpose the pixels on write,
38 * such that X and Y are swapped. Reads are always performed from contiguous
39 * memory in X, for speed. The destination buffer (dst) must be at least
40 * (width + leftRadius + rightRadius) * height bytes in size.
41 *
42 * This is what the inner loop looks like before unrolling, and with the two
43 * cases broken out separately (width < diameter, width >= diameter):
44 *
45 * if (width < diameter) {
46 * for (int x = 0; x < width; ++x) {
47 * sum += *right++;
48 * *dptr = (sum * scale + half) >> 24;
49 * dptr += dst_x_stride;
50 * }
51 * for (int x = width; x < diameter; ++x) {
52 * *dptr = (sum * scale + half) >> 24;
53 * dptr += dst_x_stride;
54 * }
55 * for (int x = 0; x < width; ++x) {
56 * *dptr = (sum * scale + half) >> 24;
57 * sum -= *left++;
58 * dptr += dst_x_stride;
59 * }
60 * } else {
61 * for (int x = 0; x < diameter; ++x) {
62 * sum += *right++;
63 * *dptr = (sum * scale + half) >> 24;
64 * dptr += dst_x_stride;
65 * }
66 * for (int x = diameter; x < width; ++x) {
67 * sum += *right++;
68 * *dptr = (sum * scale + half) >> 24;
69 * sum -= *left++;
70 * dptr += dst_x_stride;
71 * }
72 * for (int x = 0; x < diameter; ++x) {
73 * *dptr = (sum * scale + half) >> 24;
74 * sum -= *left++;
75 * dptr += dst_x_stride;
76 * }
77 * }
78 */
79 template <bool Transpose>
boxBlur(const uint8_t * src,int src_y_stride,uint8_t * dst,int leftRadius,int rightRadius,int width,int height)80 static int boxBlur(const uint8_t* src, int src_y_stride, uint8_t* dst,
81 int leftRadius, int rightRadius, int width, int height)
82 {
83 int diameter = leftRadius + rightRadius;
84 int kernelSize = diameter + 1;
85 int border = SkMin32(width, diameter);
86 uint32_t scale = (1 << 24) / kernelSize;
87 int new_width = width + SkMax32(leftRadius, rightRadius) * 2;
88 int dst_x_stride = Transpose ? height : 1;
89 int dst_y_stride = Transpose ? 1 : new_width;
90 uint32_t half = 1 << 23;
91 for (int y = 0; y < height; ++y) {
92 uint32_t sum = 0;
93 uint8_t* dptr = dst + y * dst_y_stride;
94 const uint8_t* right = src + y * src_y_stride;
95 const uint8_t* left = right;
96 for (int x = 0; x < rightRadius - leftRadius; x++) {
97 *dptr = 0;
98 dptr += dst_x_stride;
99 }
100 #define LEFT_BORDER_ITER \
101 sum += *right++; \
102 *dptr = (sum * scale + half) >> 24; \
103 dptr += dst_x_stride;
104
105 int x = 0;
106 #ifdef UNROLL_SEPARABLE_LOOPS
107 for (; x < border - 16; x += 16) {
108 LEFT_BORDER_ITER
109 LEFT_BORDER_ITER
110 LEFT_BORDER_ITER
111 LEFT_BORDER_ITER
112 LEFT_BORDER_ITER
113 LEFT_BORDER_ITER
114 LEFT_BORDER_ITER
115 LEFT_BORDER_ITER
116 LEFT_BORDER_ITER
117 LEFT_BORDER_ITER
118 LEFT_BORDER_ITER
119 LEFT_BORDER_ITER
120 LEFT_BORDER_ITER
121 LEFT_BORDER_ITER
122 LEFT_BORDER_ITER
123 LEFT_BORDER_ITER
124 }
125 #endif
126 for (; x < border; ++x) {
127 LEFT_BORDER_ITER
128 }
129 #undef LEFT_BORDER_ITER
130 #define TRIVIAL_ITER \
131 *dptr = (sum * scale + half) >> 24; \
132 dptr += dst_x_stride;
133 x = width;
134 #ifdef UNROLL_SEPARABLE_LOOPS
135 for (; x < diameter - 16; x += 16) {
136 TRIVIAL_ITER
137 TRIVIAL_ITER
138 TRIVIAL_ITER
139 TRIVIAL_ITER
140 TRIVIAL_ITER
141 TRIVIAL_ITER
142 TRIVIAL_ITER
143 TRIVIAL_ITER
144 TRIVIAL_ITER
145 TRIVIAL_ITER
146 TRIVIAL_ITER
147 TRIVIAL_ITER
148 TRIVIAL_ITER
149 TRIVIAL_ITER
150 TRIVIAL_ITER
151 TRIVIAL_ITER
152 }
153 #endif
154 for (; x < diameter; ++x) {
155 TRIVIAL_ITER
156 }
157 #undef TRIVIAL_ITER
158 #define CENTER_ITER \
159 sum += *right++; \
160 *dptr = (sum * scale + half) >> 24; \
161 sum -= *left++; \
162 dptr += dst_x_stride;
163
164 x = diameter;
165 #ifdef UNROLL_SEPARABLE_LOOPS
166 for (; x < width - 16; x += 16) {
167 CENTER_ITER
168 CENTER_ITER
169 CENTER_ITER
170 CENTER_ITER
171 CENTER_ITER
172 CENTER_ITER
173 CENTER_ITER
174 CENTER_ITER
175 CENTER_ITER
176 CENTER_ITER
177 CENTER_ITER
178 CENTER_ITER
179 CENTER_ITER
180 CENTER_ITER
181 CENTER_ITER
182 CENTER_ITER
183 }
184 #endif
185 for (; x < width; ++x) {
186 CENTER_ITER
187 }
188 #undef CENTER_ITER
189 #define RIGHT_BORDER_ITER \
190 *dptr = (sum * scale + half) >> 24; \
191 sum -= *left++; \
192 dptr += dst_x_stride;
193
194 x = 0;
195 #ifdef UNROLL_SEPARABLE_LOOPS
196 for (; x < border - 16; x += 16) {
197 RIGHT_BORDER_ITER
198 RIGHT_BORDER_ITER
199 RIGHT_BORDER_ITER
200 RIGHT_BORDER_ITER
201 RIGHT_BORDER_ITER
202 RIGHT_BORDER_ITER
203 RIGHT_BORDER_ITER
204 RIGHT_BORDER_ITER
205 RIGHT_BORDER_ITER
206 RIGHT_BORDER_ITER
207 RIGHT_BORDER_ITER
208 RIGHT_BORDER_ITER
209 RIGHT_BORDER_ITER
210 RIGHT_BORDER_ITER
211 RIGHT_BORDER_ITER
212 RIGHT_BORDER_ITER
213 }
214 #endif
215 for (; x < border; ++x) {
216 RIGHT_BORDER_ITER
217 }
218 #undef RIGHT_BORDER_ITER
219 for (int x = 0; x < leftRadius - rightRadius; ++x) {
220 *dptr = 0;
221 dptr += dst_x_stride;
222 }
223 SkASSERT(sum == 0);
224 }
225 return new_width;
226 }
227
228 /**
229 * This variant of the box blur handles blurring of non-integer radii. It
230 * keeps two running sums: an outer sum for the rounded-up kernel radius, and
231 * an inner sum for the rounded-down kernel radius. For each pixel, it linearly
232 * interpolates between them. In float this would be:
233 * outer_weight * outer_sum / kernelSize +
234 * (1.0 - outer_weight) * innerSum / (kernelSize - 2)
235 *
236 * This is what the inner loop looks like before unrolling, and with the two
237 * cases broken out separately (width < diameter, width >= diameter):
238 *
239 * if (width < diameter) {
240 * for (int x = 0; x < width; x++) {
241 * inner_sum = outer_sum;
242 * outer_sum += *right++;
243 * *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
244 * dptr += dst_x_stride;
245 * }
246 * for (int x = width; x < diameter; ++x) {
247 * *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
248 * dptr += dst_x_stride;
249 * }
250 * for (int x = 0; x < width; x++) {
251 * inner_sum = outer_sum - *left++;
252 * *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
253 * dptr += dst_x_stride;
254 * outer_sum = inner_sum;
255 * }
256 * } else {
257 * for (int x = 0; x < diameter; x++) {
258 * inner_sum = outer_sum;
259 * outer_sum += *right++;
260 * *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
261 * dptr += dst_x_stride;
262 * }
263 * for (int x = diameter; x < width; ++x) {
264 * inner_sum = outer_sum - *left;
265 * outer_sum += *right++;
266 * *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
267 * dptr += dst_x_stride;
268 * outer_sum -= *left++;
269 * }
270 * for (int x = 0; x < diameter; x++) {
271 * inner_sum = outer_sum - *left++;
272 * *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
273 * dptr += dst_x_stride;
274 * outer_sum = inner_sum;
275 * }
276 * }
277 * }
278 * return new_width;
279 */
280
281 template <bool Transpose>
boxBlurInterp(const uint8_t * src,int src_y_stride,uint8_t * dst,int radius,int width,int height,uint8_t outer_weight)282 static int boxBlurInterp(const uint8_t* src, int src_y_stride, uint8_t* dst,
283 int radius, int width, int height,
284 uint8_t outer_weight)
285 {
286 int diameter = radius * 2;
287 int kernelSize = diameter + 1;
288 int border = SkMin32(width, diameter);
289 int inner_weight = 255 - outer_weight;
290 outer_weight += outer_weight >> 7;
291 inner_weight += inner_weight >> 7;
292 uint32_t outer_scale = (outer_weight << 16) / kernelSize;
293 uint32_t inner_scale = (inner_weight << 16) / (kernelSize - 2);
294 uint32_t half = 1 << 23;
295 int new_width = width + diameter;
296 int dst_x_stride = Transpose ? height : 1;
297 int dst_y_stride = Transpose ? 1 : new_width;
298 for (int y = 0; y < height; ++y) {
299 uint32_t outer_sum = 0, inner_sum = 0;
300 uint8_t* dptr = dst + y * dst_y_stride;
301 const uint8_t* right = src + y * src_y_stride;
302 const uint8_t* left = right;
303 int x = 0;
304
305 #define LEFT_BORDER_ITER \
306 inner_sum = outer_sum; \
307 outer_sum += *right++; \
308 *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24; \
309 dptr += dst_x_stride;
310
311 #ifdef UNROLL_SEPARABLE_LOOPS
312 for (;x < border - 16; x += 16) {
313 LEFT_BORDER_ITER
314 LEFT_BORDER_ITER
315 LEFT_BORDER_ITER
316 LEFT_BORDER_ITER
317 LEFT_BORDER_ITER
318 LEFT_BORDER_ITER
319 LEFT_BORDER_ITER
320 LEFT_BORDER_ITER
321 LEFT_BORDER_ITER
322 LEFT_BORDER_ITER
323 LEFT_BORDER_ITER
324 LEFT_BORDER_ITER
325 LEFT_BORDER_ITER
326 LEFT_BORDER_ITER
327 LEFT_BORDER_ITER
328 LEFT_BORDER_ITER
329 }
330 #endif
331
332 for (;x < border; ++x) {
333 LEFT_BORDER_ITER
334 }
335 #undef LEFT_BORDER_ITER
336 for (int x = width; x < diameter; ++x) {
337 *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24;
338 dptr += dst_x_stride;
339 }
340 x = diameter;
341
342 #define CENTER_ITER \
343 inner_sum = outer_sum - *left; \
344 outer_sum += *right++; \
345 *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24; \
346 dptr += dst_x_stride; \
347 outer_sum -= *left++;
348
349 #ifdef UNROLL_SEPARABLE_LOOPS
350 for (; x < width - 16; x += 16) {
351 CENTER_ITER
352 CENTER_ITER
353 CENTER_ITER
354 CENTER_ITER
355 CENTER_ITER
356 CENTER_ITER
357 CENTER_ITER
358 CENTER_ITER
359 CENTER_ITER
360 CENTER_ITER
361 CENTER_ITER
362 CENTER_ITER
363 CENTER_ITER
364 CENTER_ITER
365 CENTER_ITER
366 CENTER_ITER
367 }
368 #endif
369 for (; x < width; ++x) {
370 CENTER_ITER
371 }
372 #undef CENTER_ITER
373
374 #define RIGHT_BORDER_ITER \
375 inner_sum = outer_sum - *left++; \
376 *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24; \
377 dptr += dst_x_stride; \
378 outer_sum = inner_sum;
379
380 x = 0;
381 #ifdef UNROLL_SEPARABLE_LOOPS
382 for (; x < border - 16; x += 16) {
383 RIGHT_BORDER_ITER
384 RIGHT_BORDER_ITER
385 RIGHT_BORDER_ITER
386 RIGHT_BORDER_ITER
387 RIGHT_BORDER_ITER
388 RIGHT_BORDER_ITER
389 RIGHT_BORDER_ITER
390 RIGHT_BORDER_ITER
391 RIGHT_BORDER_ITER
392 RIGHT_BORDER_ITER
393 RIGHT_BORDER_ITER
394 RIGHT_BORDER_ITER
395 RIGHT_BORDER_ITER
396 RIGHT_BORDER_ITER
397 RIGHT_BORDER_ITER
398 RIGHT_BORDER_ITER
399 }
400 #endif
401 for (; x < border; ++x) {
402 RIGHT_BORDER_ITER
403 }
404 #undef RIGHT_BORDER_ITER
405 SkASSERT(outer_sum == 0 && inner_sum == 0);
406 }
407 return new_width;
408 }
409
410
411
412 #include "SkColorPriv.h"
413
merge_src_with_blur(uint8_t dst[],int dstRB,const uint8_t src[],int srcRB,const uint8_t blur[],int blurRB,int sw,int sh)414 static void merge_src_with_blur(uint8_t dst[], int dstRB,
415 const uint8_t src[], int srcRB,
416 const uint8_t blur[], int blurRB,
417 int sw, int sh) {
418 dstRB -= sw;
419 srcRB -= sw;
420 blurRB -= sw;
421 while (--sh >= 0) {
422 for (int x = sw - 1; x >= 0; --x) {
423 *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*src)));
424 dst += 1;
425 src += 1;
426 blur += 1;
427 }
428 dst += dstRB;
429 src += srcRB;
430 blur += blurRB;
431 }
432 }
433
clamp_with_orig(uint8_t dst[],int dstRowBytes,const uint8_t src[],int srcRowBytes,int sw,int sh,SkBlurStyle style)434 static void clamp_with_orig(uint8_t dst[], int dstRowBytes,
435 const uint8_t src[], int srcRowBytes,
436 int sw, int sh,
437 SkBlurStyle style) {
438 int x;
439 while (--sh >= 0) {
440 switch (style) {
441 case kSolid_SkBlurStyle:
442 for (x = sw - 1; x >= 0; --x) {
443 int s = *src;
444 int d = *dst;
445 *dst = SkToU8(s + d - SkMulDiv255Round(s, d));
446 dst += 1;
447 src += 1;
448 }
449 break;
450 case kOuter_SkBlurStyle:
451 for (x = sw - 1; x >= 0; --x) {
452 if (*src) {
453 *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
454 }
455 dst += 1;
456 src += 1;
457 }
458 break;
459 default:
460 SkDEBUGFAIL("Unexpected blur style here");
461 break;
462 }
463 dst += dstRowBytes - sw;
464 src += srcRowBytes - sw;
465 }
466 }
467
468 ///////////////////////////////////////////////////////////////////////////////
469
470 // we use a local function to wrap the class static method to work around
471 // a bug in gcc98
472 void SkMask_FreeImage(uint8_t* image);
SkMask_FreeImage(uint8_t * image)473 void SkMask_FreeImage(uint8_t* image) {
474 SkMask::FreeImage(image);
475 }
476
BoxBlur(SkMask * dst,const SkMask & src,SkScalar sigma,SkBlurStyle style,SkBlurQuality quality,SkIPoint * margin,bool force_quality)477 bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src,
478 SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
479 SkIPoint* margin, bool force_quality) {
480
481 if (src.fFormat != SkMask::kA8_Format) {
482 return false;
483 }
484
485 SkIPoint border;
486
487 #ifdef SK_SUPPORT_LEGACY_MASK_BLUR
488
489 auto get_adjusted_radii = [](SkScalar passRadius, int *loRadius, int *hiRadius) {
490 *loRadius = *hiRadius = SkScalarCeilToInt(passRadius);
491 if (SkIntToScalar(*hiRadius) - passRadius > 0.5f) {
492 *loRadius = *hiRadius - 1;
493 }
494 };
495
496 // Force high quality off for small radii (performance)
497 if (!force_quality && sigma <= SkIntToScalar(2)) {
498 quality = kLow_SkBlurQuality;
499 }
500
501 SkScalar passRadius;
502 if (kHigh_SkBlurQuality == quality) {
503 // For the high quality path the 3 pass box blur kernel width is
504 // 6*rad+1 while the full Gaussian width is 6*sigma.
505 passRadius = sigma - (1 / 6.0f);
506 } else {
507 // For the low quality path we only attempt to cover 3*sigma of the
508 // Gaussian blur area (1.5*sigma on each side). The single pass box
509 // blur's kernel size is 2*rad+1.
510 passRadius = 1.5f * sigma - 0.5f;
511 }
512
513 // highQuality: use three box blur passes as a cheap way
514 // to approximate a Gaussian blur
515 int passCount = (kHigh_SkBlurQuality == quality) ? 3 : 1;
516
517 int rx = SkScalarCeilToInt(passRadius);
518 int outerWeight = 255 - SkScalarRoundToInt((SkIntToScalar(rx) - passRadius) * 255);
519
520 SkASSERT(rx >= 0);
521 SkASSERT((unsigned)outerWeight <= 255);
522 if (rx <= 0) {
523 return false;
524 }
525
526 int ry = rx; // only do square blur for now
527
528 int padx = passCount * rx;
529 int pady = passCount * ry;
530
531 border = {padx, pady};
532
533 dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady,
534 src.fBounds.fRight + padx, src.fBounds.fBottom + pady);
535
536 dst->fRowBytes = dst->fBounds.width();
537 dst->fFormat = SkMask::kA8_Format;
538 dst->fImage = nullptr;
539
540 if (src.fImage) {
541 size_t dstSize = dst->computeImageSize();
542 if (0 == dstSize) {
543 return false; // too big to allocate, abort
544 }
545
546 int sw = src.fBounds.width();
547 int sh = src.fBounds.height();
548 const uint8_t* sp = src.fImage;
549 uint8_t* dp = SkMask::AllocImage(dstSize);
550 SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);
551
552 // build the blurry destination
553 SkAutoTMalloc<uint8_t> tmpBuffer(dstSize);
554 uint8_t* tp = tmpBuffer.get();
555 int w = sw, h = sh;
556
557 if (outerWeight == 255) {
558 int loRadius, hiRadius;
559 get_adjusted_radii(passRadius, &loRadius, &hiRadius);
560 if (kHigh_SkBlurQuality == quality) {
561 // Do three X blurs, with a transpose on the final one.
562 w = boxBlur<false>(sp, src.fRowBytes, tp, loRadius, hiRadius, w, h);
563 w = boxBlur<false>(tp, w, dp, hiRadius, loRadius, w, h);
564 w = boxBlur<true>(dp, w, tp, hiRadius, hiRadius, w, h);
565 // Do three Y blurs, with a transpose on the final one.
566 h = boxBlur<false>(tp, h, dp, loRadius, hiRadius, h, w);
567 h = boxBlur<false>(dp, h, tp, hiRadius, loRadius, h, w);
568 h = boxBlur<true>(tp, h, dp, hiRadius, hiRadius, h, w);
569 } else {
570 w = boxBlur<true>(sp, src.fRowBytes, tp, rx, rx, w, h);
571 h = boxBlur<true>(tp, h, dp, ry, ry, h, w);
572 }
573 } else {
574 if (kHigh_SkBlurQuality == quality) {
575 // Do three X blurs, with a transpose on the final one.
576 w = boxBlurInterp<false>(sp, src.fRowBytes, tp, rx, w, h, outerWeight);
577 w = boxBlurInterp<false>(tp, w, dp, rx, w, h, outerWeight);
578 w = boxBlurInterp<true>(dp, w, tp, rx, w, h, outerWeight);
579 // Do three Y blurs, with a transpose on the final one.
580 h = boxBlurInterp<false>(tp, h, dp, ry, h, w, outerWeight);
581 h = boxBlurInterp<false>(dp, h, tp, ry, h, w, outerWeight);
582 h = boxBlurInterp<true>(tp, h, dp, ry, h, w, outerWeight);
583 } else {
584 w = boxBlurInterp<true>(sp, src.fRowBytes, tp, rx, w, h, outerWeight);
585 h = boxBlurInterp<true>(tp, h, dp, ry, h, w, outerWeight);
586 }
587 }
588
589 dst->fImage = autoCall.release();
590 }
591 #else
592 SkMaskBlurFilter blurFilter{sigma, sigma};
593 border = blurFilter.blur(src, dst);
594 #endif // SK_SUPPORT_LEGACY_MASK_BLUR
595
596 if (src.fImage != nullptr) {
597 // if need be, alloc the "real" dst (same size as src) and copy/merge
598 // the blur into it (applying the src)
599 if (style == kInner_SkBlurStyle) {
600 // now we allocate the "real" dst, mirror the size of src
601 size_t srcSize = src.computeImageSize();
602 if (0 == srcSize) {
603 return false; // too big to allocate, abort
604 }
605 auto blur = dst->fImage;
606 dst->fImage = SkMask::AllocImage(srcSize);
607 auto blurStart = &blur[border.x() + border.y() * dst->fRowBytes];
608 merge_src_with_blur(dst->fImage, src.fRowBytes,
609 src.fImage, src.fRowBytes,
610 blurStart,
611 dst->fRowBytes,
612 src.fBounds.width(), src.fBounds.height());
613 SkMask::FreeImage(blur);
614 } else if (style != kNormal_SkBlurStyle) {
615 auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes];
616 clamp_with_orig(dstStart,
617 dst->fRowBytes, src.fImage, src.fRowBytes,
618 src.fBounds.width(), src.fBounds.height(), style);
619 }
620 }
621
622 if (style == kInner_SkBlurStyle) {
623 dst->fBounds = src.fBounds; // restore trimmed bounds
624 dst->fRowBytes = src.fRowBytes;
625 }
626
627 if (margin != nullptr) {
628 *margin = border;
629 }
630
631 return true;
632 }
633
634 /* Convolving a box with itself three times results in a piecewise
635 quadratic function:
636
637 0 x <= -1.5
638 9/8 + 3/2 x + 1/2 x^2 -1.5 < x <= -.5
639 3/4 - x^2 -.5 < x <= .5
640 9/8 - 3/2 x + 1/2 x^2 0.5 < x <= 1.5
641 0 1.5 < x
642
643 Mathematica:
644
645 g[x_] := Piecewise [ {
646 {9/8 + 3/2 x + 1/2 x^2 , -1.5 < x <= -.5},
647 {3/4 - x^2 , -.5 < x <= .5},
648 {9/8 - 3/2 x + 1/2 x^2 , 0.5 < x <= 1.5}
649 }, 0]
650
651 To get the profile curve of the blurred step function at the rectangle
652 edge, we evaluate the indefinite integral, which is piecewise cubic:
653
654 0 x <= -1.5
655 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3 -1.5 < x <= -0.5
656 1/2 + 3/4 x - 1/3 x^3 -.5 < x <= .5
657 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3 .5 < x <= 1.5
658 1 1.5 < x
659
660 in Mathematica code:
661
662 gi[x_] := Piecewise[ {
663 { 0 , x <= -1.5 },
664 { 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3, -1.5 < x <= -0.5 },
665 { 1/2 + 3/4 x - 1/3 x^3 , -.5 < x <= .5},
666 { 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3, .5 < x <= 1.5}
667 },1]
668 */
669
gaussianIntegral(float x)670 static float gaussianIntegral(float x) {
671 if (x > 1.5f) {
672 return 0.0f;
673 }
674 if (x < -1.5f) {
675 return 1.0f;
676 }
677
678 float x2 = x*x;
679 float x3 = x2*x;
680
681 if ( x > 0.5f ) {
682 return 0.5625f - (x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
683 }
684 if ( x > -0.5f ) {
685 return 0.5f - (0.75f * x - x3 / 3.0f);
686 }
687 return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x);
688 }
689
690 /* ComputeBlurProfile allocates and fills in an array of floating
691 point values between 0 and 255 for the profile signature of
692 a blurred half-plane with the given blur radius. Since we're
693 going to be doing screened multiplications (i.e., 1 - (1-x)(1-y))
694 all the time, we actually fill in the profile pre-inverted
695 (already done 255-x).
696
697 It's the responsibility of the caller to delete the
698 memory returned in profile_out.
699 */
700
ComputeBlurProfile(SkScalar sigma)701 uint8_t* SkBlurMask::ComputeBlurProfile(SkScalar sigma) {
702 int size = SkScalarCeilToInt(6*sigma);
703
704 int center = size >> 1;
705 uint8_t* profile = new uint8_t[size];
706
707 float invr = 1.f/(2*sigma);
708
709 profile[0] = 255;
710 for (int x = 1 ; x < size ; ++x) {
711 float scaled_x = (center - x - .5f) * invr;
712 float gi = gaussianIntegral(scaled_x);
713 profile[x] = 255 - (uint8_t) (255.f * gi);
714 }
715
716 return profile;
717 }
718
719 // TODO MAYBE: Maintain a profile cache to avoid recomputing this for
720 // commonly used radii. Consider baking some of the most common blur radii
721 // directly in as static data?
722
723 // Implementation adapted from Michael Herf's approach:
724 // http://stereopsis.com/shadowrect/
725
ProfileLookup(const uint8_t * profile,int loc,int blurred_width,int sharp_width)726 uint8_t SkBlurMask::ProfileLookup(const uint8_t *profile, int loc, int blurred_width, int sharp_width) {
727 int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge?
728 int ox = dx >> 1;
729 if (ox < 0) {
730 ox = 0;
731 }
732
733 return profile[ox];
734 }
735
ComputeBlurredScanline(uint8_t * pixels,const uint8_t * profile,unsigned int width,SkScalar sigma)736 void SkBlurMask::ComputeBlurredScanline(uint8_t *pixels, const uint8_t *profile,
737 unsigned int width, SkScalar sigma) {
738
739 unsigned int profile_size = SkScalarCeilToInt(6*sigma);
740 SkAutoTMalloc<uint8_t> horizontalScanline(width);
741
742 unsigned int sw = width - profile_size;
743 // nearest odd number less than the profile size represents the center
744 // of the (2x scaled) profile
745 int center = ( profile_size & ~1 ) - 1;
746
747 int w = sw - center;
748
749 for (unsigned int x = 0 ; x < width ; ++x) {
750 if (profile_size <= sw) {
751 pixels[x] = ProfileLookup(profile, x, width, w);
752 } else {
753 float span = float(sw)/(2*sigma);
754 float giX = 1.5f - (x+.5f)/(2*sigma);
755 pixels[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
756 }
757 }
758 }
759
BlurRect(SkScalar sigma,SkMask * dst,const SkRect & src,SkBlurStyle style,SkIPoint * margin,SkMask::CreateMode createMode)760 bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
761 const SkRect &src, SkBlurStyle style,
762 SkIPoint *margin, SkMask::CreateMode createMode) {
763 int profile_size = SkScalarCeilToInt(6*sigma);
764
765 int pad = profile_size/2;
766 if (margin) {
767 margin->set( pad, pad );
768 }
769
770 dst->fBounds.set(SkScalarRoundToInt(src.fLeft - pad),
771 SkScalarRoundToInt(src.fTop - pad),
772 SkScalarRoundToInt(src.fRight + pad),
773 SkScalarRoundToInt(src.fBottom + pad));
774
775 dst->fRowBytes = dst->fBounds.width();
776 dst->fFormat = SkMask::kA8_Format;
777 dst->fImage = nullptr;
778
779 int sw = SkScalarFloorToInt(src.width());
780 int sh = SkScalarFloorToInt(src.height());
781
782 if (createMode == SkMask::kJustComputeBounds_CreateMode) {
783 if (style == kInner_SkBlurStyle) {
784 dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
785 SkScalarRoundToInt(src.fTop),
786 SkScalarRoundToInt(src.fRight),
787 SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
788 dst->fRowBytes = sw;
789 }
790 return true;
791 }
792
793 std::unique_ptr<uint8_t[]> profile(ComputeBlurProfile(sigma));
794
795 size_t dstSize = dst->computeImageSize();
796 if (0 == dstSize) {
797 return false; // too big to allocate, abort
798 }
799
800 uint8_t* dp = SkMask::AllocImage(dstSize);
801
802 dst->fImage = dp;
803
804 int dstHeight = dst->fBounds.height();
805 int dstWidth = dst->fBounds.width();
806
807 uint8_t *outptr = dp;
808
809 SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth);
810 SkAutoTMalloc<uint8_t> verticalScanline(dstHeight);
811
812 ComputeBlurredScanline(horizontalScanline, profile.get(), dstWidth, sigma);
813 ComputeBlurredScanline(verticalScanline, profile.get(), dstHeight, sigma);
814
815 for (int y = 0 ; y < dstHeight ; ++y) {
816 for (int x = 0 ; x < dstWidth ; x++) {
817 unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], verticalScanline[y]);
818 *(outptr++) = maskval;
819 }
820 }
821
822 if (style == kInner_SkBlurStyle) {
823 // now we allocate the "real" dst, mirror the size of src
824 size_t srcSize = (size_t)(src.width() * src.height());
825 if (0 == srcSize) {
826 return false; // too big to allocate, abort
827 }
828 dst->fImage = SkMask::AllocImage(srcSize);
829 for (int y = 0 ; y < sh ; y++) {
830 uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad;
831 uint8_t *inner_scanline = dst->fImage + y*sw;
832 memcpy(inner_scanline, blur_scanline, sw);
833 }
834 SkMask::FreeImage(dp);
835
836 dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
837 SkScalarRoundToInt(src.fTop),
838 SkScalarRoundToInt(src.fRight),
839 SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
840 dst->fRowBytes = sw;
841
842 } else if (style == kOuter_SkBlurStyle) {
843 for (int y = pad ; y < dstHeight-pad ; y++) {
844 uint8_t *dst_scanline = dp + y*dstWidth + pad;
845 memset(dst_scanline, 0, sw);
846 }
847 } else if (style == kSolid_SkBlurStyle) {
848 for (int y = pad ; y < dstHeight-pad ; y++) {
849 uint8_t *dst_scanline = dp + y*dstWidth + pad;
850 memset(dst_scanline, 0xff, sw);
851 }
852 }
853 // normal and solid styles are the same for analytic rect blurs, so don't
854 // need to handle solid specially.
855
856 return true;
857 }
858
BlurRRect(SkScalar sigma,SkMask * dst,const SkRRect & src,SkBlurStyle style,SkIPoint * margin,SkMask::CreateMode createMode)859 bool SkBlurMask::BlurRRect(SkScalar sigma, SkMask *dst,
860 const SkRRect &src, SkBlurStyle style,
861 SkIPoint *margin, SkMask::CreateMode createMode) {
862 // Temporary for now -- always fail, should cause caller to fall back
863 // to old path. Plumbing just to land API and parallelize effort.
864
865 return false;
866 }
867
868 // The "simple" blur is a direct implementation of separable convolution with a discrete
869 // gaussian kernel. It's "ground truth" in a sense; too slow to be used, but very
870 // useful for correctness comparisons.
871
BlurGroundTruth(SkScalar sigma,SkMask * dst,const SkMask & src,SkBlurStyle style,SkIPoint * margin)872 bool SkBlurMask::BlurGroundTruth(SkScalar sigma, SkMask* dst, const SkMask& src,
873 SkBlurStyle style, SkIPoint* margin) {
874
875 if (src.fFormat != SkMask::kA8_Format) {
876 return false;
877 }
878
879 float variance = sigma * sigma;
880
881 int windowSize = SkScalarCeilToInt(sigma*6);
882 // round window size up to nearest odd number
883 windowSize |= 1;
884
885 SkAutoTMalloc<float> gaussWindow(windowSize);
886
887 int halfWindow = windowSize >> 1;
888
889 gaussWindow[halfWindow] = 1;
890
891 float windowSum = 1;
892 for (int x = 1 ; x <= halfWindow ; ++x) {
893 float gaussian = expf(-x*x / (2*variance));
894 gaussWindow[halfWindow + x] = gaussWindow[halfWindow-x] = gaussian;
895 windowSum += 2*gaussian;
896 }
897
898 // leave the filter un-normalized for now; we will divide by the normalization
899 // sum later;
900
901 int pad = halfWindow;
902 if (margin) {
903 margin->set( pad, pad );
904 }
905
906 dst->fBounds = src.fBounds;
907 dst->fBounds.outset(pad, pad);
908
909 dst->fRowBytes = dst->fBounds.width();
910 dst->fFormat = SkMask::kA8_Format;
911 dst->fImage = nullptr;
912
913 if (src.fImage) {
914
915 size_t dstSize = dst->computeImageSize();
916 if (0 == dstSize) {
917 return false; // too big to allocate, abort
918 }
919
920 int srcWidth = src.fBounds.width();
921 int srcHeight = src.fBounds.height();
922 int dstWidth = dst->fBounds.width();
923
924 const uint8_t* srcPixels = src.fImage;
925 uint8_t* dstPixels = SkMask::AllocImage(dstSize);
926 SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dstPixels);
927
928 // do the actual blur. First, make a padded copy of the source.
929 // use double pad so we never have to check if we're outside anything
930
931 int padWidth = srcWidth + 4*pad;
932 int padHeight = srcHeight;
933 int padSize = padWidth * padHeight;
934
935 SkAutoTMalloc<uint8_t> padPixels(padSize);
936 memset(padPixels, 0, padSize);
937
938 for (int y = 0 ; y < srcHeight; ++y) {
939 uint8_t* padptr = padPixels + y * padWidth + 2*pad;
940 const uint8_t* srcptr = srcPixels + y * srcWidth;
941 memcpy(padptr, srcptr, srcWidth);
942 }
943
944 // blur in X, transposing the result into a temporary floating point buffer.
945 // also double-pad the intermediate result so that the second blur doesn't
946 // have to do extra conditionals.
947
948 int tmpWidth = padHeight + 4*pad;
949 int tmpHeight = padWidth - 2*pad;
950 int tmpSize = tmpWidth * tmpHeight;
951
952 SkAutoTMalloc<float> tmpImage(tmpSize);
953 memset(tmpImage, 0, tmpSize*sizeof(tmpImage[0]));
954
955 for (int y = 0 ; y < padHeight ; ++y) {
956 uint8_t *srcScanline = padPixels + y*padWidth;
957 for (int x = pad ; x < padWidth - pad ; ++x) {
958 float *outPixel = tmpImage + (x-pad)*tmpWidth + y + 2*pad; // transposed output
959 uint8_t *windowCenter = srcScanline + x;
960 for (int i = -pad ; i <= pad ; ++i) {
961 *outPixel += gaussWindow[pad+i]*windowCenter[i];
962 }
963 *outPixel /= windowSum;
964 }
965 }
966
967 // blur in Y; now filling in the actual desired destination. We have to do
968 // the transpose again; these transposes guarantee that we read memory in
969 // linear order.
970
971 for (int y = 0 ; y < tmpHeight ; ++y) {
972 float *srcScanline = tmpImage + y*tmpWidth;
973 for (int x = pad ; x < tmpWidth - pad ; ++x) {
974 float *windowCenter = srcScanline + x;
975 float finalValue = 0;
976 for (int i = -pad ; i <= pad ; ++i) {
977 finalValue += gaussWindow[pad+i]*windowCenter[i];
978 }
979 finalValue /= windowSum;
980 uint8_t *outPixel = dstPixels + (x-pad)*dstWidth + y; // transposed output
981 int integerPixel = int(finalValue + 0.5f);
982 *outPixel = SkClampMax( SkClampPos(integerPixel), 255 );
983 }
984 }
985
986 dst->fImage = dstPixels;
987 // if need be, alloc the "real" dst (same size as src) and copy/merge
988 // the blur into it (applying the src)
989 if (style == kInner_SkBlurStyle) {
990 // now we allocate the "real" dst, mirror the size of src
991 size_t srcSize = src.computeImageSize();
992 if (0 == srcSize) {
993 return false; // too big to allocate, abort
994 }
995 dst->fImage = SkMask::AllocImage(srcSize);
996 merge_src_with_blur(dst->fImage, src.fRowBytes,
997 srcPixels, src.fRowBytes,
998 dstPixels + pad*dst->fRowBytes + pad,
999 dst->fRowBytes, srcWidth, srcHeight);
1000 SkMask::FreeImage(dstPixels);
1001 } else if (style != kNormal_SkBlurStyle) {
1002 clamp_with_orig(dstPixels + pad*dst->fRowBytes + pad,
1003 dst->fRowBytes, srcPixels, src.fRowBytes, srcWidth, srcHeight, style);
1004 }
1005 (void)autoCall.release();
1006 }
1007
1008 if (style == kInner_SkBlurStyle) {
1009 dst->fBounds = src.fBounds; // restore trimmed bounds
1010 dst->fRowBytes = src.fRowBytes;
1011 }
1012
1013 return true;
1014 }
1015