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