• 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 #include "SkBlurMask.h"
9 #include "SkBlurPriv.h"
10 #include "SkGpuBlurUtils.h"
11 #include "SkMaskFilterBase.h"
12 #include "SkReadBuffer.h"
13 #include "SkRRectPriv.h"
14 #include "SkWriteBuffer.h"
15 #include "SkMaskFilter.h"
16 #include "SkRRect.h"
17 #include "SkStringUtils.h"
18 #include "SkStrokeRec.h"
19 #include "SkVertices.h"
20 
21 #if SK_SUPPORT_GPU
22 #include "GrClip.h"
23 #include "GrContext.h"
24 #include "GrContextPriv.h"
25 #include "GrFragmentProcessor.h"
26 #include "GrRenderTargetContext.h"
27 #include "GrResourceProvider.h"
28 #include "GrShaderCaps.h"
29 #include "GrShape.h"
30 #include "GrStyle.h"
31 #include "GrTextureProxy.h"
32 #include "effects/GrCircleBlurFragmentProcessor.h"
33 #include "effects/GrRectBlurEffect.h"
34 #include "effects/GrRRectBlurEffect.h"
35 #include "effects/GrSimpleTextureEffect.h"
36 #include "effects/GrTextureDomain.h"
37 #include "glsl/GrGLSLFragmentProcessor.h"
38 #include "glsl/GrGLSLFragmentShaderBuilder.h"
39 #include "glsl/GrGLSLProgramDataManager.h"
40 #include "glsl/GrGLSLUniformHandler.h"
41 #endif
42 
43 class SkBlurMaskFilterImpl : public SkMaskFilterBase {
44 public:
45     SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle, bool respectCTM);
46 
47     // overrides from SkMaskFilter
48     SkMask::Format getFormat() const override;
49     bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
50                     SkIPoint* margin) const override;
51 
52 #if SK_SUPPORT_GPU
53     bool canFilterMaskGPU(const GrShape& shape,
54                           const SkIRect& devSpaceShapeBounds,
55                           const SkIRect& clipBounds,
56                           const SkMatrix& ctm,
57                           SkIRect* maskRect) const override;
58     bool directFilterMaskGPU(GrContext*,
59                              GrRenderTargetContext* renderTargetContext,
60                              GrPaint&&,
61                              const GrClip&,
62                              const SkMatrix& viewMatrix,
63                              const GrShape& shape) const override;
64     sk_sp<GrTextureProxy> filterMaskGPU(GrContext*,
65                                         sk_sp<GrTextureProxy> srcProxy,
66                                         const SkMatrix& ctm,
67                                         const SkIRect& maskRect) const override;
68 #endif
69 
70     void computeFastBounds(const SkRect&, SkRect*) const override;
71     bool asABlur(BlurRec*) const override;
72 
73 
74 protected:
75     FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
76                                    const SkIRect& clipBounds,
77                                    NinePatch*) const override;
78 
79     FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&,
80                                    const SkIRect& clipBounds,
81                                    NinePatch*) const override;
82 
83     bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
84                         SkIPoint* margin, SkMask::CreateMode createMode) const;
85     bool filterRRectMask(SkMask* dstM, const SkRRect& r, const SkMatrix& matrix,
86                         SkIPoint* margin, SkMask::CreateMode createMode) const;
87 
ignoreXform() const88     bool ignoreXform() const { return !fRespectCTM; }
89 
90 private:
91     SK_FLATTENABLE_HOOKS(SkBlurMaskFilterImpl)
92     // To avoid unseemly allocation requests (esp. for finite platforms like
93     // handset) we limit the radius so something manageable. (as opposed to
94     // a request like 10,000)
95     static const SkScalar kMAX_BLUR_SIGMA;
96 
97     SkScalar    fSigma;
98     SkBlurStyle fBlurStyle;
99     bool        fRespectCTM;
100 
101     SkBlurMaskFilterImpl(SkReadBuffer&);
102     void flatten(SkWriteBuffer&) const override;
103 
computeXformedSigma(const SkMatrix & ctm) const104     SkScalar computeXformedSigma(const SkMatrix& ctm) const {
105         SkScalar xformedSigma = this->ignoreXform() ? fSigma : ctm.mapRadius(fSigma);
106         return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA);
107     }
108 
109     friend class SkBlurMaskFilter;
110 
111     typedef SkMaskFilter INHERITED;
112     friend void sk_register_blur_maskfilter_createproc();
113 };
114 
115 const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128);
116 
117 // linearly interpolate between y1 & y3 to match x2's position between x1 & x3
interp(SkScalar x1,SkScalar x2,SkScalar x3,SkScalar y1,SkScalar y3)118 static SkScalar interp(SkScalar x1, SkScalar x2, SkScalar x3, SkScalar y1, SkScalar y3) {
119     SkASSERT(x1 <= x2 && x2 <= x3);
120     SkASSERT(y1 <= y3);
121 
122     SkScalar t = (x2 - x1) / (x3 - x1);
123     return y1 + t * (y3 - y1);
124 }
125 
126 // Insert 'lower' and 'higher' into 'array1' and insert a new value at each matching insertion
127 // point in 'array2' that linearly interpolates between the existing values.
128 // Return a bit mask which contains a copy of 'inputMask' for all the cells between the two
129 // insertion points.
insert_into_arrays(SkScalar * array1,SkScalar * array2,SkScalar lower,SkScalar higher,int * num,uint32_t inputMask,int maskSize)130 static uint32_t insert_into_arrays(SkScalar* array1, SkScalar* array2,
131                                    SkScalar lower, SkScalar higher,
132                                    int* num, uint32_t inputMask, int maskSize) {
133     SkASSERT(lower < higher);
134     SkASSERT(lower >= array1[0] && higher <= array1[*num-1]);
135 
136     int32_t skipMask = 0x0;
137     int i;
138     for (i = 0; i < *num; ++i) {
139         if (lower >= array1[i] && lower < array1[i+1]) {
140             if (!SkScalarNearlyEqual(lower, array1[i])) {
141                 memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar));
142                 array1[i+1] = lower;
143                 memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar));
144                 array2[i+1] = interp(array1[i], lower, array1[i+2], array2[i], array2[i+2]);
145                 i++;
146                 (*num)++;
147             }
148             break;
149         }
150     }
151     for ( ; i < *num; ++i) {
152         skipMask |= inputMask << (i*maskSize);
153         if (higher > array1[i] && higher <= array1[i+1]) {
154             if (!SkScalarNearlyEqual(higher, array1[i+1])) {
155                 memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar));
156                 array1[i+1] = higher;
157                 memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar));
158                 array2[i+1] = interp(array1[i], higher, array1[i+2], array2[i], array2[i+2]);
159                 (*num)++;
160             }
161             break;
162         }
163     }
164 
165     return skipMask;
166 }
167 
SkComputeBlurredRRectParams(const SkRRect & srcRRect,const SkRRect & devRRect,const SkRect & occluder,SkScalar sigma,SkScalar xformedSigma,SkRRect * rrectToDraw,SkISize * widthHeight,SkScalar rectXs[kSkBlurRRectMaxDivisions],SkScalar rectYs[kSkBlurRRectMaxDivisions],SkScalar texXs[kSkBlurRRectMaxDivisions],SkScalar texYs[kSkBlurRRectMaxDivisions],int * numXs,int * numYs,uint32_t * skipMask)168 bool SkComputeBlurredRRectParams(const SkRRect& srcRRect, const SkRRect& devRRect,
169                                  const SkRect& occluder,
170                                  SkScalar sigma, SkScalar xformedSigma,
171                                  SkRRect* rrectToDraw,
172                                  SkISize* widthHeight,
173                                  SkScalar rectXs[kSkBlurRRectMaxDivisions],
174                                  SkScalar rectYs[kSkBlurRRectMaxDivisions],
175                                  SkScalar texXs[kSkBlurRRectMaxDivisions],
176                                  SkScalar texYs[kSkBlurRRectMaxDivisions],
177                                  int* numXs, int* numYs, uint32_t* skipMask) {
178     unsigned int devBlurRadius = 3*SkScalarCeilToInt(xformedSigma-1/6.0f);
179     SkScalar srcBlurRadius = 3.0f * sigma;
180 
181     const SkRect& devOrig = devRRect.getBounds();
182     const SkVector& devRadiiUL = devRRect.radii(SkRRect::kUpperLeft_Corner);
183     const SkVector& devRadiiUR = devRRect.radii(SkRRect::kUpperRight_Corner);
184     const SkVector& devRadiiLR = devRRect.radii(SkRRect::kLowerRight_Corner);
185     const SkVector& devRadiiLL = devRRect.radii(SkRRect::kLowerLeft_Corner);
186 
187     const int devLeft  = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUL.fX, devRadiiLL.fX));
188     const int devTop   = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUL.fY, devRadiiUR.fY));
189     const int devRight = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUR.fX, devRadiiLR.fX));
190     const int devBot   = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiLL.fY, devRadiiLR.fY));
191 
192     // This is a conservative check for nine-patchability
193     if (devOrig.fLeft + devLeft + devBlurRadius >= devOrig.fRight  - devRight - devBlurRadius ||
194         devOrig.fTop  + devTop  + devBlurRadius >= devOrig.fBottom - devBot   - devBlurRadius) {
195         return false;
196     }
197 
198     const SkVector& srcRadiiUL = srcRRect.radii(SkRRect::kUpperLeft_Corner);
199     const SkVector& srcRadiiUR = srcRRect.radii(SkRRect::kUpperRight_Corner);
200     const SkVector& srcRadiiLR = srcRRect.radii(SkRRect::kLowerRight_Corner);
201     const SkVector& srcRadiiLL = srcRRect.radii(SkRRect::kLowerLeft_Corner);
202 
203     const SkScalar srcLeft  = SkTMax<SkScalar>(srcRadiiUL.fX, srcRadiiLL.fX);
204     const SkScalar srcTop   = SkTMax<SkScalar>(srcRadiiUL.fY, srcRadiiUR.fY);
205     const SkScalar srcRight = SkTMax<SkScalar>(srcRadiiUR.fX, srcRadiiLR.fX);
206     const SkScalar srcBot   = SkTMax<SkScalar>(srcRadiiLL.fY, srcRadiiLR.fY);
207 
208     int newRRWidth = 2*devBlurRadius + devLeft + devRight + 1;
209     int newRRHeight = 2*devBlurRadius + devTop + devBot + 1;
210     widthHeight->fWidth = newRRWidth + 2 * devBlurRadius;
211     widthHeight->fHeight = newRRHeight + 2 * devBlurRadius;
212 
213     const SkRect srcProxyRect = srcRRect.getBounds().makeOutset(srcBlurRadius, srcBlurRadius);
214 
215     rectXs[0] = srcProxyRect.fLeft;
216     rectXs[1] = srcProxyRect.fLeft + 2*srcBlurRadius + srcLeft;
217     rectXs[2] = srcProxyRect.fRight - 2*srcBlurRadius - srcRight;
218     rectXs[3] = srcProxyRect.fRight;
219 
220     rectYs[0] = srcProxyRect.fTop;
221     rectYs[1] = srcProxyRect.fTop + 2*srcBlurRadius + srcTop;
222     rectYs[2] = srcProxyRect.fBottom - 2*srcBlurRadius - srcBot;
223     rectYs[3] = srcProxyRect.fBottom;
224 
225     texXs[0] = 0.0f;
226     texXs[1] = 2.0f*devBlurRadius + devLeft;
227     texXs[2] = 2.0f*devBlurRadius + devLeft + 1;
228     texXs[3] = SkIntToScalar(widthHeight->fWidth);
229 
230     texYs[0] = 0.0f;
231     texYs[1] = 2.0f*devBlurRadius + devTop;
232     texYs[2] = 2.0f*devBlurRadius + devTop + 1;
233     texYs[3] = SkIntToScalar(widthHeight->fHeight);
234 
235     SkRect temp = occluder;
236 
237     *numXs = 4;
238     *numYs = 4;
239     *skipMask = 0;
240     if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) {
241         *skipMask = insert_into_arrays(rectXs, texXs, temp.fLeft, temp.fRight, numXs, 0x1, 1);
242         *skipMask = insert_into_arrays(rectYs, texYs, temp.fTop, temp.fBottom,
243                                        numYs, *skipMask, *numXs-1);
244     }
245 
246     const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(devBlurRadius),
247                                             SkIntToScalar(devBlurRadius),
248                                             SkIntToScalar(newRRWidth),
249                                             SkIntToScalar(newRRHeight));
250     SkVector newRadii[4];
251     newRadii[0] = { SkScalarCeilToScalar(devRadiiUL.fX), SkScalarCeilToScalar(devRadiiUL.fY) };
252     newRadii[1] = { SkScalarCeilToScalar(devRadiiUR.fX), SkScalarCeilToScalar(devRadiiUR.fY) };
253     newRadii[2] = { SkScalarCeilToScalar(devRadiiLR.fX), SkScalarCeilToScalar(devRadiiLR.fY) };
254     newRadii[3] = { SkScalarCeilToScalar(devRadiiLL.fX), SkScalarCeilToScalar(devRadiiLL.fY) };
255 
256     rrectToDraw->setRectRadii(newRect, newRadii);
257     return true;
258 }
259 
260 ///////////////////////////////////////////////////////////////////////////////
261 
SkBlurMaskFilterImpl(SkScalar sigma,SkBlurStyle style,bool respectCTM)262 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style, bool respectCTM)
263     : fSigma(sigma)
264     , fBlurStyle(style)
265     , fRespectCTM(respectCTM) {
266     SkASSERT(fSigma > 0);
267     SkASSERT((unsigned)style <= kLastEnum_SkBlurStyle);
268 }
269 
getFormat() const270 SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
271     return SkMask::kA8_Format;
272 }
273 
asABlur(BlurRec * rec) const274 bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const {
275     if (this->ignoreXform()) {
276         return false;
277     }
278 
279     if (rec) {
280         rec->fSigma = fSigma;
281         rec->fStyle = fBlurStyle;
282     }
283     return true;
284 }
285 
filterMask(SkMask * dst,const SkMask & src,const SkMatrix & matrix,SkIPoint * margin) const286 bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
287                                       const SkMatrix& matrix,
288                                       SkIPoint* margin) const {
289     SkScalar sigma = this->computeXformedSigma(matrix);
290     return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, margin);
291 }
292 
filterRectMask(SkMask * dst,const SkRect & r,const SkMatrix & matrix,SkIPoint * margin,SkMask::CreateMode createMode) const293 bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
294                                           const SkMatrix& matrix,
295                                           SkIPoint* margin, SkMask::CreateMode createMode) const {
296     SkScalar sigma = computeXformedSigma(matrix);
297 
298     return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle, margin, createMode);
299 }
300 
filterRRectMask(SkMask * dst,const SkRRect & r,const SkMatrix & matrix,SkIPoint * margin,SkMask::CreateMode createMode) const301 bool SkBlurMaskFilterImpl::filterRRectMask(SkMask* dst, const SkRRect& r,
302                                           const SkMatrix& matrix,
303                                           SkIPoint* margin, SkMask::CreateMode createMode) const {
304     SkScalar sigma = computeXformedSigma(matrix);
305 
306     return SkBlurMask::BlurRRect(sigma, dst, r, fBlurStyle, margin, createMode);
307 }
308 
309 #include "SkCanvas.h"
310 
prepare_to_draw_into_mask(const SkRect & bounds,SkMask * mask)311 static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) {
312     SkASSERT(mask != nullptr);
313 
314     mask->fBounds = bounds.roundOut();
315     mask->fRowBytes = SkAlign4(mask->fBounds.width());
316     mask->fFormat = SkMask::kA8_Format;
317     const size_t size = mask->computeImageSize();
318     mask->fImage = SkMask::AllocImage(size, SkMask::kZeroInit_Alloc);
319     if (nullptr == mask->fImage) {
320         return false;
321     }
322     return true;
323 }
324 
draw_rrect_into_mask(const SkRRect rrect,SkMask * mask)325 static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) {
326     if (!prepare_to_draw_into_mask(rrect.rect(), mask)) {
327         return false;
328     }
329 
330     // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a
331     // clean way to share more code?
332     SkBitmap bitmap;
333     bitmap.installMaskPixels(*mask);
334 
335     SkCanvas canvas(bitmap);
336     canvas.translate(-SkIntToScalar(mask->fBounds.left()),
337                      -SkIntToScalar(mask->fBounds.top()));
338 
339     SkPaint paint;
340     paint.setAntiAlias(true);
341     canvas.drawRRect(rrect, paint);
342     return true;
343 }
344 
draw_rects_into_mask(const SkRect rects[],int count,SkMask * mask)345 static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) {
346     if (!prepare_to_draw_into_mask(rects[0], mask)) {
347         return false;
348     }
349 
350     SkBitmap bitmap;
351     bitmap.installPixels(SkImageInfo::Make(mask->fBounds.width(),
352                                            mask->fBounds.height(),
353                                            kAlpha_8_SkColorType,
354                                            kPremul_SkAlphaType),
355                          mask->fImage, mask->fRowBytes);
356 
357     SkCanvas canvas(bitmap);
358     canvas.translate(-SkIntToScalar(mask->fBounds.left()),
359                      -SkIntToScalar(mask->fBounds.top()));
360 
361     SkPaint paint;
362     paint.setAntiAlias(true);
363 
364     if (1 == count) {
365         canvas.drawRect(rects[0], paint);
366     } else {
367         // todo: do I need a fast way to do this?
368         SkPath path;
369         path.addRect(rects[0]);
370         path.addRect(rects[1]);
371         path.setFillType(SkPath::kEvenOdd_FillType);
372         canvas.drawPath(path, paint);
373     }
374     return true;
375 }
376 
rect_exceeds(const SkRect & r,SkScalar v)377 static bool rect_exceeds(const SkRect& r, SkScalar v) {
378     return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
379            r.width() > v || r.height() > v;
380 }
381 
382 #include "SkMaskCache.h"
383 
copy_mask_to_cacheddata(SkMask * mask)384 static SkCachedData* copy_mask_to_cacheddata(SkMask* mask) {
385     const size_t size = mask->computeTotalImageSize();
386     SkCachedData* data = SkResourceCache::NewCachedData(size);
387     if (data) {
388         memcpy(data->writable_data(), mask->fImage, size);
389         SkMask::FreeImage(mask->fImage);
390         mask->fImage = (uint8_t*)data->data();
391     }
392     return data;
393 }
394 
find_cached_rrect(SkMask * mask,SkScalar sigma,SkBlurStyle style,const SkRRect & rrect)395 static SkCachedData* find_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
396                                        const SkRRect& rrect) {
397     return SkMaskCache::FindAndRef(sigma, style, rrect, mask);
398 }
399 
add_cached_rrect(SkMask * mask,SkScalar sigma,SkBlurStyle style,const SkRRect & rrect)400 static SkCachedData* add_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
401                                       const SkRRect& rrect) {
402     SkCachedData* cache = copy_mask_to_cacheddata(mask);
403     if (cache) {
404         SkMaskCache::Add(sigma, style, rrect, *mask, cache);
405     }
406     return cache;
407 }
408 
find_cached_rects(SkMask * mask,SkScalar sigma,SkBlurStyle style,const SkRect rects[],int count)409 static SkCachedData* find_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
410                                        const SkRect rects[], int count) {
411     return SkMaskCache::FindAndRef(sigma, style, rects, count, mask);
412 }
413 
add_cached_rects(SkMask * mask,SkScalar sigma,SkBlurStyle style,const SkRect rects[],int count)414 static SkCachedData* add_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
415                                       const SkRect rects[], int count) {
416     SkCachedData* cache = copy_mask_to_cacheddata(mask);
417     if (cache) {
418         SkMaskCache::Add(sigma, style, rects, count, *mask, cache);
419     }
420     return cache;
421 }
422 
423 static const bool c_analyticBlurRRect{true};
424 
425 SkMaskFilterBase::FilterReturn
filterRRectToNine(const SkRRect & rrect,const SkMatrix & matrix,const SkIRect & clipBounds,NinePatch * patch) const426 SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix,
427                                         const SkIRect& clipBounds,
428                                         NinePatch* patch) const {
429     SkASSERT(patch != nullptr);
430     switch (rrect.getType()) {
431         case SkRRect::kEmpty_Type:
432             // Nothing to draw.
433             return kFalse_FilterReturn;
434 
435         case SkRRect::kRect_Type:
436             // We should have caught this earlier.
437             SkASSERT(false);
438             // Fall through.
439         case SkRRect::kOval_Type:
440             // The nine patch special case does not handle ovals, and we
441             // already have code for rectangles.
442             return kUnimplemented_FilterReturn;
443 
444         // These three can take advantage of this fast path.
445         case SkRRect::kSimple_Type:
446         case SkRRect::kNinePatch_Type:
447         case SkRRect::kComplex_Type:
448             break;
449     }
450 
451     // TODO: report correct metrics for innerstyle, where we do not grow the
452     // total bounds, but we do need an inset the size of our blur-radius
453     if (kInner_SkBlurStyle == fBlurStyle) {
454         return kUnimplemented_FilterReturn;
455     }
456 
457     // TODO: take clipBounds into account to limit our coordinates up front
458     // for now, just skip too-large src rects (to take the old code path).
459     if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) {
460         return kUnimplemented_FilterReturn;
461     }
462 
463     SkIPoint margin;
464     SkMask  srcM, dstM;
465     srcM.fBounds = rrect.rect().roundOut();
466     srcM.fFormat = SkMask::kA8_Format;
467     srcM.fRowBytes = 0;
468 
469     bool filterResult = false;
470     if (c_analyticBlurRRect) {
471         // special case for fast round rect blur
472         // don't actually do the blur the first time, just compute the correct size
473         filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin,
474                                             SkMask::kJustComputeBounds_CreateMode);
475     }
476 
477     if (!filterResult) {
478         filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
479     }
480 
481     if (!filterResult) {
482         return kFalse_FilterReturn;
483     }
484 
485     // Now figure out the appropriate width and height of the smaller round rectangle
486     // to stretch. It will take into account the larger radius per side as well as double
487     // the margin, to account for inner and outer blur.
488     const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
489     const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
490     const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
491     const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
492 
493     const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX);
494     const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX);
495 
496     // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover
497     // any fractional space on either side plus 1 for the part to stretch.
498     const SkScalar stretchSize = SkIntToScalar(3);
499 
500     const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize;
501     if (totalSmallWidth >= rrect.rect().width()) {
502         // There is no valid piece to stretch.
503         return kUnimplemented_FilterReturn;
504     }
505 
506     const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY);
507     const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY);
508 
509     const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize;
510     if (totalSmallHeight >= rrect.rect().height()) {
511         // There is no valid piece to stretch.
512         return kUnimplemented_FilterReturn;
513     }
514 
515     SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight);
516 
517     SkRRect smallRR;
518     SkVector radii[4];
519     radii[SkRRect::kUpperLeft_Corner] = UL;
520     radii[SkRRect::kUpperRight_Corner] = UR;
521     radii[SkRRect::kLowerRight_Corner] = LR;
522     radii[SkRRect::kLowerLeft_Corner] = LL;
523     smallRR.setRectRadii(smallR, radii);
524 
525     const SkScalar sigma = this->computeXformedSigma(matrix);
526     SkCachedData* cache = find_cached_rrect(&patch->fMask, sigma, fBlurStyle, smallRR);
527     if (!cache) {
528         bool analyticBlurWorked = false;
529         if (c_analyticBlurRRect) {
530             analyticBlurWorked =
531                 this->filterRRectMask(&patch->fMask, smallRR, matrix, &margin,
532                                       SkMask::kComputeBoundsAndRenderImage_CreateMode);
533         }
534 
535         if (!analyticBlurWorked) {
536             if (!draw_rrect_into_mask(smallRR, &srcM)) {
537                 return kFalse_FilterReturn;
538             }
539 
540             SkAutoMaskFreeImage amf(srcM.fImage);
541 
542             if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
543                 return kFalse_FilterReturn;
544             }
545         }
546         cache = add_cached_rrect(&patch->fMask, sigma, fBlurStyle, smallRR);
547     }
548 
549     patch->fMask.fBounds.offsetTo(0, 0);
550     patch->fOuterRect = dstM.fBounds;
551     patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1;
552     patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1;
553     SkASSERT(nullptr == patch->fCache);
554     patch->fCache = cache;  // transfer ownership to patch
555     return kTrue_FilterReturn;
556 }
557 
558 // Use the faster analytic blur approach for ninepatch rects
559 static const bool c_analyticBlurNinepatch{true};
560 
561 SkMaskFilterBase::FilterReturn
filterRectsToNine(const SkRect rects[],int count,const SkMatrix & matrix,const SkIRect & clipBounds,NinePatch * patch) const562 SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
563                                         const SkMatrix& matrix,
564                                         const SkIRect& clipBounds,
565                                         NinePatch* patch) const {
566     if (count < 1 || count > 2) {
567         return kUnimplemented_FilterReturn;
568     }
569 
570     // TODO: report correct metrics for innerstyle, where we do not grow the
571     // total bounds, but we do need an inset the size of our blur-radius
572     if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) {
573         return kUnimplemented_FilterReturn;
574     }
575 
576     // TODO: take clipBounds into account to limit our coordinates up front
577     // for now, just skip too-large src rects (to take the old code path).
578     if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
579         return kUnimplemented_FilterReturn;
580     }
581 
582     SkIPoint margin;
583     SkMask  srcM, dstM;
584     srcM.fBounds = rects[0].roundOut();
585     srcM.fFormat = SkMask::kA8_Format;
586     srcM.fRowBytes = 0;
587 
588     bool filterResult = false;
589     if (count == 1 && c_analyticBlurNinepatch) {
590         // special case for fast rect blur
591         // don't actually do the blur the first time, just compute the correct size
592         filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
593                                             SkMask::kJustComputeBounds_CreateMode);
594     } else {
595         filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
596     }
597 
598     if (!filterResult) {
599         return kFalse_FilterReturn;
600     }
601 
602     /*
603      *  smallR is the smallest version of 'rect' that will still guarantee that
604      *  we get the same blur results on all edges, plus 1 center row/col that is
605      *  representative of the extendible/stretchable edges of the ninepatch.
606      *  Since our actual edge may be fractional we inset 1 more to be sure we
607      *  don't miss any interior blur.
608      *  x is an added pixel of blur, and { and } are the (fractional) edge
609      *  pixels from the original rect.
610      *
611      *   x x { x x .... x x } x x
612      *
613      *  Thus, in this case, we inset by a total of 5 (on each side) beginning
614      *  with our outer-rect (dstM.fBounds)
615      */
616     SkRect smallR[2];
617     SkIPoint center;
618 
619     // +2 is from +1 for each edge (to account for possible fractional edges
620     int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
621     int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
622     SkIRect innerIR;
623 
624     if (1 == count) {
625         innerIR = srcM.fBounds;
626         center.set(smallW, smallH);
627     } else {
628         SkASSERT(2 == count);
629         rects[1].roundIn(&innerIR);
630         center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
631                    smallH + (innerIR.top() - srcM.fBounds.top()));
632     }
633 
634     // +1 so we get a clean, stretchable, center row/col
635     smallW += 1;
636     smallH += 1;
637 
638     // we want the inset amounts to be integral, so we don't change any
639     // fractional phase on the fRight or fBottom of our smallR.
640     const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
641     const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
642     if (dx < 0 || dy < 0) {
643         // we're too small, relative to our blur, to break into nine-patch,
644         // so we ask to have our normal filterMask() be called.
645         return kUnimplemented_FilterReturn;
646     }
647 
648     smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
649     if (smallR[0].width() < 2 || smallR[0].height() < 2) {
650         return kUnimplemented_FilterReturn;
651     }
652     if (2 == count) {
653         smallR[1].set(rects[1].left(), rects[1].top(),
654                       rects[1].right() - dx, rects[1].bottom() - dy);
655         SkASSERT(!smallR[1].isEmpty());
656     }
657 
658     const SkScalar sigma = this->computeXformedSigma(matrix);
659     SkCachedData* cache = find_cached_rects(&patch->fMask, sigma, fBlurStyle, smallR, count);
660     if (!cache) {
661         if (count > 1 || !c_analyticBlurNinepatch) {
662             if (!draw_rects_into_mask(smallR, count, &srcM)) {
663                 return kFalse_FilterReturn;
664             }
665 
666             SkAutoMaskFreeImage amf(srcM.fImage);
667 
668             if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
669                 return kFalse_FilterReturn;
670             }
671         } else {
672             if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
673                                       SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
674                 return kFalse_FilterReturn;
675             }
676         }
677         cache = add_cached_rects(&patch->fMask, sigma, fBlurStyle, smallR, count);
678     }
679     patch->fMask.fBounds.offsetTo(0, 0);
680     patch->fOuterRect = dstM.fBounds;
681     patch->fCenter = center;
682     SkASSERT(nullptr == patch->fCache);
683     patch->fCache = cache;  // transfer ownership to patch
684     return kTrue_FilterReturn;
685 }
686 
computeFastBounds(const SkRect & src,SkRect * dst) const687 void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
688                                              SkRect* dst) const {
689     SkScalar pad = 3.0f * fSigma;
690 
691     dst->set(src.fLeft  - pad, src.fTop    - pad,
692              src.fRight + pad, src.fBottom + pad);
693 }
694 
CreateProc(SkReadBuffer & buffer)695 sk_sp<SkFlattenable> SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
696     const SkScalar sigma = buffer.readScalar();
697     SkBlurStyle style = buffer.read32LE(kLastEnum_SkBlurStyle);
698 
699     uint32_t flags = buffer.read32LE(0x3);  // historically we only recorded 2 bits
700     bool respectCTM = !(flags & 1); // historically we stored ignoreCTM in low bit
701 
702     if (buffer.isVersionLT(SkReadBuffer::kRemoveOccluderFromBlurMaskFilter)) {
703         SkRect unused;
704         buffer.readRect(&unused);
705     }
706 
707     return SkMaskFilter::MakeBlur((SkBlurStyle)style, sigma, respectCTM);
708 }
709 
flatten(SkWriteBuffer & buffer) const710 void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
711     buffer.writeScalar(fSigma);
712     buffer.writeUInt(fBlurStyle);
713     buffer.writeUInt(!fRespectCTM); // historically we recorded ignoreCTM
714 }
715 
716 
717 #if SK_SUPPORT_GPU
718 
directFilterMaskGPU(GrContext * context,GrRenderTargetContext * renderTargetContext,GrPaint && paint,const GrClip & clip,const SkMatrix & viewMatrix,const GrShape & shape) const719 bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrContext* context,
720                                                GrRenderTargetContext* renderTargetContext,
721                                                GrPaint&& paint,
722                                                const GrClip& clip,
723                                                const SkMatrix& viewMatrix,
724                                                const GrShape& shape) const {
725     SkASSERT(renderTargetContext);
726 
727     if (fBlurStyle != kNormal_SkBlurStyle) {
728         return false;
729     }
730 
731     if (!viewMatrix.isScaleTranslate()) {
732         return false;
733     }
734 
735     // TODO: we could handle blurred stroked circles
736     if (!shape.style().isSimpleFill()) {
737         return false;
738     }
739 
740     SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
741     if (xformedSigma <= 0) {
742         return false;
743     }
744 
745     SkRRect srcRRect;
746     bool inverted;
747     if (!shape.asRRect(&srcRRect, nullptr, nullptr, &inverted) || inverted) {
748         return false;
749     }
750 
751     SkRRect devRRect;
752     if (!srcRRect.transform(viewMatrix, &devRRect)) {
753         return false;
754     }
755 
756     if (!SkRRectPriv::AllCornersCircular(devRRect)) {
757         return false;
758     }
759 
760     GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
761     std::unique_ptr<GrFragmentProcessor> fp;
762 
763     if (devRRect.isRect() || SkRRectPriv::IsCircle(devRRect)) {
764         if (devRRect.isRect()) {
765             SkScalar pad = 3.0f * xformedSigma;
766             const SkRect dstCoverageRect = devRRect.rect().makeOutset(pad, pad);
767 
768             fp = GrRectBlurEffect::Make(proxyProvider, *context->contextPriv().caps()->shaderCaps(),
769                                         dstCoverageRect, xformedSigma);
770         } else {
771             fp = GrCircleBlurFragmentProcessor::Make(proxyProvider, devRRect.rect(), xformedSigma);
772         }
773 
774         if (!fp) {
775             return false;
776         }
777         paint.addCoverageFragmentProcessor(std::move(fp));
778 
779         SkRect srcProxyRect = srcRRect.rect();
780         SkScalar outsetX = 3.0f*fSigma;
781         SkScalar outsetY = 3.0f*fSigma;
782         if (this->ignoreXform()) {
783             // When we're ignoring the CTM the padding added to the source rect also needs to ignore
784             // the CTM. The matrix passed in here is guaranteed to be just scale and translate so we
785             // can just grab the X and Y scales off the matrix and pre-undo the scale.
786             outsetX /= SkScalarAbs(viewMatrix.getScaleX());
787             outsetY /= SkScalarAbs(viewMatrix.getScaleY());
788         }
789         srcProxyRect.outset(outsetX, outsetY);
790 
791         renderTargetContext->drawRect(clip, std::move(paint), GrAA::kNo, viewMatrix, srcProxyRect);
792         return true;
793     }
794 
795     fp = GrRRectBlurEffect::Make(context, fSigma, xformedSigma, srcRRect, devRRect);
796     if (!fp) {
797         return false;
798     }
799 
800     if (!this->ignoreXform()) {
801         SkRect srcProxyRect = srcRRect.rect();
802         srcProxyRect.outset(3.0f*fSigma, 3.0f*fSigma);
803 
804         SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 4, 6, 0);
805         srcProxyRect.toQuad(builder.positions());
806 
807         static const uint16_t fullIndices[6] = { 0, 1, 2, 0, 2, 3 };
808         memcpy(builder.indices(), fullIndices, sizeof(fullIndices));
809         sk_sp<SkVertices> vertices = builder.detach();
810 
811         paint.addCoverageFragmentProcessor(std::move(fp));
812         renderTargetContext->drawVertices(clip, std::move(paint), viewMatrix, std::move(vertices),
813                                           nullptr, 0);
814     } else {
815         SkMatrix inverse;
816         if (!viewMatrix.invert(&inverse)) {
817             return false;
818         }
819 
820         float extra=3.f*SkScalarCeilToScalar(xformedSigma-1/6.0f);
821         SkRect proxyRect = devRRect.rect();
822         proxyRect.outset(extra, extra);
823 
824         paint.addCoverageFragmentProcessor(std::move(fp));
825         renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo,
826                                                      SkMatrix::I(), proxyRect, inverse);
827     }
828 
829     return true;
830 }
831 
canFilterMaskGPU(const GrShape & shape,const SkIRect & devSpaceShapeBounds,const SkIRect & clipBounds,const SkMatrix & ctm,SkIRect * maskRect) const832 bool SkBlurMaskFilterImpl::canFilterMaskGPU(const GrShape& shape,
833                                             const SkIRect& devSpaceShapeBounds,
834                                             const SkIRect& clipBounds,
835                                             const SkMatrix& ctm,
836                                             SkIRect* maskRect) const {
837     SkScalar xformedSigma = this->computeXformedSigma(ctm);
838     if (xformedSigma <= 0) {
839         maskRect->setEmpty();
840         return false;
841     }
842 
843     if (maskRect) {
844         float sigma3 = 3 * SkScalarToFloat(xformedSigma);
845 
846         // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
847         SkIRect clipRect = clipBounds.makeOutset(sigma3, sigma3);
848         SkIRect srcRect = devSpaceShapeBounds.makeOutset(sigma3, sigma3);
849 
850         if (!srcRect.intersect(clipRect)) {
851             srcRect.setEmpty();
852         }
853         *maskRect = srcRect;
854     }
855 
856     // We prefer to blur paths with small blur radii on the CPU.
857     if (ctm.rectStaysRect()) {
858         static const SkScalar kMIN_GPU_BLUR_SIZE  = SkIntToScalar(64);
859         static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32);
860 
861         if (devSpaceShapeBounds.width() <= kMIN_GPU_BLUR_SIZE &&
862             devSpaceShapeBounds.height() <= kMIN_GPU_BLUR_SIZE &&
863             xformedSigma <= kMIN_GPU_BLUR_SIGMA) {
864             return false;
865         }
866     }
867 
868     return true;
869 }
870 
filterMaskGPU(GrContext * context,sk_sp<GrTextureProxy> srcProxy,const SkMatrix & ctm,const SkIRect & maskRect) const871 sk_sp<GrTextureProxy> SkBlurMaskFilterImpl::filterMaskGPU(GrContext* context,
872                                                           sk_sp<GrTextureProxy> srcProxy,
873                                                           const SkMatrix& ctm,
874                                                           const SkIRect& maskRect) const {
875     // 'maskRect' isn't snapped to the UL corner but the mask in 'src' is.
876     const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
877 
878     SkScalar xformedSigma = this->computeXformedSigma(ctm);
879     SkASSERT(xformedSigma > 0);
880 
881     // If we're doing a normal blur, we can clobber the pathTexture in the
882     // gaussianBlur.  Otherwise, we need to save it for later compositing.
883     bool isNormalBlur = (kNormal_SkBlurStyle == fBlurStyle);
884     sk_sp<GrRenderTargetContext> renderTargetContext(
885               SkGpuBlurUtils::GaussianBlur(context,
886                                            srcProxy,
887                                            nullptr,
888                                            clipRect,
889                                            SkIRect::EmptyIRect(),
890                                            xformedSigma,
891                                            xformedSigma,
892                                            GrTextureDomain::kIgnore_Mode,
893                                            kPremul_SkAlphaType));
894     if (!renderTargetContext) {
895         return nullptr;
896     }
897 
898     if (!isNormalBlur) {
899         GrPaint paint;
900         // Blend pathTexture over blurTexture.
901         paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(std::move(srcProxy),
902                                                                        SkMatrix::I()));
903         if (kInner_SkBlurStyle == fBlurStyle) {
904             // inner:  dst = dst * src
905             paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op);
906         } else if (kSolid_SkBlurStyle == fBlurStyle) {
907             // solid:  dst = src + dst - src * dst
908             //             = src + (1 - src) * dst
909             paint.setCoverageSetOpXPFactory(SkRegion::kUnion_Op);
910         } else if (kOuter_SkBlurStyle == fBlurStyle) {
911             // outer:  dst = dst * (1 - src)
912             //             = 0 * src + (1 - src) * dst
913             paint.setCoverageSetOpXPFactory(SkRegion::kDifference_Op);
914         } else {
915             paint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
916         }
917 
918         renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
919                                       SkRect::Make(clipRect));
920     }
921 
922     return renderTargetContext->asTextureProxyRef();
923 }
924 
925 #endif // SK_SUPPORT_GPU
926 
sk_register_blur_maskfilter_createproc()927 void sk_register_blur_maskfilter_createproc() { SK_REGISTER_FLATTENABLE(SkBlurMaskFilterImpl); }
928 
MakeBlur(SkBlurStyle style,SkScalar sigma,bool respectCTM)929 sk_sp<SkMaskFilter> SkMaskFilter::MakeBlur(SkBlurStyle style, SkScalar sigma, bool respectCTM) {
930     if (SkScalarIsFinite(sigma) && sigma > 0) {
931         return sk_sp<SkMaskFilter>(new SkBlurMaskFilterImpl(sigma, style, respectCTM));
932     }
933     return nullptr;
934 }
935