• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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