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