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 "SkBlurMaskFilter.h"
9 #include "SkBlurMask.h"
10 #include "SkGpuBlurUtils.h"
11 #include "SkReadBuffer.h"
12 #include "SkWriteBuffer.h"
13 #include "SkMaskFilter.h"
14 #include "SkRRect.h"
15 #include "SkStringUtils.h"
16 #include "SkStrokeRec.h"
17 #include "SkVertices.h"
18
19 #if SK_SUPPORT_GPU
20 #include "GrCircleBlurFragmentProcessor.h"
21 #include "GrClip.h"
22 #include "GrContext.h"
23 #include "GrFragmentProcessor.h"
24 #include "GrRenderTargetContext.h"
25 #include "GrResourceProvider.h"
26 #include "GrShaderCaps.h"
27 #include "GrStyle.h"
28 #include "GrTextureProxy.h"
29 #include "effects/GrSimpleTextureEffect.h"
30 #include "effects/GrTextureDomain.h"
31 #include "glsl/GrGLSLFragmentProcessor.h"
32 #include "glsl/GrGLSLFragmentShaderBuilder.h"
33 #include "glsl/GrGLSLProgramDataManager.h"
34 #include "glsl/GrGLSLUniformHandler.h"
35 #endif
36
ConvertRadiusToSigma(SkScalar radius)37 SkScalar SkBlurMaskFilter::ConvertRadiusToSigma(SkScalar radius) {
38 return SkBlurMask::ConvertRadiusToSigma(radius);
39 }
40
41 class SkBlurMaskFilterImpl : public SkMaskFilter {
42 public:
43 SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle, const SkRect& occluder, uint32_t flags);
44
45 // overrides from SkMaskFilter
46 SkMask::Format getFormat() const override;
47 bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
48 SkIPoint* margin) const override;
49
50 #if SK_SUPPORT_GPU
51 bool canFilterMaskGPU(const SkRRect& devRRect,
52 const SkIRect& clipBounds,
53 const SkMatrix& ctm,
54 SkRect* maskRect) const override;
55 bool directFilterMaskGPU(GrContext*,
56 GrRenderTargetContext* renderTargetContext,
57 GrPaint&&,
58 const GrClip&,
59 const SkMatrix& viewMatrix,
60 const SkStrokeRec& strokeRec,
61 const SkPath& path) const override;
62 bool directFilterRRectMaskGPU(GrContext*,
63 GrRenderTargetContext* renderTargetContext,
64 GrPaint&&,
65 const GrClip&,
66 const SkMatrix& viewMatrix,
67 const SkStrokeRec& strokeRec,
68 const SkRRect& rrect,
69 const SkRRect& devRRect) const override;
70 sk_sp<GrTextureProxy> filterMaskGPU(GrContext*,
71 sk_sp<GrTextureProxy> srcProxy,
72 const SkMatrix& ctm,
73 const SkIRect& maskRect) const override;
74 #endif
75
76 void computeFastBounds(const SkRect&, SkRect*) const override;
77 bool asABlur(BlurRec*) const override;
78
79 SK_TO_STRING_OVERRIDE()
80 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
81
82 protected:
83 FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
84 const SkIRect& clipBounds,
85 NinePatch*) const override;
86
87 FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&,
88 const SkIRect& clipBounds,
89 NinePatch*) const override;
90
91 bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
92 SkIPoint* margin, SkMask::CreateMode createMode) const;
93 bool filterRRectMask(SkMask* dstM, const SkRRect& r, const SkMatrix& matrix,
94 SkIPoint* margin, SkMask::CreateMode createMode) const;
95
ignoreXform() const96 bool ignoreXform() const {
97 return SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
98 }
99
100 private:
101 // To avoid unseemly allocation requests (esp. for finite platforms like
102 // handset) we limit the radius so something manageable. (as opposed to
103 // a request like 10,000)
104 static const SkScalar kMAX_BLUR_SIGMA;
105
106 SkScalar fSigma;
107 SkBlurStyle fBlurStyle;
108 SkRect fOccluder;
109 uint32_t fBlurFlags;
110
getQuality() const111 SkBlurQuality getQuality() const {
112 return (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
113 kHigh_SkBlurQuality : kLow_SkBlurQuality;
114 }
115
116 SkBlurMaskFilterImpl(SkReadBuffer&);
117 void flatten(SkWriteBuffer&) const override;
118
computeXformedSigma(const SkMatrix & ctm) const119 SkScalar computeXformedSigma(const SkMatrix& ctm) const {
120 SkScalar xformedSigma = this->ignoreXform() ? fSigma : ctm.mapRadius(fSigma);
121 return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA);
122 }
123
124 friend class SkBlurMaskFilter;
125
126 typedef SkMaskFilter INHERITED;
127 };
128
129 const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128);
130
Make(SkBlurStyle style,SkScalar sigma,const SkRect & occluder,uint32_t flags)131 sk_sp<SkMaskFilter> SkBlurMaskFilter::Make(SkBlurStyle style, SkScalar sigma,
132 const SkRect& occluder, uint32_t flags) {
133 SkASSERT(!(flags & ~SkBlurMaskFilter::kAll_BlurFlag));
134 SkASSERT(style <= kLastEnum_SkBlurStyle);
135
136 if (!SkScalarIsFinite(sigma) || sigma <= 0) {
137 return nullptr;
138 }
139
140 return sk_sp<SkMaskFilter>(new SkBlurMaskFilterImpl(sigma, style, occluder, flags));
141 }
142
143 // linearly interpolate between y1 & y3 to match x2's position between x1 & x3
interp(SkScalar x1,SkScalar x2,SkScalar x3,SkScalar y1,SkScalar y3)144 static SkScalar interp(SkScalar x1, SkScalar x2, SkScalar x3, SkScalar y1, SkScalar y3) {
145 SkASSERT(x1 <= x2 && x2 <= x3);
146 SkASSERT(y1 <= y3);
147
148 SkScalar t = (x2 - x1) / (x3 - x1);
149 return y1 + t * (y3 - y1);
150 }
151
152 // Insert 'lower' and 'higher' into 'array1' and insert a new value at each matching insertion
153 // point in 'array2' that linearly interpolates between the existing values.
154 // Return a bit mask which contains a copy of 'inputMask' for all the cells between the two
155 // insertion points.
insert_into_arrays(SkScalar * array1,SkScalar * array2,SkScalar lower,SkScalar higher,int * num,uint32_t inputMask,int maskSize)156 static uint32_t insert_into_arrays(SkScalar* array1, SkScalar* array2,
157 SkScalar lower, SkScalar higher,
158 int* num, uint32_t inputMask, int maskSize) {
159 SkASSERT(lower < higher);
160 SkASSERT(lower >= array1[0] && higher <= array1[*num-1]);
161
162 int32_t skipMask = 0x0;
163 int i;
164 for (i = 0; i < *num; ++i) {
165 if (lower >= array1[i] && lower < array1[i+1]) {
166 if (!SkScalarNearlyEqual(lower, array1[i])) {
167 memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar));
168 array1[i+1] = lower;
169 memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar));
170 array2[i+1] = interp(array1[i], lower, array1[i+2], array2[i], array2[i+2]);
171 i++;
172 (*num)++;
173 }
174 break;
175 }
176 }
177 for ( ; i < *num; ++i) {
178 skipMask |= inputMask << (i*maskSize);
179 if (higher > array1[i] && higher <= array1[i+1]) {
180 if (!SkScalarNearlyEqual(higher, array1[i+1])) {
181 memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar));
182 array1[i+1] = higher;
183 memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar));
184 array2[i+1] = interp(array1[i], higher, array1[i+2], array2[i], array2[i+2]);
185 (*num)++;
186 }
187 break;
188 }
189 }
190
191 return skipMask;
192 }
193
ComputeBlurredRRectParams(const SkRRect & srcRRect,const SkRRect & devRRect,const SkRect & occluder,SkScalar sigma,SkScalar xformedSigma,SkRRect * rrectToDraw,SkISize * widthHeight,SkScalar rectXs[kMaxDivisions],SkScalar rectYs[kMaxDivisions],SkScalar texXs[kMaxDivisions],SkScalar texYs[kMaxDivisions],int * numXs,int * numYs,uint32_t * skipMask)194 bool SkBlurMaskFilter::ComputeBlurredRRectParams(const SkRRect& srcRRect, const SkRRect& devRRect,
195 const SkRect& occluder,
196 SkScalar sigma, SkScalar xformedSigma,
197 SkRRect* rrectToDraw,
198 SkISize* widthHeight,
199 SkScalar rectXs[kMaxDivisions],
200 SkScalar rectYs[kMaxDivisions],
201 SkScalar texXs[kMaxDivisions],
202 SkScalar texYs[kMaxDivisions],
203 int* numXs, int* numYs, uint32_t* skipMask) {
204 unsigned int devBlurRadius = 3*SkScalarCeilToInt(xformedSigma-1/6.0f);
205 SkScalar srcBlurRadius = 3.0f * sigma;
206
207 const SkRect& devOrig = devRRect.getBounds();
208 const SkVector& devRadiiUL = devRRect.radii(SkRRect::kUpperLeft_Corner);
209 const SkVector& devRadiiUR = devRRect.radii(SkRRect::kUpperRight_Corner);
210 const SkVector& devRadiiLR = devRRect.radii(SkRRect::kLowerRight_Corner);
211 const SkVector& devRadiiLL = devRRect.radii(SkRRect::kLowerLeft_Corner);
212
213 const int devLeft = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUL.fX, devRadiiLL.fX));
214 const int devTop = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUL.fY, devRadiiUR.fY));
215 const int devRight = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUR.fX, devRadiiLR.fX));
216 const int devBot = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiLL.fY, devRadiiLR.fY));
217
218 // This is a conservative check for nine-patchability
219 if (devOrig.fLeft + devLeft + devBlurRadius >= devOrig.fRight - devRight - devBlurRadius ||
220 devOrig.fTop + devTop + devBlurRadius >= devOrig.fBottom - devBot - devBlurRadius) {
221 return false;
222 }
223
224 const SkVector& srcRadiiUL = srcRRect.radii(SkRRect::kUpperLeft_Corner);
225 const SkVector& srcRadiiUR = srcRRect.radii(SkRRect::kUpperRight_Corner);
226 const SkVector& srcRadiiLR = srcRRect.radii(SkRRect::kLowerRight_Corner);
227 const SkVector& srcRadiiLL = srcRRect.radii(SkRRect::kLowerLeft_Corner);
228
229 const SkScalar srcLeft = SkTMax<SkScalar>(srcRadiiUL.fX, srcRadiiLL.fX);
230 const SkScalar srcTop = SkTMax<SkScalar>(srcRadiiUL.fY, srcRadiiUR.fY);
231 const SkScalar srcRight = SkTMax<SkScalar>(srcRadiiUR.fX, srcRadiiLR.fX);
232 const SkScalar srcBot = SkTMax<SkScalar>(srcRadiiLL.fY, srcRadiiLR.fY);
233
234 int newRRWidth = 2*devBlurRadius + devLeft + devRight + 1;
235 int newRRHeight = 2*devBlurRadius + devTop + devBot + 1;
236 widthHeight->fWidth = newRRWidth + 2 * devBlurRadius;
237 widthHeight->fHeight = newRRHeight + 2 * devBlurRadius;
238
239 const SkRect srcProxyRect = srcRRect.getBounds().makeOutset(srcBlurRadius, srcBlurRadius);
240
241 rectXs[0] = srcProxyRect.fLeft;
242 rectXs[1] = srcProxyRect.fLeft + 2*srcBlurRadius + srcLeft;
243 rectXs[2] = srcProxyRect.fRight - 2*srcBlurRadius - srcRight;
244 rectXs[3] = srcProxyRect.fRight;
245
246 rectYs[0] = srcProxyRect.fTop;
247 rectYs[1] = srcProxyRect.fTop + 2*srcBlurRadius + srcTop;
248 rectYs[2] = srcProxyRect.fBottom - 2*srcBlurRadius - srcBot;
249 rectYs[3] = srcProxyRect.fBottom;
250
251 texXs[0] = 0.0f;
252 texXs[1] = 2.0f*devBlurRadius + devLeft;
253 texXs[2] = 2.0f*devBlurRadius + devLeft + 1;
254 texXs[3] = SkIntToScalar(widthHeight->fWidth);
255
256 texYs[0] = 0.0f;
257 texYs[1] = 2.0f*devBlurRadius + devTop;
258 texYs[2] = 2.0f*devBlurRadius + devTop + 1;
259 texYs[3] = SkIntToScalar(widthHeight->fHeight);
260
261 SkRect temp = occluder;
262
263 *numXs = 4;
264 *numYs = 4;
265 *skipMask = 0;
266 if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) {
267 *skipMask = insert_into_arrays(rectXs, texXs, temp.fLeft, temp.fRight, numXs, 0x1, 1);
268 *skipMask = insert_into_arrays(rectYs, texYs, temp.fTop, temp.fBottom,
269 numYs, *skipMask, *numXs-1);
270 }
271
272 const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(devBlurRadius),
273 SkIntToScalar(devBlurRadius),
274 SkIntToScalar(newRRWidth),
275 SkIntToScalar(newRRHeight));
276 SkVector newRadii[4];
277 newRadii[0] = { SkScalarCeilToScalar(devRadiiUL.fX), SkScalarCeilToScalar(devRadiiUL.fY) };
278 newRadii[1] = { SkScalarCeilToScalar(devRadiiUR.fX), SkScalarCeilToScalar(devRadiiUR.fY) };
279 newRadii[2] = { SkScalarCeilToScalar(devRadiiLR.fX), SkScalarCeilToScalar(devRadiiLR.fY) };
280 newRadii[3] = { SkScalarCeilToScalar(devRadiiLL.fX), SkScalarCeilToScalar(devRadiiLL.fY) };
281
282 rrectToDraw->setRectRadii(newRect, newRadii);
283 return true;
284 }
285
286 ///////////////////////////////////////////////////////////////////////////////
287
SkBlurMaskFilterImpl(SkScalar sigma,SkBlurStyle style,const SkRect & occluder,uint32_t flags)288 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style,
289 const SkRect& occluder, uint32_t flags)
290 : fSigma(sigma)
291 , fBlurStyle(style)
292 , fOccluder(occluder)
293 , fBlurFlags(flags) {
294 SkASSERT(fSigma > 0);
295 SkASSERT((unsigned)style <= kLastEnum_SkBlurStyle);
296 SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
297 }
298
getFormat() const299 SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
300 return SkMask::kA8_Format;
301 }
302
asABlur(BlurRec * rec) const303 bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const {
304 if (this->ignoreXform()) {
305 return false;
306 }
307
308 if (rec) {
309 rec->fSigma = fSigma;
310 rec->fStyle = fBlurStyle;
311 rec->fQuality = this->getQuality();
312 }
313 return true;
314 }
315
filterMask(SkMask * dst,const SkMask & src,const SkMatrix & matrix,SkIPoint * margin) const316 bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
317 const SkMatrix& matrix,
318 SkIPoint* margin) const {
319 SkScalar sigma = this->computeXformedSigma(matrix);
320 return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, this->getQuality(), margin);
321 }
322
filterRectMask(SkMask * dst,const SkRect & r,const SkMatrix & matrix,SkIPoint * margin,SkMask::CreateMode createMode) const323 bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
324 const SkMatrix& matrix,
325 SkIPoint* margin, SkMask::CreateMode createMode) const {
326 SkScalar sigma = computeXformedSigma(matrix);
327
328 return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle, margin, createMode);
329 }
330
filterRRectMask(SkMask * dst,const SkRRect & r,const SkMatrix & matrix,SkIPoint * margin,SkMask::CreateMode createMode) const331 bool SkBlurMaskFilterImpl::filterRRectMask(SkMask* dst, const SkRRect& r,
332 const SkMatrix& matrix,
333 SkIPoint* margin, SkMask::CreateMode createMode) const {
334 SkScalar sigma = computeXformedSigma(matrix);
335
336 return SkBlurMask::BlurRRect(sigma, dst, r, fBlurStyle, margin, createMode);
337 }
338
339 #include "SkCanvas.h"
340
prepare_to_draw_into_mask(const SkRect & bounds,SkMask * mask)341 static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) {
342 SkASSERT(mask != nullptr);
343
344 mask->fBounds = bounds.roundOut();
345 mask->fRowBytes = SkAlign4(mask->fBounds.width());
346 mask->fFormat = SkMask::kA8_Format;
347 const size_t size = mask->computeImageSize();
348 mask->fImage = SkMask::AllocImage(size);
349 if (nullptr == mask->fImage) {
350 return false;
351 }
352
353 // FIXME: use sk_calloc in AllocImage?
354 sk_bzero(mask->fImage, size);
355 return true;
356 }
357
draw_rrect_into_mask(const SkRRect rrect,SkMask * mask)358 static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) {
359 if (!prepare_to_draw_into_mask(rrect.rect(), mask)) {
360 return false;
361 }
362
363 // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a
364 // clean way to share more code?
365 SkBitmap bitmap;
366 bitmap.installMaskPixels(*mask);
367
368 SkCanvas canvas(bitmap);
369 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
370 -SkIntToScalar(mask->fBounds.top()));
371
372 SkPaint paint;
373 paint.setAntiAlias(true);
374 canvas.drawRRect(rrect, paint);
375 return true;
376 }
377
draw_rects_into_mask(const SkRect rects[],int count,SkMask * mask)378 static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) {
379 if (!prepare_to_draw_into_mask(rects[0], mask)) {
380 return false;
381 }
382
383 SkBitmap bitmap;
384 bitmap.installPixels(SkImageInfo::Make(mask->fBounds.width(),
385 mask->fBounds.height(),
386 kAlpha_8_SkColorType,
387 kPremul_SkAlphaType),
388 mask->fImage, mask->fRowBytes);
389
390 SkCanvas canvas(bitmap);
391 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
392 -SkIntToScalar(mask->fBounds.top()));
393
394 SkPaint paint;
395 paint.setAntiAlias(true);
396
397 if (1 == count) {
398 canvas.drawRect(rects[0], paint);
399 } else {
400 // todo: do I need a fast way to do this?
401 SkPath path;
402 path.addRect(rects[0]);
403 path.addRect(rects[1]);
404 path.setFillType(SkPath::kEvenOdd_FillType);
405 canvas.drawPath(path, paint);
406 }
407 return true;
408 }
409
rect_exceeds(const SkRect & r,SkScalar v)410 static bool rect_exceeds(const SkRect& r, SkScalar v) {
411 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
412 r.width() > v || r.height() > v;
413 }
414
415 #include "SkMaskCache.h"
416
copy_mask_to_cacheddata(SkMask * mask)417 static SkCachedData* copy_mask_to_cacheddata(SkMask* mask) {
418 const size_t size = mask->computeTotalImageSize();
419 SkCachedData* data = SkResourceCache::NewCachedData(size);
420 if (data) {
421 memcpy(data->writable_data(), mask->fImage, size);
422 SkMask::FreeImage(mask->fImage);
423 mask->fImage = (uint8_t*)data->data();
424 }
425 return data;
426 }
427
find_cached_rrect(SkMask * mask,SkScalar sigma,SkBlurStyle style,SkBlurQuality quality,const SkRRect & rrect)428 static SkCachedData* find_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
429 SkBlurQuality quality, const SkRRect& rrect) {
430 return SkMaskCache::FindAndRef(sigma, style, quality, rrect, mask);
431 }
432
add_cached_rrect(SkMask * mask,SkScalar sigma,SkBlurStyle style,SkBlurQuality quality,const SkRRect & rrect)433 static SkCachedData* add_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
434 SkBlurQuality quality, const SkRRect& rrect) {
435 SkCachedData* cache = copy_mask_to_cacheddata(mask);
436 if (cache) {
437 SkMaskCache::Add(sigma, style, quality, rrect, *mask, cache);
438 }
439 return cache;
440 }
441
find_cached_rects(SkMask * mask,SkScalar sigma,SkBlurStyle style,SkBlurQuality quality,const SkRect rects[],int count)442 static SkCachedData* find_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
443 SkBlurQuality quality, const SkRect rects[], int count) {
444 return SkMaskCache::FindAndRef(sigma, style, quality, rects, count, mask);
445 }
446
add_cached_rects(SkMask * mask,SkScalar sigma,SkBlurStyle style,SkBlurQuality quality,const SkRect rects[],int count)447 static SkCachedData* add_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
448 SkBlurQuality quality, const SkRect rects[], int count) {
449 SkCachedData* cache = copy_mask_to_cacheddata(mask);
450 if (cache) {
451 SkMaskCache::Add(sigma, style, quality, rects, count, *mask, cache);
452 }
453 return cache;
454 }
455
456 #ifdef SK_IGNORE_FAST_RRECT_BLUR
457 // Use the faster analytic blur approach for ninepatch round rects
458 static const bool c_analyticBlurRRect{false};
459 #else
460 static const bool c_analyticBlurRRect{true};
461 #endif
462
463 SkMaskFilter::FilterReturn
filterRRectToNine(const SkRRect & rrect,const SkMatrix & matrix,const SkIRect & clipBounds,NinePatch * patch) const464 SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix,
465 const SkIRect& clipBounds,
466 NinePatch* patch) const {
467 SkASSERT(patch != nullptr);
468 switch (rrect.getType()) {
469 case SkRRect::kEmpty_Type:
470 // Nothing to draw.
471 return kFalse_FilterReturn;
472
473 case SkRRect::kRect_Type:
474 // We should have caught this earlier.
475 SkASSERT(false);
476 // Fall through.
477 case SkRRect::kOval_Type:
478 // The nine patch special case does not handle ovals, and we
479 // already have code for rectangles.
480 return kUnimplemented_FilterReturn;
481
482 // These three can take advantage of this fast path.
483 case SkRRect::kSimple_Type:
484 case SkRRect::kNinePatch_Type:
485 case SkRRect::kComplex_Type:
486 break;
487 }
488
489 // TODO: report correct metrics for innerstyle, where we do not grow the
490 // total bounds, but we do need an inset the size of our blur-radius
491 if (kInner_SkBlurStyle == fBlurStyle) {
492 return kUnimplemented_FilterReturn;
493 }
494
495 // TODO: take clipBounds into account to limit our coordinates up front
496 // for now, just skip too-large src rects (to take the old code path).
497 if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) {
498 return kUnimplemented_FilterReturn;
499 }
500
501 SkIPoint margin;
502 SkMask srcM, dstM;
503 srcM.fBounds = rrect.rect().roundOut();
504 srcM.fFormat = SkMask::kA8_Format;
505 srcM.fRowBytes = 0;
506
507 bool filterResult = false;
508 if (c_analyticBlurRRect) {
509 // special case for fast round rect blur
510 // don't actually do the blur the first time, just compute the correct size
511 filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin,
512 SkMask::kJustComputeBounds_CreateMode);
513 }
514
515 if (!filterResult) {
516 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
517 }
518
519 if (!filterResult) {
520 return kFalse_FilterReturn;
521 }
522
523 // Now figure out the appropriate width and height of the smaller round rectangle
524 // to stretch. It will take into account the larger radius per side as well as double
525 // the margin, to account for inner and outer blur.
526 const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
527 const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
528 const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
529 const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
530
531 const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX);
532 const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX);
533
534 // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover
535 // any fractional space on either side plus 1 for the part to stretch.
536 const SkScalar stretchSize = SkIntToScalar(3);
537
538 const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize;
539 if (totalSmallWidth >= rrect.rect().width()) {
540 // There is no valid piece to stretch.
541 return kUnimplemented_FilterReturn;
542 }
543
544 const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY);
545 const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY);
546
547 const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize;
548 if (totalSmallHeight >= rrect.rect().height()) {
549 // There is no valid piece to stretch.
550 return kUnimplemented_FilterReturn;
551 }
552
553 SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight);
554
555 SkRRect smallRR;
556 SkVector radii[4];
557 radii[SkRRect::kUpperLeft_Corner] = UL;
558 radii[SkRRect::kUpperRight_Corner] = UR;
559 radii[SkRRect::kLowerRight_Corner] = LR;
560 radii[SkRRect::kLowerLeft_Corner] = LL;
561 smallRR.setRectRadii(smallR, radii);
562
563 const SkScalar sigma = this->computeXformedSigma(matrix);
564 SkCachedData* cache = find_cached_rrect(&patch->fMask, sigma, fBlurStyle,
565 this->getQuality(), smallRR);
566 if (!cache) {
567 bool analyticBlurWorked = false;
568 if (c_analyticBlurRRect) {
569 analyticBlurWorked =
570 this->filterRRectMask(&patch->fMask, smallRR, matrix, &margin,
571 SkMask::kComputeBoundsAndRenderImage_CreateMode);
572 }
573
574 if (!analyticBlurWorked) {
575 if (!draw_rrect_into_mask(smallRR, &srcM)) {
576 return kFalse_FilterReturn;
577 }
578
579 SkAutoMaskFreeImage amf(srcM.fImage);
580
581 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
582 return kFalse_FilterReturn;
583 }
584 }
585 cache = add_cached_rrect(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallRR);
586 }
587
588 patch->fMask.fBounds.offsetTo(0, 0);
589 patch->fOuterRect = dstM.fBounds;
590 patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1;
591 patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1;
592 SkASSERT(nullptr == patch->fCache);
593 patch->fCache = cache; // transfer ownership to patch
594 return kTrue_FilterReturn;
595 }
596
597 // Use the faster analytic blur approach for ninepatch rects
598 static const bool c_analyticBlurNinepatch{true};
599
600 SkMaskFilter::FilterReturn
filterRectsToNine(const SkRect rects[],int count,const SkMatrix & matrix,const SkIRect & clipBounds,NinePatch * patch) const601 SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
602 const SkMatrix& matrix,
603 const SkIRect& clipBounds,
604 NinePatch* patch) const {
605 if (count < 1 || count > 2) {
606 return kUnimplemented_FilterReturn;
607 }
608
609 // TODO: report correct metrics for innerstyle, where we do not grow the
610 // total bounds, but we do need an inset the size of our blur-radius
611 if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) {
612 return kUnimplemented_FilterReturn;
613 }
614
615 // TODO: take clipBounds into account to limit our coordinates up front
616 // for now, just skip too-large src rects (to take the old code path).
617 if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
618 return kUnimplemented_FilterReturn;
619 }
620
621 SkIPoint margin;
622 SkMask srcM, dstM;
623 srcM.fBounds = rects[0].roundOut();
624 srcM.fFormat = SkMask::kA8_Format;
625 srcM.fRowBytes = 0;
626
627 bool filterResult = false;
628 if (count == 1 && c_analyticBlurNinepatch) {
629 // special case for fast rect blur
630 // don't actually do the blur the first time, just compute the correct size
631 filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
632 SkMask::kJustComputeBounds_CreateMode);
633 } else {
634 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
635 }
636
637 if (!filterResult) {
638 return kFalse_FilterReturn;
639 }
640
641 /*
642 * smallR is the smallest version of 'rect' that will still guarantee that
643 * we get the same blur results on all edges, plus 1 center row/col that is
644 * representative of the extendible/stretchable edges of the ninepatch.
645 * Since our actual edge may be fractional we inset 1 more to be sure we
646 * don't miss any interior blur.
647 * x is an added pixel of blur, and { and } are the (fractional) edge
648 * pixels from the original rect.
649 *
650 * x x { x x .... x x } x x
651 *
652 * Thus, in this case, we inset by a total of 5 (on each side) beginning
653 * with our outer-rect (dstM.fBounds)
654 */
655 SkRect smallR[2];
656 SkIPoint center;
657
658 // +2 is from +1 for each edge (to account for possible fractional edges
659 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
660 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
661 SkIRect innerIR;
662
663 if (1 == count) {
664 innerIR = srcM.fBounds;
665 center.set(smallW, smallH);
666 } else {
667 SkASSERT(2 == count);
668 rects[1].roundIn(&innerIR);
669 center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
670 smallH + (innerIR.top() - srcM.fBounds.top()));
671 }
672
673 // +1 so we get a clean, stretchable, center row/col
674 smallW += 1;
675 smallH += 1;
676
677 // we want the inset amounts to be integral, so we don't change any
678 // fractional phase on the fRight or fBottom of our smallR.
679 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
680 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
681 if (dx < 0 || dy < 0) {
682 // we're too small, relative to our blur, to break into nine-patch,
683 // so we ask to have our normal filterMask() be called.
684 return kUnimplemented_FilterReturn;
685 }
686
687 smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
688 if (smallR[0].width() < 2 || smallR[0].height() < 2) {
689 return kUnimplemented_FilterReturn;
690 }
691 if (2 == count) {
692 smallR[1].set(rects[1].left(), rects[1].top(),
693 rects[1].right() - dx, rects[1].bottom() - dy);
694 SkASSERT(!smallR[1].isEmpty());
695 }
696
697 const SkScalar sigma = this->computeXformedSigma(matrix);
698 SkCachedData* cache = find_cached_rects(&patch->fMask, sigma, fBlurStyle,
699 this->getQuality(), smallR, count);
700 if (!cache) {
701 if (count > 1 || !c_analyticBlurNinepatch) {
702 if (!draw_rects_into_mask(smallR, count, &srcM)) {
703 return kFalse_FilterReturn;
704 }
705
706 SkAutoMaskFreeImage amf(srcM.fImage);
707
708 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
709 return kFalse_FilterReturn;
710 }
711 } else {
712 if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
713 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
714 return kFalse_FilterReturn;
715 }
716 }
717 cache = add_cached_rects(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallR, count);
718 }
719 patch->fMask.fBounds.offsetTo(0, 0);
720 patch->fOuterRect = dstM.fBounds;
721 patch->fCenter = center;
722 SkASSERT(nullptr == patch->fCache);
723 patch->fCache = cache; // transfer ownership to patch
724 return kTrue_FilterReturn;
725 }
726
computeFastBounds(const SkRect & src,SkRect * dst) const727 void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
728 SkRect* dst) const {
729 SkScalar pad = 3.0f * fSigma;
730
731 dst->set(src.fLeft - pad, src.fTop - pad,
732 src.fRight + pad, src.fBottom + pad);
733 }
734
CreateProc(SkReadBuffer & buffer)735 sk_sp<SkFlattenable> SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
736 const SkScalar sigma = buffer.readScalar();
737 const unsigned style = buffer.readUInt();
738 unsigned flags = buffer.readUInt();
739
740 buffer.validate(style <= kLastEnum_SkBlurStyle);
741 buffer.validate(!(flags & ~SkBlurMaskFilter::kAll_BlurFlag));
742
743 flags &= SkBlurMaskFilter::kAll_BlurFlag;
744
745 SkRect occluder;
746 buffer.readRect(&occluder);
747
748 if (style <= kLastEnum_SkBlurStyle) {
749 return SkBlurMaskFilter::Make((SkBlurStyle)style, sigma, occluder, flags);
750 }
751 return nullptr;
752 }
753
flatten(SkWriteBuffer & buffer) const754 void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
755 buffer.writeScalar(fSigma);
756 buffer.writeUInt(fBlurStyle);
757 buffer.writeUInt(fBlurFlags);
758 buffer.writeRect(fOccluder);
759 }
760
761
762 #if SK_SUPPORT_GPU
763
764 class GrGLRectBlurEffect;
765
766 class GrRectBlurEffect : public GrFragmentProcessor {
767 public:
~GrRectBlurEffect()768 ~GrRectBlurEffect() override { }
769
name() const770 const char* name() const override { return "RectBlur"; }
771
Make(GrResourceProvider * resourceProvider,const SkRect & rect,float sigma)772 static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider,
773 const SkRect& rect, float sigma) {
774 int doubleProfileSize = SkScalarCeilToInt(12*sigma);
775
776 if (doubleProfileSize >= rect.width() || doubleProfileSize >= rect.height()) {
777 // if the blur sigma is too large so the gaussian overlaps the whole
778 // rect in either direction, fall back to CPU path for now.
779 return nullptr;
780 }
781
782 sk_sp<GrTextureProxy> blurProfile(CreateBlurProfileTexture(resourceProvider, sigma));
783 if (!blurProfile) {
784 return nullptr;
785 }
786 // in OpenGL ES, mediump floats have a minimum range of 2^14. If we have coordinates bigger
787 // than that, the shader math will end up with infinities and result in the blur effect not
788 // working correctly. To avoid this, we switch into highp when the coordinates are too big.
789 // As 2^14 is the minimum range but the actual range can be bigger, we might end up
790 // switching to highp sooner than strictly necessary, but most devices that have a bigger
791 // range for mediump also have mediump being exactly the same as highp (e.g. all non-OpenGL
792 // ES devices), and thus incur no additional penalty for the switch.
793 static const SkScalar kMAX_BLUR_COORD = SkIntToScalar(16000);
794 GrSLPrecision precision;
795 if (SkScalarAbs(rect.top()) > kMAX_BLUR_COORD ||
796 SkScalarAbs(rect.left()) > kMAX_BLUR_COORD ||
797 SkScalarAbs(rect.bottom()) > kMAX_BLUR_COORD ||
798 SkScalarAbs(rect.right()) > kMAX_BLUR_COORD ||
799 SkScalarAbs(rect.width()) > kMAX_BLUR_COORD ||
800 SkScalarAbs(rect.height()) > kMAX_BLUR_COORD) {
801 precision = kHigh_GrSLPrecision;
802 } else {
803 precision = kDefault_GrSLPrecision;
804 }
805
806 return sk_sp<GrFragmentProcessor>(new GrRectBlurEffect(rect, sigma,
807 std::move(blurProfile), precision));
808 }
809
getRect() const810 const SkRect& getRect() const { return fRect; }
getSigma() const811 float getSigma() const { return fSigma; }
precision() const812 GrSLPrecision precision() const { return fPrecision; }
813
814 private:
815 GrRectBlurEffect(const SkRect& rect, float sigma,
816 sk_sp<GrTextureProxy> blurProfile, GrSLPrecision fPrecision);
817
818 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
819
820 void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
821
822 bool onIsEqual(const GrFragmentProcessor&) const override;
823
824 static sk_sp<GrTextureProxy> CreateBlurProfileTexture(GrResourceProvider*, float sigma);
825
826 SkRect fRect;
827 float fSigma;
828 TextureSampler fBlurProfileSampler;
829 GrSLPrecision fPrecision;
830
831 GR_DECLARE_FRAGMENT_PROCESSOR_TEST
832
833 typedef GrFragmentProcessor INHERITED;
834 };
835
836 class GrGLRectBlurEffect : public GrGLSLFragmentProcessor {
837 public:
838 void emitCode(EmitArgs&) override;
839
840 static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder* b);
841
842 protected:
843 void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
844
845 private:
846 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
847
848 UniformHandle fProxyRectUniform;
849 UniformHandle fProfileSizeUniform;
850
851 typedef GrGLSLFragmentProcessor INHERITED;
852 };
853
OutputRectBlurProfileLookup(GrGLSLFPFragmentBuilder * fragBuilder,GrGLSLFragmentProcessor::SamplerHandle sampler,const char * output,const char * profileSize,const char * loc,const char * blurred_width,const char * sharp_width)854 void OutputRectBlurProfileLookup(GrGLSLFPFragmentBuilder* fragBuilder,
855 GrGLSLFragmentProcessor::SamplerHandle sampler,
856 const char *output,
857 const char *profileSize, const char *loc,
858 const char *blurred_width,
859 const char *sharp_width) {
860 fragBuilder->codeAppendf("float %s;", output);
861 fragBuilder->codeAppendf("{");
862 fragBuilder->codeAppendf("float coord = ((abs(%s - 0.5 * %s) - 0.5 * %s)) / %s;",
863 loc, blurred_width, sharp_width, profileSize);
864 fragBuilder->codeAppendf("%s = ", output);
865 fragBuilder->appendTextureLookup(sampler, "vec2(coord,0.5)");
866 fragBuilder->codeAppend(".a;");
867 fragBuilder->codeAppendf("}");
868 }
869
870
GenKey(const GrProcessor & proc,const GrShaderCaps &,GrProcessorKeyBuilder * b)871 void GrGLRectBlurEffect::GenKey(const GrProcessor& proc, const GrShaderCaps&,
872 GrProcessorKeyBuilder* b) {
873 const GrRectBlurEffect& rbe = proc.cast<GrRectBlurEffect>();
874
875 b->add32(rbe.precision());
876 }
877
878
emitCode(EmitArgs & args)879 void GrGLRectBlurEffect::emitCode(EmitArgs& args) {
880 const GrRectBlurEffect& rbe = args.fFp.cast<GrRectBlurEffect>();
881
882 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
883
884 const char *rectName;
885 const char *profileSizeName;
886
887 SkString precisionString;
888 if (args.fShaderCaps->usesPrecisionModifiers()) {
889 precisionString.printf("%s ", GrGLSLPrecisionString(rbe.precision()));
890 }
891 fProxyRectUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
892 kVec4f_GrSLType,
893 rbe.precision(),
894 "proxyRect",
895 &rectName);
896 fProfileSizeUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
897 kFloat_GrSLType,
898 kDefault_GrSLPrecision,
899 "profileSize",
900 &profileSizeName);
901
902 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
903
904 if (args.fInputColor) {
905 fragBuilder->codeAppendf("vec4 src=%s;", args.fInputColor);
906 } else {
907 fragBuilder->codeAppendf("vec4 src=vec4(1);");
908 }
909
910 fragBuilder->codeAppendf("%s vec2 translatedPos = sk_FragCoord.xy - %s.xy;",
911 precisionString.c_str(), rectName);
912 fragBuilder->codeAppendf("%s float width = %s.z - %s.x;", precisionString.c_str(), rectName,
913 rectName);
914 fragBuilder->codeAppendf("%s float height = %s.w - %s.y;", precisionString.c_str(), rectName,
915 rectName);
916
917 fragBuilder->codeAppendf("%s vec2 smallDims = vec2(width - %s, height - %s);",
918 precisionString.c_str(), profileSizeName, profileSizeName);
919 fragBuilder->codeAppendf("%s float center = 2.0 * floor(%s/2.0 + .25) - 1.0;",
920 precisionString.c_str(), profileSizeName);
921 fragBuilder->codeAppendf("%s vec2 wh = smallDims - vec2(center,center);",
922 precisionString.c_str());
923
924 OutputRectBlurProfileLookup(fragBuilder, args.fTexSamplers[0], "horiz_lookup", profileSizeName,
925 "translatedPos.x", "width", "wh.x");
926 OutputRectBlurProfileLookup(fragBuilder, args.fTexSamplers[0], "vert_lookup", profileSizeName,
927 "translatedPos.y", "height", "wh.y");
928
929 fragBuilder->codeAppendf("float final = horiz_lookup * vert_lookup;");
930 fragBuilder->codeAppendf("%s = src * final;", args.fOutputColor);
931 }
932
onSetData(const GrGLSLProgramDataManager & pdman,const GrFragmentProcessor & proc)933 void GrGLRectBlurEffect::onSetData(const GrGLSLProgramDataManager& pdman,
934 const GrFragmentProcessor& proc) {
935 const GrRectBlurEffect& rbe = proc.cast<GrRectBlurEffect>();
936 SkRect rect = rbe.getRect();
937
938 pdman.set4f(fProxyRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
939 pdman.set1f(fProfileSizeUniform, SkScalarCeilToScalar(6*rbe.getSigma()));
940 }
941
CreateBlurProfileTexture(GrResourceProvider * resourceProvider,float sigma)942 sk_sp<GrTextureProxy> GrRectBlurEffect::CreateBlurProfileTexture(
943 GrResourceProvider* resourceProvider,
944 float sigma) {
945 GrSurfaceDesc texDesc;
946
947 unsigned int profileSize = SkScalarCeilToInt(6*sigma);
948
949 texDesc.fWidth = profileSize;
950 texDesc.fHeight = 1;
951 texDesc.fConfig = kAlpha_8_GrPixelConfig;
952 texDesc.fIsMipMapped = false;
953
954 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
955 GrUniqueKey key;
956 GrUniqueKey::Builder builder(&key, kDomain, 1);
957 builder[0] = profileSize;
958 builder.finish();
959
960 sk_sp<GrTextureProxy> blurProfile(resourceProvider->findProxyByUniqueKey(key));
961 if (!blurProfile) {
962 std::unique_ptr<uint8_t[]> profile(SkBlurMask::ComputeBlurProfile(sigma));
963
964 blurProfile = GrSurfaceProxy::MakeDeferred(resourceProvider,
965 texDesc, SkBudgeted::kYes, profile.get(), 0);
966 if (!blurProfile) {
967 return nullptr;
968 }
969
970 resourceProvider->assignUniqueKeyToProxy(key, blurProfile.get());
971 }
972
973 return blurProfile;
974 }
975
GrRectBlurEffect(const SkRect & rect,float sigma,sk_sp<GrTextureProxy> blurProfile,GrSLPrecision precision)976 GrRectBlurEffect::GrRectBlurEffect(const SkRect& rect, float sigma,
977 sk_sp<GrTextureProxy> blurProfile,
978 GrSLPrecision precision)
979 : INHERITED(kCompatibleWithCoverageAsAlpha_OptimizationFlag)
980 , fRect(rect)
981 , fSigma(sigma)
982 , fBlurProfileSampler(std::move(blurProfile))
983 , fPrecision(precision) {
984 this->initClassID<GrRectBlurEffect>();
985 this->addTextureSampler(&fBlurProfileSampler);
986 }
987
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const988 void GrRectBlurEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
989 GrProcessorKeyBuilder* b) const {
990 GrGLRectBlurEffect::GenKey(*this, caps, b);
991 }
992
onCreateGLSLInstance() const993 GrGLSLFragmentProcessor* GrRectBlurEffect::onCreateGLSLInstance() const {
994 return new GrGLRectBlurEffect;
995 }
996
onIsEqual(const GrFragmentProcessor & sBase) const997 bool GrRectBlurEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
998 const GrRectBlurEffect& s = sBase.cast<GrRectBlurEffect>();
999 return this->getSigma() == s.getSigma() && this->getRect() == s.getRect();
1000 }
1001
1002 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRectBlurEffect);
1003
1004 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)1005 sk_sp<GrFragmentProcessor> GrRectBlurEffect::TestCreate(GrProcessorTestData* d) {
1006 float sigma = d->fRandom->nextRangeF(3,8);
1007 float width = d->fRandom->nextRangeF(200,300);
1008 float height = d->fRandom->nextRangeF(200,300);
1009 return GrRectBlurEffect::Make(d->resourceProvider(),
1010 SkRect::MakeWH(width, height), sigma);
1011 }
1012 #endif
1013
directFilterMaskGPU(GrContext * context,GrRenderTargetContext * renderTargetContext,GrPaint && paint,const GrClip & clip,const SkMatrix & viewMatrix,const SkStrokeRec & strokeRec,const SkPath & path) const1014 bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrContext* context,
1015 GrRenderTargetContext* renderTargetContext,
1016 GrPaint&& paint,
1017 const GrClip& clip,
1018 const SkMatrix& viewMatrix,
1019 const SkStrokeRec& strokeRec,
1020 const SkPath& path) const {
1021 SkASSERT(renderTargetContext);
1022
1023 if (fBlurStyle != kNormal_SkBlurStyle) {
1024 return false;
1025 }
1026
1027 // TODO: we could handle blurred stroked circles
1028 if (!strokeRec.isFillStyle()) {
1029 return false;
1030 }
1031
1032 SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
1033
1034 GrResourceProvider* resourceProvider = context->resourceProvider();
1035 sk_sp<GrFragmentProcessor> fp;
1036
1037 SkRect rect;
1038 if (path.isRect(&rect)) {
1039 SkScalar pad = 3.0f * xformedSigma;
1040 rect.outset(pad, pad);
1041
1042 fp = GrRectBlurEffect::Make(resourceProvider, rect, xformedSigma);
1043 } else if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height())) {
1044 fp = GrCircleBlurFragmentProcessor::Make(resourceProvider, rect, xformedSigma);
1045
1046 // expand the rect for the coverage geometry
1047 int pad = SkScalarCeilToInt(6*xformedSigma)/2;
1048 rect.outset(SkIntToScalar(pad), SkIntToScalar(pad));
1049 } else {
1050 return false;
1051 }
1052
1053 if (!fp) {
1054 return false;
1055 }
1056
1057 SkMatrix inverse;
1058 if (!viewMatrix.invert(&inverse)) {
1059 return false;
1060 }
1061
1062 paint.addCoverageFragmentProcessor(std::move(fp));
1063 renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
1064 rect, inverse);
1065 return true;
1066 }
1067
1068 //////////////////////////////////////////////////////////////////////////////
1069
1070 class GrRRectBlurEffect : public GrFragmentProcessor {
1071 public:
1072 static sk_sp<GrFragmentProcessor> Make(GrContext*,
1073 float sigma, float xformedSigma,
1074 const SkRRect& srcRRect, const SkRRect& devRRect);
1075
~GrRRectBlurEffect()1076 ~GrRRectBlurEffect() override {}
name() const1077 const char* name() const override { return "GrRRectBlur"; }
1078
getRRect() const1079 const SkRRect& getRRect() const { return fRRect; }
getSigma() const1080 float getSigma() const { return fSigma; }
1081
1082 private:
1083 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
1084
1085 GrRRectBlurEffect(float sigma, const SkRRect&,
1086 sk_sp<GrTextureProxy> profileProxy);
1087
1088 virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
1089 GrProcessorKeyBuilder* b) const override;
1090
1091 bool onIsEqual(const GrFragmentProcessor& other) const override;
1092
1093 SkRRect fRRect;
1094 float fSigma;
1095 TextureSampler fNinePatchSampler;
1096
1097 GR_DECLARE_FRAGMENT_PROCESSOR_TEST
1098
1099 typedef GrFragmentProcessor INHERITED;
1100 };
1101
find_or_create_rrect_blur_mask(GrContext * context,const SkRRect & rrectToDraw,const SkISize & size,float xformedSigma)1102 static sk_sp<GrTextureProxy> find_or_create_rrect_blur_mask(GrContext* context,
1103 const SkRRect& rrectToDraw,
1104 const SkISize& size,
1105 float xformedSigma) {
1106 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
1107 GrUniqueKey key;
1108 GrUniqueKey::Builder builder(&key, kDomain, 9);
1109 builder[0] = SkScalarCeilToInt(xformedSigma-1/6.0f);
1110
1111 int index = 1;
1112 for (auto c : { SkRRect::kUpperLeft_Corner, SkRRect::kUpperRight_Corner,
1113 SkRRect::kLowerRight_Corner, SkRRect::kLowerLeft_Corner }) {
1114 SkASSERT(SkScalarIsInt(rrectToDraw.radii(c).fX) && SkScalarIsInt(rrectToDraw.radii(c).fY));
1115 builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fX);
1116 builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fY);
1117 }
1118 builder.finish();
1119
1120 sk_sp<GrTextureProxy> mask(context->resourceProvider()->findProxyByUniqueKey(key));
1121 if (!mask) {
1122 // TODO: this could be approx but the texture coords will need to be updated
1123 sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContextWithFallback(
1124 SkBackingFit::kExact, size.fWidth, size.fHeight, kAlpha_8_GrPixelConfig, nullptr));
1125 if (!rtc) {
1126 return nullptr;
1127 }
1128
1129 GrPaint paint;
1130
1131 rtc->clear(nullptr, 0x0, true);
1132 rtc->drawRRect(GrNoClip(), std::move(paint), GrAA::kYes, SkMatrix::I(), rrectToDraw,
1133 GrStyle::SimpleFill());
1134
1135 sk_sp<GrTextureProxy> srcProxy(rtc->asTextureProxyRef());
1136 if (!srcProxy) {
1137 return nullptr;
1138 }
1139 sk_sp<GrRenderTargetContext> rtc2(
1140 SkGpuBlurUtils::GaussianBlur(context,
1141 std::move(srcProxy),
1142 nullptr,
1143 SkIRect::MakeWH(size.fWidth, size.fHeight),
1144 SkIRect::EmptyIRect(),
1145 xformedSigma,
1146 xformedSigma,
1147 GrTextureDomain::kIgnore_Mode,
1148 SkBackingFit::kExact));
1149 if (!rtc2) {
1150 return nullptr;
1151 }
1152
1153 mask = rtc2->asTextureProxyRef();
1154 if (!mask) {
1155 return nullptr;
1156 }
1157 context->resourceProvider()->assignUniqueKeyToProxy(key, mask.get());
1158 }
1159
1160 return mask;
1161 }
1162
Make(GrContext * context,float sigma,float xformedSigma,const SkRRect & srcRRect,const SkRRect & devRRect)1163 sk_sp<GrFragmentProcessor> GrRRectBlurEffect::Make(GrContext* context,
1164 float sigma, float xformedSigma,
1165 const SkRRect& srcRRect, const SkRRect& devRRect) {
1166 SkASSERT(!devRRect.isCircle() && !devRRect.isRect()); // Should've been caught up-stream
1167
1168 // TODO: loosen this up
1169 if (!devRRect.isSimpleCircular()) {
1170 return nullptr;
1171 }
1172
1173 // Make sure we can successfully ninepatch this rrect -- the blur sigma has to be
1174 // sufficiently small relative to both the size of the corner radius and the
1175 // width (and height) of the rrect.
1176 SkRRect rrectToDraw;
1177 SkISize size;
1178 SkScalar ignored[SkBlurMaskFilter::kMaxDivisions];
1179 int ignoredSize;
1180 uint32_t ignored32;
1181
1182 bool ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(srcRRect, devRRect,
1183 SkRect::MakeEmpty(),
1184 sigma, xformedSigma,
1185 &rrectToDraw, &size,
1186 ignored, ignored,
1187 ignored, ignored,
1188 &ignoredSize, &ignoredSize,
1189 &ignored32);
1190 if (!ninePatchable) {
1191 return nullptr;
1192 }
1193
1194 sk_sp<GrTextureProxy> mask(find_or_create_rrect_blur_mask(context, rrectToDraw,
1195 size, xformedSigma));
1196 if (!mask) {
1197 return nullptr;
1198 }
1199
1200 return sk_sp<GrFragmentProcessor>(new GrRRectBlurEffect(xformedSigma,
1201 devRRect,
1202 std::move(mask)));
1203 }
1204
GrRRectBlurEffect(float sigma,const SkRRect & rrect,sk_sp<GrTextureProxy> ninePatchProxy)1205 GrRRectBlurEffect::GrRRectBlurEffect(float sigma, const SkRRect& rrect,
1206 sk_sp<GrTextureProxy> ninePatchProxy)
1207 : INHERITED(kCompatibleWithCoverageAsAlpha_OptimizationFlag)
1208 , fRRect(rrect)
1209 , fSigma(sigma)
1210 , fNinePatchSampler(std::move(ninePatchProxy)) {
1211 this->initClassID<GrRRectBlurEffect>();
1212 this->addTextureSampler(&fNinePatchSampler);
1213 }
1214
onIsEqual(const GrFragmentProcessor & other) const1215 bool GrRRectBlurEffect::onIsEqual(const GrFragmentProcessor& other) const {
1216 const GrRRectBlurEffect& rrbe = other.cast<GrRRectBlurEffect>();
1217 return fRRect.getSimpleRadii().fX == rrbe.fRRect.getSimpleRadii().fX &&
1218 fSigma == rrbe.fSigma &&
1219 fRRect.rect() == rrbe.fRRect.rect();
1220 }
1221
1222 //////////////////////////////////////////////////////////////////////////////
1223
1224 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRRectBlurEffect);
1225
1226 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)1227 sk_sp<GrFragmentProcessor> GrRRectBlurEffect::TestCreate(GrProcessorTestData* d) {
1228 SkScalar w = d->fRandom->nextRangeScalar(100.f, 1000.f);
1229 SkScalar h = d->fRandom->nextRangeScalar(100.f, 1000.f);
1230 SkScalar r = d->fRandom->nextRangeF(1.f, 9.f);
1231 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f);
1232 SkRRect rrect;
1233 rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
1234 return GrRRectBlurEffect::Make(d->context(), sigma, sigma, rrect, rrect);
1235 }
1236 #endif
1237
1238 //////////////////////////////////////////////////////////////////////////////
1239
1240 class GrGLRRectBlurEffect : public GrGLSLFragmentProcessor {
1241 public:
1242 void emitCode(EmitArgs&) override;
1243
1244 protected:
1245 void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
1246
1247 private:
1248 GrGLSLProgramDataManager::UniformHandle fProxyRectUniform;
1249 GrGLSLProgramDataManager::UniformHandle fCornerRadiusUniform;
1250 GrGLSLProgramDataManager::UniformHandle fBlurRadiusUniform;
1251 typedef GrGLSLFragmentProcessor INHERITED;
1252 };
1253
emitCode(EmitArgs & args)1254 void GrGLRRectBlurEffect::emitCode(EmitArgs& args) {
1255 const char *rectName;
1256 const char *cornerRadiusName;
1257 const char *blurRadiusName;
1258
1259 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
1260 // The proxy rect has left, top, right, and bottom edges correspond to
1261 // components x, y, z, and w, respectively.
1262
1263 fProxyRectUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
1264 kVec4f_GrSLType,
1265 kDefault_GrSLPrecision,
1266 "proxyRect",
1267 &rectName);
1268 fCornerRadiusUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
1269 kFloat_GrSLType,
1270 kDefault_GrSLPrecision,
1271 "cornerRadius",
1272 &cornerRadiusName);
1273 fBlurRadiusUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
1274 kFloat_GrSLType,
1275 kDefault_GrSLPrecision,
1276 "blurRadius",
1277 &blurRadiusName);
1278
1279 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
1280
1281 // warp the fragment position to the appropriate part of the 9patch blur texture
1282
1283 fragBuilder->codeAppendf("vec2 rectCenter = (%s.xy + %s.zw)/2.0;", rectName, rectName);
1284 fragBuilder->codeAppendf("vec2 translatedFragPos = sk_FragCoord.xy - %s.xy;", rectName);
1285 fragBuilder->codeAppendf("float threshold = %s + 2.0*%s;", cornerRadiusName, blurRadiusName);
1286 fragBuilder->codeAppendf("vec2 middle = %s.zw - %s.xy - 2.0*threshold;", rectName, rectName);
1287
1288 fragBuilder->codeAppendf(
1289 "if (translatedFragPos.x >= threshold && translatedFragPos.x < (middle.x+threshold)) {");
1290 fragBuilder->codeAppendf("translatedFragPos.x = threshold;\n");
1291 fragBuilder->codeAppendf("} else if (translatedFragPos.x >= (middle.x + threshold)) {");
1292 fragBuilder->codeAppendf("translatedFragPos.x -= middle.x - 1.0;");
1293 fragBuilder->codeAppendf("}");
1294
1295 fragBuilder->codeAppendf(
1296 "if (translatedFragPos.y > threshold && translatedFragPos.y < (middle.y+threshold)) {");
1297 fragBuilder->codeAppendf("translatedFragPos.y = threshold;");
1298 fragBuilder->codeAppendf("} else if (translatedFragPos.y >= (middle.y + threshold)) {");
1299 fragBuilder->codeAppendf("translatedFragPos.y -= middle.y - 1.0;");
1300 fragBuilder->codeAppendf("}");
1301
1302 fragBuilder->codeAppendf("vec2 proxyDims = vec2(2.0*threshold+1.0);");
1303 fragBuilder->codeAppendf("vec2 texCoord = translatedFragPos / proxyDims;");
1304
1305 fragBuilder->codeAppendf("%s = ", args.fOutputColor);
1306 fragBuilder->appendTextureLookupAndModulate(args.fInputColor, args.fTexSamplers[0], "texCoord");
1307 fragBuilder->codeAppend(";");
1308 }
1309
onSetData(const GrGLSLProgramDataManager & pdman,const GrFragmentProcessor & proc)1310 void GrGLRRectBlurEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1311 const GrFragmentProcessor& proc) {
1312 const GrRRectBlurEffect& brre = proc.cast<GrRRectBlurEffect>();
1313 const SkRRect& rrect = brre.getRRect();
1314
1315 float blurRadius = 3.f*SkScalarCeilToScalar(brre.getSigma()-1/6.0f);
1316 pdman.set1f(fBlurRadiusUniform, blurRadius);
1317
1318 SkRect rect = rrect.getBounds();
1319 rect.outset(blurRadius, blurRadius);
1320 pdman.set4f(fProxyRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
1321
1322 SkScalar radius = 0;
1323 SkASSERT(rrect.isSimpleCircular() || rrect.isRect());
1324 radius = rrect.getSimpleRadii().fX;
1325 pdman.set1f(fCornerRadiusUniform, radius);
1326 }
1327
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const1328 void GrRRectBlurEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
1329 GrProcessorKeyBuilder* b) const {
1330 GrGLRRectBlurEffect::GenKey(*this, caps, b);
1331 }
1332
onCreateGLSLInstance() const1333 GrGLSLFragmentProcessor* GrRRectBlurEffect::onCreateGLSLInstance() const {
1334 return new GrGLRRectBlurEffect;
1335 }
1336
directFilterRRectMaskGPU(GrContext * context,GrRenderTargetContext * renderTargetContext,GrPaint && paint,const GrClip & clip,const SkMatrix & viewMatrix,const SkStrokeRec & strokeRec,const SkRRect & srcRRect,const SkRRect & devRRect) const1337 bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context,
1338 GrRenderTargetContext* renderTargetContext,
1339 GrPaint&& paint,
1340 const GrClip& clip,
1341 const SkMatrix& viewMatrix,
1342 const SkStrokeRec& strokeRec,
1343 const SkRRect& srcRRect,
1344 const SkRRect& devRRect) const {
1345 SkASSERT(renderTargetContext);
1346
1347 if (fBlurStyle != kNormal_SkBlurStyle) {
1348 return false;
1349 }
1350
1351 if (!strokeRec.isFillStyle()) {
1352 return false;
1353 }
1354
1355 GrResourceProvider* resourceProvider = context->resourceProvider();
1356 SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
1357
1358 if (devRRect.isRect() || devRRect.isCircle()) {
1359 sk_sp<GrFragmentProcessor> fp;
1360 if (devRRect.isRect()) {
1361 SkScalar pad = 3.0f * xformedSigma;
1362 const SkRect dstCoverageRect = devRRect.rect().makeOutset(pad, pad);
1363
1364 fp = GrRectBlurEffect::Make(resourceProvider, dstCoverageRect, xformedSigma);
1365 } else {
1366 fp = GrCircleBlurFragmentProcessor::Make(resourceProvider,
1367 devRRect.rect(), xformedSigma);
1368 }
1369
1370 if (!fp) {
1371 return false;
1372 }
1373 paint.addCoverageFragmentProcessor(std::move(fp));
1374
1375 SkRect srcProxyRect = srcRRect.rect();
1376 SkScalar outsetX = 3.0f*fSigma;
1377 SkScalar outsetY = 3.0f*fSigma;
1378 if (this->ignoreXform()) {
1379 // When we're ignoring the CTM the padding added to the source rect also needs to ignore
1380 // the CTM. The matrix passed in here is guaranteed to be just scale and translate so we
1381 // can just grab the X and Y scales off the matrix and pre-undo the scale.
1382 outsetX /= viewMatrix.getScaleX();
1383 outsetY /= viewMatrix.getScaleY();
1384 }
1385 srcProxyRect.outset(outsetX, outsetY);
1386
1387 renderTargetContext->drawRect(clip, std::move(paint), GrAA::kNo, viewMatrix, srcProxyRect);
1388 return true;
1389 }
1390
1391 sk_sp<GrFragmentProcessor> fp(GrRRectBlurEffect::Make(context, fSigma, xformedSigma,
1392 srcRRect, devRRect));
1393 if (!fp) {
1394 return false;
1395 }
1396
1397 if (!this->ignoreXform()) {
1398 SkRect srcProxyRect = srcRRect.rect();
1399 srcProxyRect.outset(3.0f*fSigma, 3.0f*fSigma);
1400
1401 sk_sp<SkVertices> vertices = nullptr;
1402 SkRect temp = fOccluder;
1403
1404 if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) {
1405 SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 8, 24, 0);
1406 srcProxyRect.toQuad(builder.positions());
1407 temp.toQuad(builder.positions() + 4);
1408
1409 static const uint16_t ringI[24] = { 0, 1, 5, 5, 4, 0,
1410 1, 2, 6, 6, 5, 1,
1411 2, 3, 7, 7, 6, 2,
1412 3, 0, 4, 4, 7, 3 };
1413 memcpy(builder.indices(), ringI, sizeof(ringI));
1414 vertices = builder.detach();
1415 } else {
1416 // full rect case
1417 SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 4, 6, 0);
1418 srcProxyRect.toQuad(builder.positions());
1419
1420 static const uint16_t fullI[6] = { 0, 1, 2, 0, 2, 3 };
1421 memcpy(builder.indices(), fullI, sizeof(fullI));
1422 vertices = builder.detach();
1423 }
1424
1425 paint.addCoverageFragmentProcessor(std::move(fp));
1426 renderTargetContext->drawVertices(clip, std::move(paint), viewMatrix, std::move(vertices));
1427 } else {
1428 SkMatrix inverse;
1429 if (!viewMatrix.invert(&inverse)) {
1430 return false;
1431 }
1432
1433 float extra=3.f*SkScalarCeilToScalar(xformedSigma-1/6.0f);
1434 SkRect proxyRect = devRRect.rect();
1435 proxyRect.outset(extra, extra);
1436
1437 paint.addCoverageFragmentProcessor(std::move(fp));
1438 renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo,
1439 SkMatrix::I(), proxyRect, inverse);
1440 }
1441
1442 return true;
1443 }
1444
canFilterMaskGPU(const SkRRect & devRRect,const SkIRect & clipBounds,const SkMatrix & ctm,SkRect * maskRect) const1445 bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
1446 const SkIRect& clipBounds,
1447 const SkMatrix& ctm,
1448 SkRect* maskRect) const {
1449 SkScalar xformedSigma = this->computeXformedSigma(ctm);
1450 if (xformedSigma <= 0) {
1451 return false;
1452 }
1453
1454 // We always do circles and simple circular rrects on the GPU
1455 if (!devRRect.isCircle() && !devRRect.isSimpleCircular()) {
1456 static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64);
1457 static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32);
1458
1459 if (devRRect.width() <= kMIN_GPU_BLUR_SIZE &&
1460 devRRect.height() <= kMIN_GPU_BLUR_SIZE &&
1461 xformedSigma <= kMIN_GPU_BLUR_SIGMA) {
1462 // We prefer to blur small rects with small radii on the CPU.
1463 return false;
1464 }
1465 }
1466
1467 if (nullptr == maskRect) {
1468 // don't need to compute maskRect
1469 return true;
1470 }
1471
1472 float sigma3 = 3 * SkScalarToFloat(xformedSigma);
1473
1474 SkRect clipRect = SkRect::Make(clipBounds);
1475 SkRect srcRect(devRRect.rect());
1476
1477 // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
1478 srcRect.outset(sigma3, sigma3);
1479 clipRect.outset(sigma3, sigma3);
1480 if (!srcRect.intersect(clipRect)) {
1481 srcRect.setEmpty();
1482 }
1483 *maskRect = srcRect;
1484 return true;
1485 }
1486
filterMaskGPU(GrContext * context,sk_sp<GrTextureProxy> srcProxy,const SkMatrix & ctm,const SkIRect & maskRect) const1487 sk_sp<GrTextureProxy> SkBlurMaskFilterImpl::filterMaskGPU(GrContext* context,
1488 sk_sp<GrTextureProxy> srcProxy,
1489 const SkMatrix& ctm,
1490 const SkIRect& maskRect) const {
1491 // 'maskRect' isn't snapped to the UL corner but the mask in 'src' is.
1492 const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
1493
1494 SkScalar xformedSigma = this->computeXformedSigma(ctm);
1495 SkASSERT(xformedSigma > 0);
1496
1497 // If we're doing a normal blur, we can clobber the pathTexture in the
1498 // gaussianBlur. Otherwise, we need to save it for later compositing.
1499 bool isNormalBlur = (kNormal_SkBlurStyle == fBlurStyle);
1500 sk_sp<GrRenderTargetContext> renderTargetContext(
1501 SkGpuBlurUtils::GaussianBlur(context,
1502 srcProxy,
1503 nullptr,
1504 clipRect,
1505 SkIRect::EmptyIRect(),
1506 xformedSigma,
1507 xformedSigma,
1508 GrTextureDomain::kIgnore_Mode));
1509 if (!renderTargetContext) {
1510 return nullptr;
1511 }
1512
1513 if (!isNormalBlur) {
1514 GrPaint paint;
1515 // Blend pathTexture over blurTexture.
1516 paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(std::move(srcProxy),
1517 nullptr, SkMatrix::I()));
1518 if (kInner_SkBlurStyle == fBlurStyle) {
1519 // inner: dst = dst * src
1520 paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op);
1521 } else if (kSolid_SkBlurStyle == fBlurStyle) {
1522 // solid: dst = src + dst - src * dst
1523 // = src + (1 - src) * dst
1524 paint.setCoverageSetOpXPFactory(SkRegion::kUnion_Op);
1525 } else if (kOuter_SkBlurStyle == fBlurStyle) {
1526 // outer: dst = dst * (1 - src)
1527 // = 0 * src + (1 - src) * dst
1528 paint.setCoverageSetOpXPFactory(SkRegion::kDifference_Op);
1529 } else {
1530 paint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
1531 }
1532
1533 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
1534 SkRect::Make(clipRect));
1535 }
1536
1537 return renderTargetContext->asTextureProxyRef();
1538 }
1539
1540 #endif // SK_SUPPORT_GPU
1541
1542
1543 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const1544 void SkBlurMaskFilterImpl::toString(SkString* str) const {
1545 str->append("SkBlurMaskFilterImpl: (");
1546
1547 str->append("sigma: ");
1548 str->appendScalar(fSigma);
1549 str->append(" ");
1550
1551 static const char* gStyleName[kLastEnum_SkBlurStyle + 1] = {
1552 "normal", "solid", "outer", "inner"
1553 };
1554
1555 str->appendf("style: %s ", gStyleName[fBlurStyle]);
1556 str->append("flags: (");
1557 if (fBlurFlags) {
1558 bool needSeparator = false;
1559 SkAddFlagToString(str, this->ignoreXform(), "IgnoreXform", &needSeparator);
1560 SkAddFlagToString(str,
1561 SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag),
1562 "HighQuality", &needSeparator);
1563 } else {
1564 str->append("None");
1565 }
1566 str->append("))");
1567 }
1568 #endif
1569
1570 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
1571 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
1572 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
1573