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 "SkRTConf.h"
16 #include "SkStringUtils.h"
17 #include "SkStrokeRec.h"
18
19 #if SK_SUPPORT_GPU
20 #include "GrCircleBlurFragmentProcessor.h"
21 #include "GrContext.h"
22 #include "GrDrawContext.h"
23 #include "GrTexture.h"
24 #include "GrFragmentProcessor.h"
25 #include "GrInvariantOutput.h"
26 #include "SkDraw.h"
27 #include "effects/GrSimpleTextureEffect.h"
28 #include "glsl/GrGLSLFragmentProcessor.h"
29 #include "glsl/GrGLSLFragmentShaderBuilder.h"
30 #include "glsl/GrGLSLProgramDataManager.h"
31 #include "glsl/GrGLSLTextureSampler.h"
32 #include "glsl/GrGLSLUniformHandler.h"
33 #endif
34
ConvertRadiusToSigma(SkScalar radius)35 SkScalar SkBlurMaskFilter::ConvertRadiusToSigma(SkScalar radius) {
36 return SkBlurMask::ConvertRadiusToSigma(radius);
37 }
38
39 class SkBlurMaskFilterImpl : public SkMaskFilter {
40 public:
41 SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle, uint32_t flags);
42
43 // overrides from SkMaskFilter
44 SkMask::Format getFormat() const override;
45 bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
46 SkIPoint* margin) const override;
47
48 #if SK_SUPPORT_GPU
49 bool canFilterMaskGPU(const SkRRect& devRRect,
50 const SkIRect& clipBounds,
51 const SkMatrix& ctm,
52 SkRect* maskRect) const override;
53 bool directFilterMaskGPU(GrTextureProvider* texProvider,
54 GrDrawContext* drawContext,
55 GrPaint* grp,
56 const GrClip&,
57 const SkMatrix& viewMatrix,
58 const SkStrokeRec& strokeRec,
59 const SkPath& path) const override;
60 bool directFilterRRectMaskGPU(GrTextureProvider* texProvider,
61 GrDrawContext* drawContext,
62 GrPaint* grp,
63 const GrClip&,
64 const SkMatrix& viewMatrix,
65 const SkStrokeRec& strokeRec,
66 const SkRRect& rrect) const override;
67 bool filterMaskGPU(GrTexture* src,
68 const SkMatrix& ctm,
69 const SkRect& maskRect,
70 GrTexture** result,
71 bool canOverwriteSrc) const override;
72 #endif
73
74 void computeFastBounds(const SkRect&, SkRect*) const override;
75 bool asABlur(BlurRec*) const override;
76
77 SK_TO_STRING_OVERRIDE()
78 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
79
80 protected:
81 FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
82 const SkIRect& clipBounds,
83 NinePatch*) const override;
84
85 FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&,
86 const SkIRect& clipBounds,
87 NinePatch*) const override;
88
89 bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
90 SkIPoint* margin, SkMask::CreateMode createMode) const;
91 bool filterRRectMask(SkMask* dstM, const SkRRect& r, const SkMatrix& matrix,
92 SkIPoint* margin, SkMask::CreateMode createMode) const;
93
94 private:
95 // To avoid unseemly allocation requests (esp. for finite platforms like
96 // handset) we limit the radius so something manageable. (as opposed to
97 // a request like 10,000)
98 static const SkScalar kMAX_BLUR_SIGMA;
99
100 SkScalar fSigma;
101 SkBlurStyle fBlurStyle;
102 uint32_t fBlurFlags;
103
getQuality() const104 SkBlurQuality getQuality() const {
105 return (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
106 kHigh_SkBlurQuality : kLow_SkBlurQuality;
107 }
108
109 SkBlurMaskFilterImpl(SkReadBuffer&);
110 void flatten(SkWriteBuffer&) const override;
111
computeXformedSigma(const SkMatrix & ctm) const112 SkScalar computeXformedSigma(const SkMatrix& ctm) const {
113 bool ignoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
114
115 SkScalar xformedSigma = ignoreTransform ? fSigma : ctm.mapRadius(fSigma);
116 return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA);
117 }
118
119 friend class SkBlurMaskFilter;
120
121 typedef SkMaskFilter INHERITED;
122 };
123
124 const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128);
125
Create(SkBlurStyle style,SkScalar sigma,uint32_t flags)126 SkMaskFilter* SkBlurMaskFilter::Create(SkBlurStyle style, SkScalar sigma, uint32_t flags) {
127 if (!SkScalarIsFinite(sigma) || sigma <= 0) {
128 return nullptr;
129 }
130 if ((unsigned)style > (unsigned)kLastEnum_SkBlurStyle) {
131 return nullptr;
132 }
133 if (flags > SkBlurMaskFilter::kAll_BlurFlag) {
134 return nullptr;
135 }
136 return new SkBlurMaskFilterImpl(sigma, style, flags);
137 }
138
139 ///////////////////////////////////////////////////////////////////////////////
140
SkBlurMaskFilterImpl(SkScalar sigma,SkBlurStyle style,uint32_t flags)141 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style, uint32_t flags)
142 : fSigma(sigma)
143 , fBlurStyle(style)
144 , fBlurFlags(flags) {
145 SkASSERT(fSigma > 0);
146 SkASSERT((unsigned)style <= kLastEnum_SkBlurStyle);
147 SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
148 }
149
getFormat() const150 SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
151 return SkMask::kA8_Format;
152 }
153
asABlur(BlurRec * rec) const154 bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const {
155 if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
156 return false;
157 }
158
159 if (rec) {
160 rec->fSigma = fSigma;
161 rec->fStyle = fBlurStyle;
162 rec->fQuality = this->getQuality();
163 }
164 return true;
165 }
166
filterMask(SkMask * dst,const SkMask & src,const SkMatrix & matrix,SkIPoint * margin) const167 bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
168 const SkMatrix& matrix,
169 SkIPoint* margin) const {
170 SkScalar sigma = this->computeXformedSigma(matrix);
171 return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, this->getQuality(), margin);
172 }
173
filterRectMask(SkMask * dst,const SkRect & r,const SkMatrix & matrix,SkIPoint * margin,SkMask::CreateMode createMode) const174 bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
175 const SkMatrix& matrix,
176 SkIPoint* margin, SkMask::CreateMode createMode) const {
177 SkScalar sigma = computeXformedSigma(matrix);
178
179 return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle, margin, createMode);
180 }
181
filterRRectMask(SkMask * dst,const SkRRect & r,const SkMatrix & matrix,SkIPoint * margin,SkMask::CreateMode createMode) const182 bool SkBlurMaskFilterImpl::filterRRectMask(SkMask* dst, const SkRRect& r,
183 const SkMatrix& matrix,
184 SkIPoint* margin, SkMask::CreateMode createMode) const {
185 SkScalar sigma = computeXformedSigma(matrix);
186
187 return SkBlurMask::BlurRRect(sigma, dst, r, fBlurStyle, margin, createMode);
188 }
189
190 #include "SkCanvas.h"
191
prepare_to_draw_into_mask(const SkRect & bounds,SkMask * mask)192 static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) {
193 SkASSERT(mask != nullptr);
194
195 mask->fBounds = bounds.roundOut();
196 mask->fRowBytes = SkAlign4(mask->fBounds.width());
197 mask->fFormat = SkMask::kA8_Format;
198 const size_t size = mask->computeImageSize();
199 mask->fImage = SkMask::AllocImage(size);
200 if (nullptr == mask->fImage) {
201 return false;
202 }
203
204 // FIXME: use sk_calloc in AllocImage?
205 sk_bzero(mask->fImage, size);
206 return true;
207 }
208
draw_rrect_into_mask(const SkRRect rrect,SkMask * mask)209 static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) {
210 if (!prepare_to_draw_into_mask(rrect.rect(), mask)) {
211 return false;
212 }
213
214 // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a
215 // clean way to share more code?
216 SkBitmap bitmap;
217 bitmap.installMaskPixels(*mask);
218
219 SkCanvas canvas(bitmap);
220 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
221 -SkIntToScalar(mask->fBounds.top()));
222
223 SkPaint paint;
224 paint.setAntiAlias(true);
225 canvas.drawRRect(rrect, paint);
226 return true;
227 }
228
draw_rects_into_mask(const SkRect rects[],int count,SkMask * mask)229 static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) {
230 if (!prepare_to_draw_into_mask(rects[0], mask)) {
231 return false;
232 }
233
234 SkBitmap bitmap;
235 bitmap.installPixels(SkImageInfo::Make(mask->fBounds.width(),
236 mask->fBounds.height(),
237 kAlpha_8_SkColorType,
238 kPremul_SkAlphaType),
239 mask->fImage, mask->fRowBytes);
240
241 SkCanvas canvas(bitmap);
242 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
243 -SkIntToScalar(mask->fBounds.top()));
244
245 SkPaint paint;
246 paint.setAntiAlias(true);
247
248 if (1 == count) {
249 canvas.drawRect(rects[0], paint);
250 } else {
251 // todo: do I need a fast way to do this?
252 SkPath path;
253 path.addRect(rects[0]);
254 path.addRect(rects[1]);
255 path.setFillType(SkPath::kEvenOdd_FillType);
256 canvas.drawPath(path, paint);
257 }
258 return true;
259 }
260
rect_exceeds(const SkRect & r,SkScalar v)261 static bool rect_exceeds(const SkRect& r, SkScalar v) {
262 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
263 r.width() > v || r.height() > v;
264 }
265
266 #include "SkMaskCache.h"
267
copy_mask_to_cacheddata(SkMask * mask)268 static SkCachedData* copy_mask_to_cacheddata(SkMask* mask) {
269 const size_t size = mask->computeTotalImageSize();
270 SkCachedData* data = SkResourceCache::NewCachedData(size);
271 if (data) {
272 memcpy(data->writable_data(), mask->fImage, size);
273 SkMask::FreeImage(mask->fImage);
274 mask->fImage = (uint8_t*)data->data();
275 }
276 return data;
277 }
278
find_cached_rrect(SkMask * mask,SkScalar sigma,SkBlurStyle style,SkBlurQuality quality,const SkRRect & rrect)279 static SkCachedData* find_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
280 SkBlurQuality quality, const SkRRect& rrect) {
281 return SkMaskCache::FindAndRef(sigma, style, quality, rrect, mask);
282 }
283
add_cached_rrect(SkMask * mask,SkScalar sigma,SkBlurStyle style,SkBlurQuality quality,const SkRRect & rrect)284 static SkCachedData* add_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
285 SkBlurQuality quality, const SkRRect& rrect) {
286 SkCachedData* cache = copy_mask_to_cacheddata(mask);
287 if (cache) {
288 SkMaskCache::Add(sigma, style, quality, rrect, *mask, cache);
289 }
290 return cache;
291 }
292
find_cached_rects(SkMask * mask,SkScalar sigma,SkBlurStyle style,SkBlurQuality quality,const SkRect rects[],int count)293 static SkCachedData* find_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
294 SkBlurQuality quality, const SkRect rects[], int count) {
295 return SkMaskCache::FindAndRef(sigma, style, quality, rects, count, mask);
296 }
297
add_cached_rects(SkMask * mask,SkScalar sigma,SkBlurStyle style,SkBlurQuality quality,const SkRect rects[],int count)298 static SkCachedData* add_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
299 SkBlurQuality quality, const SkRect rects[], int count) {
300 SkCachedData* cache = copy_mask_to_cacheddata(mask);
301 if (cache) {
302 SkMaskCache::Add(sigma, style, quality, rects, count, *mask, cache);
303 }
304 return cache;
305 }
306
307 #ifdef SK_IGNORE_FAST_RRECT_BLUR
308 SK_CONF_DECLARE(bool, c_analyticBlurRRect, "mask.filter.blur.analyticblurrrect", false, "Use the faster analytic blur approach for ninepatch rects");
309 #else
310 SK_CONF_DECLARE(bool, c_analyticBlurRRect, "mask.filter.blur.analyticblurrrect", true, "Use the faster analytic blur approach for ninepatch round rects");
311 #endif
312
313 SkMaskFilter::FilterReturn
filterRRectToNine(const SkRRect & rrect,const SkMatrix & matrix,const SkIRect & clipBounds,NinePatch * patch) const314 SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix,
315 const SkIRect& clipBounds,
316 NinePatch* patch) const {
317 SkASSERT(patch != nullptr);
318 switch (rrect.getType()) {
319 case SkRRect::kEmpty_Type:
320 // Nothing to draw.
321 return kFalse_FilterReturn;
322
323 case SkRRect::kRect_Type:
324 // We should have caught this earlier.
325 SkASSERT(false);
326 // Fall through.
327 case SkRRect::kOval_Type:
328 // The nine patch special case does not handle ovals, and we
329 // already have code for rectangles.
330 return kUnimplemented_FilterReturn;
331
332 // These three can take advantage of this fast path.
333 case SkRRect::kSimple_Type:
334 case SkRRect::kNinePatch_Type:
335 case SkRRect::kComplex_Type:
336 break;
337 }
338
339 // TODO: report correct metrics for innerstyle, where we do not grow the
340 // total bounds, but we do need an inset the size of our blur-radius
341 if (kInner_SkBlurStyle == fBlurStyle) {
342 return kUnimplemented_FilterReturn;
343 }
344
345 // TODO: take clipBounds into account to limit our coordinates up front
346 // for now, just skip too-large src rects (to take the old code path).
347 if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) {
348 return kUnimplemented_FilterReturn;
349 }
350
351 SkIPoint margin;
352 SkMask srcM, dstM;
353 srcM.fBounds = rrect.rect().roundOut();
354 srcM.fFormat = SkMask::kA8_Format;
355 srcM.fRowBytes = 0;
356
357 bool filterResult = false;
358 if (c_analyticBlurRRect) {
359 // special case for fast round rect blur
360 // don't actually do the blur the first time, just compute the correct size
361 filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin,
362 SkMask::kJustComputeBounds_CreateMode);
363 }
364
365 if (!filterResult) {
366 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
367 }
368
369 if (!filterResult) {
370 return kFalse_FilterReturn;
371 }
372
373 // Now figure out the appropriate width and height of the smaller round rectangle
374 // to stretch. It will take into account the larger radius per side as well as double
375 // the margin, to account for inner and outer blur.
376 const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
377 const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
378 const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
379 const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
380
381 const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX);
382 const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX);
383
384 // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover
385 // any fractional space on either side plus 1 for the part to stretch.
386 const SkScalar stretchSize = SkIntToScalar(3);
387
388 const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize;
389 if (totalSmallWidth >= rrect.rect().width()) {
390 // There is no valid piece to stretch.
391 return kUnimplemented_FilterReturn;
392 }
393
394 const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY);
395 const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY);
396
397 const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize;
398 if (totalSmallHeight >= rrect.rect().height()) {
399 // There is no valid piece to stretch.
400 return kUnimplemented_FilterReturn;
401 }
402
403 SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight);
404
405 SkRRect smallRR;
406 SkVector radii[4];
407 radii[SkRRect::kUpperLeft_Corner] = UL;
408 radii[SkRRect::kUpperRight_Corner] = UR;
409 radii[SkRRect::kLowerRight_Corner] = LR;
410 radii[SkRRect::kLowerLeft_Corner] = LL;
411 smallRR.setRectRadii(smallR, radii);
412
413 const SkScalar sigma = this->computeXformedSigma(matrix);
414 SkCachedData* cache = find_cached_rrect(&patch->fMask, sigma, fBlurStyle,
415 this->getQuality(), smallRR);
416 if (!cache) {
417 bool analyticBlurWorked = false;
418 if (c_analyticBlurRRect) {
419 analyticBlurWorked =
420 this->filterRRectMask(&patch->fMask, smallRR, matrix, &margin,
421 SkMask::kComputeBoundsAndRenderImage_CreateMode);
422 }
423
424 if (!analyticBlurWorked) {
425 if (!draw_rrect_into_mask(smallRR, &srcM)) {
426 return kFalse_FilterReturn;
427 }
428
429 SkAutoMaskFreeImage amf(srcM.fImage);
430
431 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
432 return kFalse_FilterReturn;
433 }
434 }
435 cache = add_cached_rrect(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallRR);
436 }
437
438 patch->fMask.fBounds.offsetTo(0, 0);
439 patch->fOuterRect = dstM.fBounds;
440 patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1;
441 patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1;
442 SkASSERT(nullptr == patch->fCache);
443 patch->fCache = cache; // transfer ownership to patch
444 return kTrue_FilterReturn;
445 }
446
447 SK_CONF_DECLARE(bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", true, "Use the faster analytic blur approach for ninepatch rects");
448
449 SkMaskFilter::FilterReturn
filterRectsToNine(const SkRect rects[],int count,const SkMatrix & matrix,const SkIRect & clipBounds,NinePatch * patch) const450 SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
451 const SkMatrix& matrix,
452 const SkIRect& clipBounds,
453 NinePatch* patch) const {
454 if (count < 1 || count > 2) {
455 return kUnimplemented_FilterReturn;
456 }
457
458 // TODO: report correct metrics for innerstyle, where we do not grow the
459 // total bounds, but we do need an inset the size of our blur-radius
460 if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) {
461 return kUnimplemented_FilterReturn;
462 }
463
464 // TODO: take clipBounds into account to limit our coordinates up front
465 // for now, just skip too-large src rects (to take the old code path).
466 if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
467 return kUnimplemented_FilterReturn;
468 }
469
470 SkIPoint margin;
471 SkMask srcM, dstM;
472 srcM.fBounds = rects[0].roundOut();
473 srcM.fFormat = SkMask::kA8_Format;
474 srcM.fRowBytes = 0;
475
476 bool filterResult = false;
477 if (count == 1 && c_analyticBlurNinepatch) {
478 // special case for fast rect blur
479 // don't actually do the blur the first time, just compute the correct size
480 filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
481 SkMask::kJustComputeBounds_CreateMode);
482 } else {
483 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
484 }
485
486 if (!filterResult) {
487 return kFalse_FilterReturn;
488 }
489
490 /*
491 * smallR is the smallest version of 'rect' that will still guarantee that
492 * we get the same blur results on all edges, plus 1 center row/col that is
493 * representative of the extendible/stretchable edges of the ninepatch.
494 * Since our actual edge may be fractional we inset 1 more to be sure we
495 * don't miss any interior blur.
496 * x is an added pixel of blur, and { and } are the (fractional) edge
497 * pixels from the original rect.
498 *
499 * x x { x x .... x x } x x
500 *
501 * Thus, in this case, we inset by a total of 5 (on each side) beginning
502 * with our outer-rect (dstM.fBounds)
503 */
504 SkRect smallR[2];
505 SkIPoint center;
506
507 // +2 is from +1 for each edge (to account for possible fractional edges
508 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
509 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
510 SkIRect innerIR;
511
512 if (1 == count) {
513 innerIR = srcM.fBounds;
514 center.set(smallW, smallH);
515 } else {
516 SkASSERT(2 == count);
517 rects[1].roundIn(&innerIR);
518 center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
519 smallH + (innerIR.top() - srcM.fBounds.top()));
520 }
521
522 // +1 so we get a clean, stretchable, center row/col
523 smallW += 1;
524 smallH += 1;
525
526 // we want the inset amounts to be integral, so we don't change any
527 // fractional phase on the fRight or fBottom of our smallR.
528 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
529 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
530 if (dx < 0 || dy < 0) {
531 // we're too small, relative to our blur, to break into nine-patch,
532 // so we ask to have our normal filterMask() be called.
533 return kUnimplemented_FilterReturn;
534 }
535
536 smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
537 if (smallR[0].width() < 2 || smallR[0].height() < 2) {
538 return kUnimplemented_FilterReturn;
539 }
540 if (2 == count) {
541 smallR[1].set(rects[1].left(), rects[1].top(),
542 rects[1].right() - dx, rects[1].bottom() - dy);
543 SkASSERT(!smallR[1].isEmpty());
544 }
545
546 const SkScalar sigma = this->computeXformedSigma(matrix);
547 SkCachedData* cache = find_cached_rects(&patch->fMask, sigma, fBlurStyle,
548 this->getQuality(), smallR, count);
549 if (!cache) {
550 if (count > 1 || !c_analyticBlurNinepatch) {
551 if (!draw_rects_into_mask(smallR, count, &srcM)) {
552 return kFalse_FilterReturn;
553 }
554
555 SkAutoMaskFreeImage amf(srcM.fImage);
556
557 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
558 return kFalse_FilterReturn;
559 }
560 } else {
561 if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
562 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
563 return kFalse_FilterReturn;
564 }
565 }
566 cache = add_cached_rects(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallR, count);
567 }
568 patch->fMask.fBounds.offsetTo(0, 0);
569 patch->fOuterRect = dstM.fBounds;
570 patch->fCenter = center;
571 SkASSERT(nullptr == patch->fCache);
572 patch->fCache = cache; // transfer ownership to patch
573 return kTrue_FilterReturn;
574 }
575
computeFastBounds(const SkRect & src,SkRect * dst) const576 void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
577 SkRect* dst) const {
578 SkScalar pad = 3.0f * fSigma;
579
580 dst->set(src.fLeft - pad, src.fTop - pad,
581 src.fRight + pad, src.fBottom + pad);
582 }
583
CreateProc(SkReadBuffer & buffer)584 SkFlattenable* SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
585 const SkScalar sigma = buffer.readScalar();
586 const unsigned style = buffer.readUInt();
587 const unsigned flags = buffer.readUInt();
588 if (style <= kLastEnum_SkBlurStyle) {
589 return SkBlurMaskFilter::Create((SkBlurStyle)style, sigma, flags);
590 }
591 return nullptr;
592 }
593
flatten(SkWriteBuffer & buffer) const594 void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
595 buffer.writeScalar(fSigma);
596 buffer.writeUInt(fBlurStyle);
597 buffer.writeUInt(fBlurFlags);
598 }
599
600 #if SK_SUPPORT_GPU
601
602 class GrGLRectBlurEffect;
603
604 class GrRectBlurEffect : public GrFragmentProcessor {
605 public:
~GrRectBlurEffect()606 ~GrRectBlurEffect() override { }
607
name() const608 const char* name() const override { return "RectBlur"; }
609
Create(GrTextureProvider * textureProvider,const SkRect & rect,float sigma)610 static GrFragmentProcessor* Create(GrTextureProvider *textureProvider,
611 const SkRect& rect, float sigma) {
612 int doubleProfileSize = SkScalarCeilToInt(12*sigma);
613
614 if (doubleProfileSize >= rect.width() || doubleProfileSize >= rect.height()) {
615 // if the blur sigma is too large so the gaussian overlaps the whole
616 // rect in either direction, fall back to CPU path for now.
617 return nullptr;
618 }
619
620 SkAutoTUnref<GrTexture> blurProfile(CreateBlurProfileTexture(textureProvider, sigma));
621 if (!blurProfile) {
622 return nullptr;
623 }
624 // in OpenGL ES, mediump floats have a minimum range of 2^14. If we have coordinates bigger
625 // than that, the shader math will end up with infinities and result in the blur effect not
626 // working correctly. To avoid this, we switch into highp when the coordinates are too big.
627 // As 2^14 is the minimum range but the actual range can be bigger, we might end up
628 // switching to highp sooner than strictly necessary, but most devices that have a bigger
629 // range for mediump also have mediump being exactly the same as highp (e.g. all non-OpenGL
630 // ES devices), and thus incur no additional penalty for the switch.
631 static const SkScalar kMAX_BLUR_COORD = SkIntToScalar(16000);
632 GrSLPrecision precision;
633 if (SkScalarAbs(rect.top()) > kMAX_BLUR_COORD ||
634 SkScalarAbs(rect.left()) > kMAX_BLUR_COORD ||
635 SkScalarAbs(rect.bottom()) > kMAX_BLUR_COORD ||
636 SkScalarAbs(rect.right()) > kMAX_BLUR_COORD ||
637 SkScalarAbs(rect.width()) > kMAX_BLUR_COORD ||
638 SkScalarAbs(rect.height()) > kMAX_BLUR_COORD) {
639 precision = kHigh_GrSLPrecision;
640 }
641 else {
642 precision = kDefault_GrSLPrecision;
643 }
644 return new GrRectBlurEffect(rect, sigma, blurProfile, precision);
645 }
646
getRect() const647 const SkRect& getRect() const { return fRect; }
getSigma() const648 float getSigma() const { return fSigma; }
precision() const649 GrSLPrecision precision() const { return fPrecision; }
650
651 private:
652 GrRectBlurEffect(const SkRect& rect, float sigma, GrTexture *blurProfile,
653 GrSLPrecision fPrecision);
654
655 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
656
657 void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
658
659 bool onIsEqual(const GrFragmentProcessor&) const override;
660
661 void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
662
663 static GrTexture* CreateBlurProfileTexture(GrTextureProvider*, float sigma);
664
665 SkRect fRect;
666 float fSigma;
667 GrTextureAccess fBlurProfileAccess;
668 GrSLPrecision fPrecision;
669
670 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
671
672 typedef GrFragmentProcessor INHERITED;
673 };
674
675 class GrGLRectBlurEffect : public GrGLSLFragmentProcessor {
676 public:
677 void emitCode(EmitArgs&) override;
678
679 static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b);
680
681 protected:
682 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
683
684 private:
685 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
686
687 UniformHandle fProxyRectUniform;
688 UniformHandle fProfileSizeUniform;
689
690 typedef GrGLSLFragmentProcessor INHERITED;
691 };
692
OutputRectBlurProfileLookup(GrGLSLFPFragmentBuilder * fragBuilder,const GrGLSLTextureSampler & sampler,const char * output,const char * profileSize,const char * loc,const char * blurred_width,const char * sharp_width)693 void OutputRectBlurProfileLookup(GrGLSLFPFragmentBuilder* fragBuilder,
694 const GrGLSLTextureSampler& sampler,
695 const char *output,
696 const char *profileSize, const char *loc,
697 const char *blurred_width,
698 const char *sharp_width) {
699 fragBuilder->codeAppendf("float %s;", output);
700 fragBuilder->codeAppendf("{");
701 fragBuilder->codeAppendf("float coord = ((abs(%s - 0.5 * %s) - 0.5 * %s)) / %s;",
702 loc, blurred_width, sharp_width, profileSize);
703 fragBuilder->codeAppendf("%s = ", output);
704 fragBuilder->appendTextureLookup(sampler, "vec2(coord,0.5)");
705 fragBuilder->codeAppend(".a;");
706 fragBuilder->codeAppendf("}");
707 }
708
709
GenKey(const GrProcessor & proc,const GrGLSLCaps &,GrProcessorKeyBuilder * b)710 void GrGLRectBlurEffect::GenKey(const GrProcessor& proc, const GrGLSLCaps&,
711 GrProcessorKeyBuilder* b) {
712 const GrRectBlurEffect& rbe = proc.cast<GrRectBlurEffect>();
713
714 b->add32(rbe.precision());
715 }
716
717
emitCode(EmitArgs & args)718 void GrGLRectBlurEffect::emitCode(EmitArgs& args) {
719 const GrRectBlurEffect& rbe = args.fFp.cast<GrRectBlurEffect>();
720
721 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
722
723 const char *rectName;
724 const char *profileSizeName;
725
726 const char* precisionString = GrGLSLShaderVar::PrecisionString(args.fGLSLCaps, rbe.precision());
727 fProxyRectUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
728 kVec4f_GrSLType,
729 rbe.precision(),
730 "proxyRect",
731 &rectName);
732 fProfileSizeUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
733 kFloat_GrSLType,
734 kDefault_GrSLPrecision,
735 "profileSize",
736 &profileSizeName);
737
738 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
739 const char *fragmentPos = fragBuilder->fragmentPosition();
740
741 if (args.fInputColor) {
742 fragBuilder->codeAppendf("vec4 src=%s;", args.fInputColor);
743 } else {
744 fragBuilder->codeAppendf("vec4 src=vec4(1);");
745 }
746
747 fragBuilder->codeAppendf("%s vec2 translatedPos = %s.xy - %s.xy;", precisionString, fragmentPos,
748 rectName);
749 fragBuilder->codeAppendf("%s float width = %s.z - %s.x;", precisionString, rectName, rectName);
750 fragBuilder->codeAppendf("%s float height = %s.w - %s.y;", precisionString, rectName, rectName);
751
752 fragBuilder->codeAppendf("%s vec2 smallDims = vec2(width - %s, height - %s);", precisionString,
753 profileSizeName, profileSizeName);
754 fragBuilder->codeAppendf("%s float center = 2.0 * floor(%s/2.0 + .25) - 1.0;", precisionString,
755 profileSizeName);
756 fragBuilder->codeAppendf("%s vec2 wh = smallDims - vec2(center,center);", precisionString);
757
758 OutputRectBlurProfileLookup(fragBuilder, args.fSamplers[0], "horiz_lookup", profileSizeName,
759 "translatedPos.x", "width", "wh.x");
760 OutputRectBlurProfileLookup(fragBuilder, args.fSamplers[0], "vert_lookup", profileSizeName,
761 "translatedPos.y", "height", "wh.y");
762
763 fragBuilder->codeAppendf("float final = horiz_lookup * vert_lookup;");
764 fragBuilder->codeAppendf("%s = src * final;", args.fOutputColor);
765 }
766
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & proc)767 void GrGLRectBlurEffect::onSetData(const GrGLSLProgramDataManager& pdman,
768 const GrProcessor& proc) {
769 const GrRectBlurEffect& rbe = proc.cast<GrRectBlurEffect>();
770 SkRect rect = rbe.getRect();
771
772 pdman.set4f(fProxyRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
773 pdman.set1f(fProfileSizeUniform, SkScalarCeilToScalar(6*rbe.getSigma()));
774 }
775
CreateBlurProfileTexture(GrTextureProvider * textureProvider,float sigma)776 GrTexture* GrRectBlurEffect::CreateBlurProfileTexture(GrTextureProvider* textureProvider,
777 float sigma) {
778 GrSurfaceDesc texDesc;
779
780 unsigned int profileSize = SkScalarCeilToInt(6*sigma);
781
782 texDesc.fWidth = profileSize;
783 texDesc.fHeight = 1;
784 texDesc.fConfig = kAlpha_8_GrPixelConfig;
785
786 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
787 GrUniqueKey key;
788 GrUniqueKey::Builder builder(&key, kDomain, 1);
789 builder[0] = profileSize;
790 builder.finish();
791
792 GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key);
793
794 if (!blurProfile) {
795 SkAutoTDeleteArray<uint8_t> profile(SkBlurMask::ComputeBlurProfile(sigma));
796
797 blurProfile = textureProvider->createTexture(texDesc, SkBudgeted::kYes, profile.get(), 0);
798 if (blurProfile) {
799 textureProvider->assignUniqueKeyToTexture(key, blurProfile);
800 }
801 }
802
803 return blurProfile;
804 }
805
GrRectBlurEffect(const SkRect & rect,float sigma,GrTexture * blurProfile,GrSLPrecision precision)806 GrRectBlurEffect::GrRectBlurEffect(const SkRect& rect, float sigma, GrTexture *blurProfile,
807 GrSLPrecision precision)
808 : fRect(rect)
809 , fSigma(sigma)
810 , fBlurProfileAccess(blurProfile)
811 , fPrecision(precision) {
812 this->initClassID<GrRectBlurEffect>();
813 this->addTextureAccess(&fBlurProfileAccess);
814 this->setWillReadFragmentPosition();
815 }
816
onGetGLSLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const817 void GrRectBlurEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
818 GrProcessorKeyBuilder* b) const {
819 GrGLRectBlurEffect::GenKey(*this, caps, b);
820 }
821
onCreateGLSLInstance() const822 GrGLSLFragmentProcessor* GrRectBlurEffect::onCreateGLSLInstance() const {
823 return new GrGLRectBlurEffect;
824 }
825
onIsEqual(const GrFragmentProcessor & sBase) const826 bool GrRectBlurEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
827 const GrRectBlurEffect& s = sBase.cast<GrRectBlurEffect>();
828 return this->getSigma() == s.getSigma() && this->getRect() == s.getRect();
829 }
830
onComputeInvariantOutput(GrInvariantOutput * inout) const831 void GrRectBlurEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
832 inout->mulByUnknownSingleComponent();
833 }
834
835 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRectBlurEffect);
836
TestCreate(GrProcessorTestData * d)837 const GrFragmentProcessor* GrRectBlurEffect::TestCreate(GrProcessorTestData* d) {
838 float sigma = d->fRandom->nextRangeF(3,8);
839 float width = d->fRandom->nextRangeF(200,300);
840 float height = d->fRandom->nextRangeF(200,300);
841 return GrRectBlurEffect::Create(d->fContext->textureProvider(), SkRect::MakeWH(width, height),
842 sigma);
843 }
844
845
directFilterMaskGPU(GrTextureProvider * texProvider,GrDrawContext * drawContext,GrPaint * grp,const GrClip & clip,const SkMatrix & viewMatrix,const SkStrokeRec & strokeRec,const SkPath & path) const846 bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvider,
847 GrDrawContext* drawContext,
848 GrPaint* grp,
849 const GrClip& clip,
850 const SkMatrix& viewMatrix,
851 const SkStrokeRec& strokeRec,
852 const SkPath& path) const {
853 SkASSERT(drawContext);
854
855 if (fBlurStyle != kNormal_SkBlurStyle) {
856 return false;
857 }
858
859 // TODO: we could handle blurred stroked circles
860 if (!strokeRec.isFillStyle()) {
861 return false;
862 }
863
864 SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
865
866 SkAutoTUnref<const GrFragmentProcessor> fp;
867
868 SkRect rect;
869 if (path.isRect(&rect)) {
870 int pad = SkScalarCeilToInt(6*xformedSigma)/2;
871 rect.outset(SkIntToScalar(pad), SkIntToScalar(pad));
872
873 fp.reset(GrRectBlurEffect::Create(texProvider, rect, xformedSigma));
874 } else if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height())) {
875 fp.reset(GrCircleBlurFragmentProcessor::Create(texProvider, rect, xformedSigma));
876
877 // expand the rect for the coverage geometry
878 int pad = SkScalarCeilToInt(6*xformedSigma)/2;
879 rect.outset(SkIntToScalar(pad), SkIntToScalar(pad));
880 } else {
881 return false;
882 }
883
884 if (!fp) {
885 return false;
886 }
887
888 grp->addCoverageFragmentProcessor(fp);
889
890 SkMatrix inverse;
891 if (!viewMatrix.invert(&inverse)) {
892 return false;
893 }
894
895 drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(), rect, inverse);
896 return true;
897 }
898
899 //////////////////////////////////////////////////////////////////////////////
900
901 class GrRRectBlurEffect : public GrFragmentProcessor {
902 public:
903
904 static const GrFragmentProcessor* Create(GrTextureProvider*, float sigma, const SkRRect&);
905
~GrRRectBlurEffect()906 virtual ~GrRRectBlurEffect() {};
name() const907 const char* name() const override { return "GrRRectBlur"; }
908
getRRect() const909 const SkRRect& getRRect() const { return fRRect; }
getSigma() const910 float getSigma() const { return fSigma; }
911
912 private:
913 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
914
915 GrRRectBlurEffect(float sigma, const SkRRect&, GrTexture* profileTexture);
916
917 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
918 GrProcessorKeyBuilder* b) const override;
919
920 bool onIsEqual(const GrFragmentProcessor& other) const override;
921
922 void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
923
924 SkRRect fRRect;
925 float fSigma;
926 GrTextureAccess fNinePatchAccess;
927
928 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
929
930 typedef GrFragmentProcessor INHERITED;
931 };
932
933
Create(GrTextureProvider * texProvider,float sigma,const SkRRect & rrect)934 const GrFragmentProcessor* GrRRectBlurEffect::Create(GrTextureProvider* texProvider, float sigma,
935 const SkRRect& rrect) {
936 if (rrect.isCircle()) {
937 return GrCircleBlurFragmentProcessor::Create(texProvider, rrect.rect(), sigma);
938 }
939
940 if (!rrect.isSimpleCircular()) {
941 return nullptr;
942 }
943
944 // Make sure we can successfully ninepatch this rrect -- the blur sigma has to be
945 // sufficiently small relative to both the size of the corner radius and the
946 // width (and height) of the rrect.
947
948 unsigned int blurRadius = 3*SkScalarCeilToInt(sigma-1/6.0f);
949 unsigned int cornerRadius = SkScalarCeilToInt(rrect.getSimpleRadii().x());
950 if (cornerRadius + blurRadius > rrect.width()/2 ||
951 cornerRadius + blurRadius > rrect.height()/2) {
952 return nullptr;
953 }
954
955 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
956 GrUniqueKey key;
957 GrUniqueKey::Builder builder(&key, kDomain, 2);
958 builder[0] = blurRadius;
959 builder[1] = cornerRadius;
960 builder.finish();
961
962 SkAutoTUnref<GrTexture> blurNinePatchTexture(texProvider->findAndRefTextureByUniqueKey(key));
963
964 if (!blurNinePatchTexture) {
965 SkMask mask;
966
967 unsigned int smallRectSide = 2*(blurRadius + cornerRadius) + 1;
968
969 mask.fBounds = SkIRect::MakeWH(smallRectSide, smallRectSide);
970 mask.fFormat = SkMask::kA8_Format;
971 mask.fRowBytes = mask.fBounds.width();
972 mask.fImage = SkMask::AllocImage(mask.computeTotalImageSize());
973 SkAutoMaskFreeImage amfi(mask.fImage);
974
975 memset(mask.fImage, 0, mask.computeTotalImageSize());
976
977 SkRect smallRect;
978 smallRect.setWH(SkIntToScalar(smallRectSide), SkIntToScalar(smallRectSide));
979
980 SkRRect smallRRect;
981 smallRRect.setRectXY(smallRect, SkIntToScalar(cornerRadius), SkIntToScalar(cornerRadius));
982
983 SkPath path;
984 path.addRRect(smallRRect);
985
986 SkDraw::DrawToMask(path, &mask.fBounds, nullptr, nullptr, &mask,
987 SkMask::kJustRenderImage_CreateMode, SkPaint::kFill_Style);
988
989 SkMask blurredMask;
990 if (!SkBlurMask::BoxBlur(&blurredMask, mask, sigma, kNormal_SkBlurStyle,
991 kHigh_SkBlurQuality, nullptr, true)) {
992 return nullptr;
993 }
994
995 unsigned int texSide = smallRectSide + 2*blurRadius;
996 GrSurfaceDesc texDesc;
997 texDesc.fWidth = texSide;
998 texDesc.fHeight = texSide;
999 texDesc.fConfig = kAlpha_8_GrPixelConfig;
1000
1001 blurNinePatchTexture.reset(
1002 texProvider->createTexture(texDesc, SkBudgeted::kYes , blurredMask.fImage, 0));
1003 SkMask::FreeImage(blurredMask.fImage);
1004 if (!blurNinePatchTexture) {
1005 return nullptr;
1006 }
1007 texProvider->assignUniqueKeyToTexture(key, blurNinePatchTexture);
1008 }
1009 return new GrRRectBlurEffect(sigma, rrect, blurNinePatchTexture);
1010 }
1011
onComputeInvariantOutput(GrInvariantOutput * inout) const1012 void GrRRectBlurEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
1013 inout->mulByUnknownSingleComponent();
1014 }
1015
GrRRectBlurEffect(float sigma,const SkRRect & rrect,GrTexture * ninePatchTexture)1016 GrRRectBlurEffect::GrRRectBlurEffect(float sigma, const SkRRect& rrect, GrTexture *ninePatchTexture)
1017 : fRRect(rrect),
1018 fSigma(sigma),
1019 fNinePatchAccess(ninePatchTexture) {
1020 this->initClassID<GrRRectBlurEffect>();
1021 this->addTextureAccess(&fNinePatchAccess);
1022 this->setWillReadFragmentPosition();
1023 }
1024
onIsEqual(const GrFragmentProcessor & other) const1025 bool GrRRectBlurEffect::onIsEqual(const GrFragmentProcessor& other) const {
1026 const GrRRectBlurEffect& rrbe = other.cast<GrRRectBlurEffect>();
1027 return fRRect.getSimpleRadii().fX == rrbe.fRRect.getSimpleRadii().fX && fSigma == rrbe.fSigma;
1028 }
1029
1030 //////////////////////////////////////////////////////////////////////////////
1031
1032 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRRectBlurEffect);
1033
TestCreate(GrProcessorTestData * d)1034 const GrFragmentProcessor* GrRRectBlurEffect::TestCreate(GrProcessorTestData* d) {
1035 SkScalar w = d->fRandom->nextRangeScalar(100.f, 1000.f);
1036 SkScalar h = d->fRandom->nextRangeScalar(100.f, 1000.f);
1037 SkScalar r = d->fRandom->nextRangeF(1.f, 9.f);
1038 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f);
1039 SkRRect rrect;
1040 rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
1041 return GrRRectBlurEffect::Create(d->fContext->textureProvider(), sigma, rrect);
1042 }
1043
1044 //////////////////////////////////////////////////////////////////////////////
1045
1046 class GrGLRRectBlurEffect : public GrGLSLFragmentProcessor {
1047 public:
1048 void emitCode(EmitArgs&) override;
1049
1050 protected:
1051 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
1052
1053 private:
1054 GrGLSLProgramDataManager::UniformHandle fProxyRectUniform;
1055 GrGLSLProgramDataManager::UniformHandle fCornerRadiusUniform;
1056 GrGLSLProgramDataManager::UniformHandle fBlurRadiusUniform;
1057 typedef GrGLSLFragmentProcessor INHERITED;
1058 };
1059
emitCode(EmitArgs & args)1060 void GrGLRRectBlurEffect::emitCode(EmitArgs& args) {
1061 const char *rectName;
1062 const char *cornerRadiusName;
1063 const char *blurRadiusName;
1064
1065 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
1066 // The proxy rect has left, top, right, and bottom edges correspond to
1067 // components x, y, z, and w, respectively.
1068
1069 fProxyRectUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
1070 kVec4f_GrSLType,
1071 kDefault_GrSLPrecision,
1072 "proxyRect",
1073 &rectName);
1074 fCornerRadiusUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
1075 kFloat_GrSLType,
1076 kDefault_GrSLPrecision,
1077 "cornerRadius",
1078 &cornerRadiusName);
1079 fBlurRadiusUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
1080 kFloat_GrSLType,
1081 kDefault_GrSLPrecision,
1082 "blurRadius",
1083 &blurRadiusName);
1084
1085 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
1086 const char* fragmentPos = fragBuilder->fragmentPosition();
1087
1088 // warp the fragment position to the appropriate part of the 9patch blur texture
1089
1090 fragBuilder->codeAppendf("vec2 rectCenter = (%s.xy + %s.zw)/2.0;", rectName, rectName);
1091 fragBuilder->codeAppendf("vec2 translatedFragPos = %s.xy - %s.xy;", fragmentPos, rectName);
1092 fragBuilder->codeAppendf("float threshold = %s + 2.0*%s;", cornerRadiusName, blurRadiusName);
1093 fragBuilder->codeAppendf("vec2 middle = %s.zw - %s.xy - 2.0*threshold;", rectName, rectName);
1094
1095 fragBuilder->codeAppendf(
1096 "if (translatedFragPos.x >= threshold && translatedFragPos.x < (middle.x+threshold)) {");
1097 fragBuilder->codeAppendf("translatedFragPos.x = threshold;\n");
1098 fragBuilder->codeAppendf("} else if (translatedFragPos.x >= (middle.x + threshold)) {");
1099 fragBuilder->codeAppendf("translatedFragPos.x -= middle.x - 1.0;");
1100 fragBuilder->codeAppendf("}");
1101
1102 fragBuilder->codeAppendf(
1103 "if (translatedFragPos.y > threshold && translatedFragPos.y < (middle.y+threshold)) {");
1104 fragBuilder->codeAppendf("translatedFragPos.y = threshold;");
1105 fragBuilder->codeAppendf("} else if (translatedFragPos.y >= (middle.y + threshold)) {");
1106 fragBuilder->codeAppendf("translatedFragPos.y -= middle.y - 1.0;");
1107 fragBuilder->codeAppendf("}");
1108
1109 fragBuilder->codeAppendf("vec2 proxyDims = vec2(2.0*threshold+1.0);");
1110 fragBuilder->codeAppendf("vec2 texCoord = translatedFragPos / proxyDims;");
1111
1112 fragBuilder->codeAppendf("%s = ", args.fOutputColor);
1113 fragBuilder->appendTextureLookupAndModulate(args.fInputColor, args.fSamplers[0], "texCoord");
1114 fragBuilder->codeAppend(";");
1115 }
1116
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & proc)1117 void GrGLRRectBlurEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1118 const GrProcessor& proc) {
1119 const GrRRectBlurEffect& brre = proc.cast<GrRRectBlurEffect>();
1120 SkRRect rrect = brre.getRRect();
1121
1122 float blurRadius = 3.f*SkScalarCeilToScalar(brre.getSigma()-1/6.0f);
1123 pdman.set1f(fBlurRadiusUniform, blurRadius);
1124
1125 SkRect rect = rrect.getBounds();
1126 rect.outset(blurRadius, blurRadius);
1127 pdman.set4f(fProxyRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
1128
1129 SkScalar radius = 0;
1130 SkASSERT(rrect.isSimpleCircular() || rrect.isRect());
1131 radius = rrect.getSimpleRadii().fX;
1132 pdman.set1f(fCornerRadiusUniform, radius);
1133 }
1134
onGetGLSLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const1135 void GrRRectBlurEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
1136 GrProcessorKeyBuilder* b) const {
1137 GrGLRRectBlurEffect::GenKey(*this, caps, b);
1138 }
1139
onCreateGLSLInstance() const1140 GrGLSLFragmentProcessor* GrRRectBlurEffect::onCreateGLSLInstance() const {
1141 return new GrGLRRectBlurEffect;
1142 }
1143
directFilterRRectMaskGPU(GrTextureProvider * texProvider,GrDrawContext * drawContext,GrPaint * grp,const GrClip & clip,const SkMatrix & viewMatrix,const SkStrokeRec & strokeRec,const SkRRect & rrect) const1144 bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrTextureProvider* texProvider,
1145 GrDrawContext* drawContext,
1146 GrPaint* grp,
1147 const GrClip& clip,
1148 const SkMatrix& viewMatrix,
1149 const SkStrokeRec& strokeRec,
1150 const SkRRect& rrect) const {
1151 SkASSERT(drawContext);
1152
1153 if (fBlurStyle != kNormal_SkBlurStyle) {
1154 return false;
1155 }
1156
1157 if (!strokeRec.isFillStyle()) {
1158 return false;
1159 }
1160
1161 SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
1162 float extra=3.f*SkScalarCeilToScalar(xformedSigma-1/6.0f);
1163
1164 SkRect proxyRect = rrect.rect();
1165 proxyRect.outset(extra, extra);
1166
1167 SkAutoTUnref<const GrFragmentProcessor> fp(GrRRectBlurEffect::Create(texProvider,
1168 xformedSigma, rrect));
1169 if (!fp) {
1170 return false;
1171 }
1172
1173 grp->addCoverageFragmentProcessor(fp);
1174
1175 SkMatrix inverse;
1176 if (!viewMatrix.invert(&inverse)) {
1177 return false;
1178 }
1179
1180 drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(), proxyRect, inverse);
1181 return true;
1182 }
1183
canFilterMaskGPU(const SkRRect & devRRect,const SkIRect & clipBounds,const SkMatrix & ctm,SkRect * maskRect) const1184 bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
1185 const SkIRect& clipBounds,
1186 const SkMatrix& ctm,
1187 SkRect* maskRect) const {
1188 SkScalar xformedSigma = this->computeXformedSigma(ctm);
1189 if (xformedSigma <= 0) {
1190 return false;
1191 }
1192
1193 // We always do circles on the GPU
1194 if (!devRRect.isCircle()) {
1195 static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64);
1196 static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32);
1197
1198 if (devRRect.width() <= kMIN_GPU_BLUR_SIZE &&
1199 devRRect.height() <= kMIN_GPU_BLUR_SIZE &&
1200 xformedSigma <= kMIN_GPU_BLUR_SIGMA) {
1201 // We prefer to blur small rects with small radii on the CPU.
1202 return false;
1203 }
1204 }
1205
1206 if (nullptr == maskRect) {
1207 // don't need to compute maskRect
1208 return true;
1209 }
1210
1211 float sigma3 = 3 * SkScalarToFloat(xformedSigma);
1212
1213 SkRect clipRect = SkRect::Make(clipBounds);
1214 SkRect srcRect(devRRect.rect());
1215
1216 // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
1217 srcRect.outset(sigma3, sigma3);
1218 clipRect.outset(sigma3, sigma3);
1219 if (!srcRect.intersect(clipRect)) {
1220 srcRect.setEmpty();
1221 }
1222 *maskRect = srcRect;
1223 return true;
1224 }
1225
filterMaskGPU(GrTexture * src,const SkMatrix & ctm,const SkRect & maskRect,GrTexture ** result,bool canOverwriteSrc) const1226 bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src,
1227 const SkMatrix& ctm,
1228 const SkRect& maskRect,
1229 GrTexture** result,
1230 bool canOverwriteSrc) const {
1231 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
1232
1233 GrContext* context = src->getContext();
1234
1235 SkScalar xformedSigma = this->computeXformedSigma(ctm);
1236 SkASSERT(xformedSigma > 0);
1237
1238 // If we're doing a normal blur, we can clobber the pathTexture in the
1239 // gaussianBlur. Otherwise, we need to save it for later compositing.
1240 bool isNormalBlur = (kNormal_SkBlurStyle == fBlurStyle);
1241 *result = SkGpuBlurUtils::GaussianBlur(context, src, isNormalBlur && canOverwriteSrc,
1242 clipRect, nullptr, xformedSigma, xformedSigma);
1243 if (nullptr == *result) {
1244 return false;
1245 }
1246
1247 if (!isNormalBlur) {
1248 GrPaint paint;
1249 SkMatrix matrix;
1250 matrix.setIDiv(src->width(), src->height());
1251 // Blend pathTexture over blurTexture.
1252 paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Create(src, matrix))->unref();
1253 if (kInner_SkBlurStyle == fBlurStyle) {
1254 // inner: dst = dst * src
1255 paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op);
1256 } else if (kSolid_SkBlurStyle == fBlurStyle) {
1257 // solid: dst = src + dst - src * dst
1258 // = src + (1 - src) * dst
1259 paint.setCoverageSetOpXPFactory(SkRegion::kUnion_Op);
1260 } else if (kOuter_SkBlurStyle == fBlurStyle) {
1261 // outer: dst = dst * (1 - src)
1262 // = 0 * src + (1 - src) * dst
1263 paint.setCoverageSetOpXPFactory(SkRegion::kDifference_Op);
1264 } else {
1265 paint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
1266 }
1267
1268 SkAutoTUnref<GrDrawContext> drawContext(context->drawContext((*result)->asRenderTarget()));
1269 if (!drawContext) {
1270 return false;
1271 }
1272
1273 drawContext->drawRect(GrClip::WideOpen(), paint, SkMatrix::I(), clipRect);
1274 }
1275
1276 return true;
1277 }
1278
1279 #endif // SK_SUPPORT_GPU
1280
1281
1282 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const1283 void SkBlurMaskFilterImpl::toString(SkString* str) const {
1284 str->append("SkBlurMaskFilterImpl: (");
1285
1286 str->append("sigma: ");
1287 str->appendScalar(fSigma);
1288 str->append(" ");
1289
1290 static const char* gStyleName[kLastEnum_SkBlurStyle + 1] = {
1291 "normal", "solid", "outer", "inner"
1292 };
1293
1294 str->appendf("style: %s ", gStyleName[fBlurStyle]);
1295 str->append("flags: (");
1296 if (fBlurFlags) {
1297 bool needSeparator = false;
1298 SkAddFlagToString(str,
1299 SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag),
1300 "IgnoreXform", &needSeparator);
1301 SkAddFlagToString(str,
1302 SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag),
1303 "HighQuality", &needSeparator);
1304 } else {
1305 str->append("None");
1306 }
1307 str->append("))");
1308 }
1309 #endif
1310
1311 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
1312 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
1313 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
1314