1
2 /*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9 #include "SkBlurMaskFilter.h"
10 #include "SkBlurMask.h"
11 #include "SkGpuBlurUtils.h"
12 #include "SkReadBuffer.h"
13 #include "SkWriteBuffer.h"
14 #include "SkMaskFilter.h"
15 #include "SkRRect.h"
16 #include "SkRTConf.h"
17 #include "SkStringUtils.h"
18 #include "SkStrokeRec.h"
19
20 #if SK_SUPPORT_GPU
21 #include "GrContext.h"
22 #include "GrTexture.h"
23 #include "GrFragmentProcessor.h"
24 #include "GrInvariantOutput.h"
25 #include "SkGrPixelRef.h"
26 #include "SkDraw.h"
27 #include "effects/GrSimpleTextureEffect.h"
28 #include "gl/GrGLProcessor.h"
29 #include "gl/builders/GrGLProgramBuilder.h"
30 #endif
31
ConvertRadiusToSigma(SkScalar radius)32 SkScalar SkBlurMaskFilter::ConvertRadiusToSigma(SkScalar radius) {
33 return SkBlurMask::ConvertRadiusToSigma(radius);
34 }
35
36 class SkBlurMaskFilterImpl : public SkMaskFilter {
37 public:
38 SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle, uint32_t flags);
39
40 // overrides from SkMaskFilter
41 SkMask::Format getFormat() const override;
42 virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
43 SkIPoint* margin) const override;
44
45 #if SK_SUPPORT_GPU
46 virtual bool canFilterMaskGPU(const SkRect& devBounds,
47 const SkIRect& clipBounds,
48 const SkMatrix& ctm,
49 SkRect* maskRect) const override;
50 virtual bool directFilterMaskGPU(GrContext* context,
51 GrRenderTarget* rt,
52 GrPaint* grp,
53 const GrClip&,
54 const SkMatrix& viewMatrix,
55 const SkStrokeRec& strokeRec,
56 const SkPath& path) const override;
57 virtual bool directFilterRRectMaskGPU(GrContext* context,
58 GrRenderTarget* rt,
59 GrPaint* grp,
60 const GrClip&,
61 const SkMatrix& viewMatrix,
62 const SkStrokeRec& strokeRec,
63 const SkRRect& rrect) const override;
64
65 virtual bool filterMaskGPU(GrTexture* src,
66 const SkMatrix& ctm,
67 const SkRect& maskRect,
68 GrTexture** result,
69 bool canOverwriteSrc) const override;
70 #endif
71
72 void computeFastBounds(const SkRect&, SkRect*) const override;
73 bool asABlur(BlurRec*) const override;
74
75 SK_TO_STRING_OVERRIDE()
76 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
77
78 protected:
79 virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
80 const SkIRect& clipBounds,
81 NinePatch*) const override;
82
83 virtual FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&,
84 const SkIRect& clipBounds,
85 NinePatch*) const override;
86
87 bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
88 SkIPoint* margin, SkMask::CreateMode createMode) const;
89 bool filterRRectMask(SkMask* dstM, const SkRRect& r, const SkMatrix& matrix,
90 SkIPoint* margin, SkMask::CreateMode createMode) const;
91
92 private:
93 // To avoid unseemly allocation requests (esp. for finite platforms like
94 // handset) we limit the radius so something manageable. (as opposed to
95 // a request like 10,000)
96 static const SkScalar kMAX_BLUR_SIGMA;
97
98 SkScalar fSigma;
99 SkBlurStyle fBlurStyle;
100 uint32_t fBlurFlags;
101
getQuality() const102 SkBlurQuality getQuality() const {
103 return (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
104 kHigh_SkBlurQuality : kLow_SkBlurQuality;
105 }
106
107 SkBlurMaskFilterImpl(SkReadBuffer&);
108 void flatten(SkWriteBuffer&) const override;
109
computeXformedSigma(const SkMatrix & ctm) const110 SkScalar computeXformedSigma(const SkMatrix& ctm) const {
111 bool ignoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
112
113 SkScalar xformedSigma = ignoreTransform ? fSigma : ctm.mapRadius(fSigma);
114 return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA);
115 }
116
117 friend class SkBlurMaskFilter;
118
119 typedef SkMaskFilter INHERITED;
120 };
121
122 const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128);
123
Create(SkBlurStyle style,SkScalar sigma,uint32_t flags)124 SkMaskFilter* SkBlurMaskFilter::Create(SkBlurStyle style, SkScalar sigma, uint32_t flags) {
125 if (!SkScalarIsFinite(sigma) || sigma <= 0) {
126 return NULL;
127 }
128 if ((unsigned)style > (unsigned)kLastEnum_SkBlurStyle) {
129 return NULL;
130 }
131 if (flags > SkBlurMaskFilter::kAll_BlurFlag) {
132 return NULL;
133 }
134 return SkNEW_ARGS(SkBlurMaskFilterImpl, (sigma, style, flags));
135 }
136
137 ///////////////////////////////////////////////////////////////////////////////
138
SkBlurMaskFilterImpl(SkScalar sigma,SkBlurStyle style,uint32_t flags)139 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style, uint32_t flags)
140 : fSigma(sigma)
141 , fBlurStyle(style)
142 , fBlurFlags(flags) {
143 SkASSERT(fSigma > 0);
144 SkASSERT((unsigned)style <= kLastEnum_SkBlurStyle);
145 SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
146 }
147
getFormat() const148 SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
149 return SkMask::kA8_Format;
150 }
151
asABlur(BlurRec * rec) const152 bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const {
153 if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
154 return false;
155 }
156
157 if (rec) {
158 rec->fSigma = fSigma;
159 rec->fStyle = fBlurStyle;
160 rec->fQuality = this->getQuality();
161 }
162 return true;
163 }
164
filterMask(SkMask * dst,const SkMask & src,const SkMatrix & matrix,SkIPoint * margin) const165 bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
166 const SkMatrix& matrix,
167 SkIPoint* margin) const{
168 SkScalar sigma = this->computeXformedSigma(matrix);
169 return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, this->getQuality(), margin);
170 }
171
filterRectMask(SkMask * dst,const SkRect & r,const SkMatrix & matrix,SkIPoint * margin,SkMask::CreateMode createMode) const172 bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
173 const SkMatrix& matrix,
174 SkIPoint* margin, SkMask::CreateMode createMode) const{
175 SkScalar sigma = computeXformedSigma(matrix);
176
177 return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle,
178 margin, createMode);
179 }
180
filterRRectMask(SkMask * dst,const SkRRect & r,const SkMatrix & matrix,SkIPoint * margin,SkMask::CreateMode createMode) const181 bool SkBlurMaskFilterImpl::filterRRectMask(SkMask* dst, const SkRRect& r,
182 const SkMatrix& matrix,
183 SkIPoint* margin, SkMask::CreateMode createMode) const{
184 SkScalar sigma = computeXformedSigma(matrix);
185
186 return SkBlurMask::BlurRRect(sigma, dst, r, fBlurStyle,
187 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 != NULL);
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 (NULL == 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 != NULL);
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.fImage = NULL;
355 srcM.fFormat = SkMask::kA8_Format;
356 srcM.fRowBytes = 0;
357
358 bool filterResult = false;
359 if (c_analyticBlurRRect) {
360 // special case for fast round rect blur
361 // don't actually do the blur the first time, just compute the correct size
362 filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin,
363 SkMask::kJustComputeBounds_CreateMode);
364 }
365
366 if (!filterResult) {
367 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
368 }
369
370 if (!filterResult) {
371 return kFalse_FilterReturn;
372 }
373
374 // Now figure out the appropriate width and height of the smaller round rectangle
375 // to stretch. It will take into account the larger radius per side as well as double
376 // the margin, to account for inner and outer blur.
377 const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
378 const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
379 const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
380 const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
381
382 const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX);
383 const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX);
384
385 // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover
386 // any fractional space on either side plus 1 for the part to stretch.
387 const SkScalar stretchSize = SkIntToScalar(3);
388
389 const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize;
390 if (totalSmallWidth >= rrect.rect().width()) {
391 // There is no valid piece to stretch.
392 return kUnimplemented_FilterReturn;
393 }
394
395 const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY);
396 const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY);
397
398 const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize;
399 if (totalSmallHeight >= rrect.rect().height()) {
400 // There is no valid piece to stretch.
401 return kUnimplemented_FilterReturn;
402 }
403
404 SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight);
405
406 SkRRect smallRR;
407 SkVector radii[4];
408 radii[SkRRect::kUpperLeft_Corner] = UL;
409 radii[SkRRect::kUpperRight_Corner] = UR;
410 radii[SkRRect::kLowerRight_Corner] = LR;
411 radii[SkRRect::kLowerLeft_Corner] = LL;
412 smallRR.setRectRadii(smallR, radii);
413
414 const SkScalar sigma = this->computeXformedSigma(matrix);
415 SkCachedData* cache = find_cached_rrect(&patch->fMask, sigma, fBlurStyle,
416 this->getQuality(), smallRR);
417 if (!cache) {
418 bool analyticBlurWorked = false;
419 if (c_analyticBlurRRect) {
420 analyticBlurWorked =
421 this->filterRRectMask(&patch->fMask, smallRR, matrix, &margin,
422 SkMask::kComputeBoundsAndRenderImage_CreateMode);
423 }
424
425 if (!analyticBlurWorked) {
426 if (!draw_rrect_into_mask(smallRR, &srcM)) {
427 return kFalse_FilterReturn;
428 }
429
430 SkAutoMaskFreeImage amf(srcM.fImage);
431
432 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
433 return kFalse_FilterReturn;
434 }
435 }
436 cache = add_cached_rrect(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallRR);
437 }
438
439 patch->fMask.fBounds.offsetTo(0, 0);
440 patch->fOuterRect = dstM.fBounds;
441 patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1;
442 patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1;
443 SkASSERT(NULL == patch->fCache);
444 patch->fCache = cache; // transfer ownership to patch
445 return kTrue_FilterReturn;
446 }
447
448 SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", true, "Use the faster analytic blur approach for ninepatch rects" );
449
450 SkMaskFilter::FilterReturn
filterRectsToNine(const SkRect rects[],int count,const SkMatrix & matrix,const SkIRect & clipBounds,NinePatch * patch) const451 SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
452 const SkMatrix& matrix,
453 const SkIRect& clipBounds,
454 NinePatch* patch) const {
455 if (count < 1 || count > 2) {
456 return kUnimplemented_FilterReturn;
457 }
458
459 // TODO: report correct metrics for innerstyle, where we do not grow the
460 // total bounds, but we do need an inset the size of our blur-radius
461 if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) {
462 return kUnimplemented_FilterReturn;
463 }
464
465 // TODO: take clipBounds into account to limit our coordinates up front
466 // for now, just skip too-large src rects (to take the old code path).
467 if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
468 return kUnimplemented_FilterReturn;
469 }
470
471 SkIPoint margin;
472 SkMask srcM, dstM;
473 srcM.fBounds = rects[0].roundOut();
474 srcM.fImage = NULL;
475 srcM.fFormat = SkMask::kA8_Format;
476 srcM.fRowBytes = 0;
477
478 bool filterResult = false;
479 if (count == 1 && c_analyticBlurNinepatch) {
480 // special case for fast rect blur
481 // don't actually do the blur the first time, just compute the correct size
482 filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
483 SkMask::kJustComputeBounds_CreateMode);
484 } else {
485 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
486 }
487
488 if (!filterResult) {
489 return kFalse_FilterReturn;
490 }
491
492 /*
493 * smallR is the smallest version of 'rect' that will still guarantee that
494 * we get the same blur results on all edges, plus 1 center row/col that is
495 * representative of the extendible/stretchable edges of the ninepatch.
496 * Since our actual edge may be fractional we inset 1 more to be sure we
497 * don't miss any interior blur.
498 * x is an added pixel of blur, and { and } are the (fractional) edge
499 * pixels from the original rect.
500 *
501 * x x { x x .... x x } x x
502 *
503 * Thus, in this case, we inset by a total of 5 (on each side) beginning
504 * with our outer-rect (dstM.fBounds)
505 */
506 SkRect smallR[2];
507 SkIPoint center;
508
509 // +2 is from +1 for each edge (to account for possible fractional edges
510 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
511 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
512 SkIRect innerIR;
513
514 if (1 == count) {
515 innerIR = srcM.fBounds;
516 center.set(smallW, smallH);
517 } else {
518 SkASSERT(2 == count);
519 rects[1].roundIn(&innerIR);
520 center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
521 smallH + (innerIR.top() - srcM.fBounds.top()));
522 }
523
524 // +1 so we get a clean, stretchable, center row/col
525 smallW += 1;
526 smallH += 1;
527
528 // we want the inset amounts to be integral, so we don't change any
529 // fractional phase on the fRight or fBottom of our smallR.
530 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
531 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
532 if (dx < 0 || dy < 0) {
533 // we're too small, relative to our blur, to break into nine-patch,
534 // so we ask to have our normal filterMask() be called.
535 return kUnimplemented_FilterReturn;
536 }
537
538 smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
539 if (smallR[0].width() < 2 || smallR[0].height() < 2) {
540 return kUnimplemented_FilterReturn;
541 }
542 if (2 == count) {
543 smallR[1].set(rects[1].left(), rects[1].top(),
544 rects[1].right() - dx, rects[1].bottom() - dy);
545 SkASSERT(!smallR[1].isEmpty());
546 }
547
548 const SkScalar sigma = this->computeXformedSigma(matrix);
549 SkCachedData* cache = find_cached_rects(&patch->fMask, sigma, fBlurStyle,
550 this->getQuality(), smallR, count);
551 if (!cache) {
552 if (count > 1 || !c_analyticBlurNinepatch) {
553 if (!draw_rects_into_mask(smallR, count, &srcM)) {
554 return kFalse_FilterReturn;
555 }
556
557 SkAutoMaskFreeImage amf(srcM.fImage);
558
559 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
560 return kFalse_FilterReturn;
561 }
562 } else {
563 if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
564 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
565 return kFalse_FilterReturn;
566 }
567 }
568 cache = add_cached_rects(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallR, count);
569 }
570 patch->fMask.fBounds.offsetTo(0, 0);
571 patch->fOuterRect = dstM.fBounds;
572 patch->fCenter = center;
573 SkASSERT(NULL == patch->fCache);
574 patch->fCache = cache; // transfer ownership to patch
575 return kTrue_FilterReturn;
576 }
577
computeFastBounds(const SkRect & src,SkRect * dst) const578 void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
579 SkRect* dst) const {
580 SkScalar pad = 3.0f * fSigma;
581
582 dst->set(src.fLeft - pad, src.fTop - pad,
583 src.fRight + pad, src.fBottom + pad);
584 }
585
CreateProc(SkReadBuffer & buffer)586 SkFlattenable* SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
587 const SkScalar sigma = buffer.readScalar();
588 const unsigned style = buffer.readUInt();
589 const unsigned flags = buffer.readUInt();
590 if (style <= kLastEnum_SkBlurStyle) {
591 return SkBlurMaskFilter::Create((SkBlurStyle)style, sigma, flags);
592 }
593 return NULL;
594 }
595
flatten(SkWriteBuffer & buffer) const596 void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
597 buffer.writeScalar(fSigma);
598 buffer.writeUInt(fBlurStyle);
599 buffer.writeUInt(fBlurFlags);
600 }
601
602 #if SK_SUPPORT_GPU
603
604 class GrGLRectBlurEffect;
605
606 class GrRectBlurEffect : public GrFragmentProcessor {
607 public:
608 virtual ~GrRectBlurEffect();
609
name() const610 const char* name() const override { return "RectBlur"; }
611
612 virtual void getGLProcessorKey(const GrGLSLCaps& caps,
613 GrProcessorKeyBuilder* b) const override;
614
615 GrGLFragmentProcessor* createGLInstance() const override;
616
617 /**
618 * Create a simple filter effect with custom bicubic coefficients.
619 */
Create(GrTextureProvider * textureProvider,const SkRect & rect,float sigma)620 static GrFragmentProcessor* Create(GrTextureProvider *textureProvider, const SkRect& rect,
621 float sigma) {
622 GrTexture *blurProfileTexture = NULL;
623 int doubleProfileSize = SkScalarCeilToInt(12*sigma);
624
625 if (doubleProfileSize >= rect.width() || doubleProfileSize >= rect.height()) {
626 // if the blur sigma is too large so the gaussian overlaps the whole
627 // rect in either direction, fall back to CPU path for now.
628
629 return NULL;
630 }
631
632 bool createdBlurProfileTexture = CreateBlurProfileTexture(
633 textureProvider, sigma, &blurProfileTexture);
634 SkAutoTUnref<GrTexture> hunref(blurProfileTexture);
635 if (!createdBlurProfileTexture) {
636 return NULL;
637 }
638 return SkNEW_ARGS(GrRectBlurEffect, (rect, sigma, blurProfileTexture));
639 }
640
getRect() const641 const SkRect& getRect() const { return fRect; }
getSigma() const642 float getSigma() const { return fSigma; }
643
644 private:
645 GrRectBlurEffect(const SkRect& rect, float sigma, GrTexture *blur_profile);
646 bool onIsEqual(const GrFragmentProcessor&) const override;
647
648 void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
649
650 static bool CreateBlurProfileTexture(GrTextureProvider*, float sigma,
651 GrTexture **blurProfileTexture);
652
653 SkRect fRect;
654 float fSigma;
655 GrTextureAccess fBlurProfileAccess;
656
657 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
658
659 typedef GrFragmentProcessor INHERITED;
660 };
661
662 class GrGLRectBlurEffect : public GrGLFragmentProcessor {
663 public:
GrGLRectBlurEffect(const GrProcessor &)664 GrGLRectBlurEffect(const GrProcessor&) {}
665 virtual void emitCode(GrGLFPBuilder*,
666 const GrFragmentProcessor&,
667 const char* outputColor,
668 const char* inputColor,
669 const TransformedCoordsArray&,
670 const TextureSamplerArray&) override;
671
672 void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
673
674 private:
675 typedef GrGLProgramDataManager::UniformHandle UniformHandle;
676
677 UniformHandle fProxyRectUniform;
678 UniformHandle fProfileSizeUniform;
679
680 typedef GrGLFragmentProcessor INHERITED;
681 };
682
OutputRectBlurProfileLookup(GrGLFragmentBuilder * fsBuilder,const GrGLShaderBuilder::TextureSampler & sampler,const char * output,const char * profileSize,const char * loc,const char * blurred_width,const char * sharp_width)683 void OutputRectBlurProfileLookup(GrGLFragmentBuilder* fsBuilder,
684 const GrGLShaderBuilder::TextureSampler& sampler,
685 const char *output,
686 const char *profileSize, const char *loc,
687 const char *blurred_width,
688 const char *sharp_width) {
689 fsBuilder->codeAppendf("\tfloat %s;\n", output);
690 fsBuilder->codeAppendf("\t\t{\n");
691 fsBuilder->codeAppendf("\t\t\tfloat coord = (0.5 * (abs(2.0*%s - %s) - %s))/%s;\n",
692 loc, blurred_width, sharp_width, profileSize);
693 fsBuilder->codeAppendf("\t\t\t%s = ", output);
694 fsBuilder->appendTextureLookup(sampler, "vec2(coord,0.5)");
695 fsBuilder->codeAppend(".a;\n");
696 fsBuilder->codeAppendf("\t\t}\n");
697 }
698
emitCode(GrGLFPBuilder * builder,const GrFragmentProcessor &,const char * outputColor,const char * inputColor,const TransformedCoordsArray & coords,const TextureSamplerArray & samplers)699 void GrGLRectBlurEffect::emitCode(GrGLFPBuilder* builder,
700 const GrFragmentProcessor&,
701 const char* outputColor,
702 const char* inputColor,
703 const TransformedCoordsArray& coords,
704 const TextureSamplerArray& samplers) {
705
706 const char *rectName;
707 const char *profileSizeName;
708
709 fProxyRectUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
710 kVec4f_GrSLType,
711 kDefault_GrSLPrecision,
712 "proxyRect",
713 &rectName);
714 fProfileSizeUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
715 kFloat_GrSLType,
716 kDefault_GrSLPrecision,
717 "profileSize",
718 &profileSizeName);
719
720 GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
721 const char *fragmentPos = fsBuilder->fragmentPosition();
722
723 if (inputColor) {
724 fsBuilder->codeAppendf("\tvec4 src=%s;\n", inputColor);
725 } else {
726 fsBuilder->codeAppendf("\tvec4 src=vec4(1)\n;");
727 }
728
729 fsBuilder->codeAppendf("\tvec2 translatedPos = %s.xy - %s.xy;\n", fragmentPos, rectName );
730 fsBuilder->codeAppendf("\tfloat width = %s.z - %s.x;\n", rectName, rectName);
731 fsBuilder->codeAppendf("\tfloat height = %s.w - %s.y;\n", rectName, rectName);
732
733 fsBuilder->codeAppendf("\tvec2 smallDims = vec2(width - %s, height-%s);\n", profileSizeName, profileSizeName);
734 fsBuilder->codeAppendf("\tfloat center = 2.0 * floor(%s/2.0 + .25) - 1.0;\n", profileSizeName);
735 fsBuilder->codeAppendf("\tvec2 wh = smallDims - vec2(center,center);\n");
736
737 OutputRectBlurProfileLookup(fsBuilder, samplers[0], "horiz_lookup", profileSizeName, "translatedPos.x", "width", "wh.x");
738 OutputRectBlurProfileLookup(fsBuilder, samplers[0], "vert_lookup", profileSizeName, "translatedPos.y", "height", "wh.y");
739
740 fsBuilder->codeAppendf("\tfloat final = horiz_lookup * vert_lookup;\n");
741 fsBuilder->codeAppendf("\t%s = src * final;\n", outputColor );
742 }
743
setData(const GrGLProgramDataManager & pdman,const GrProcessor & proc)744 void GrGLRectBlurEffect::setData(const GrGLProgramDataManager& pdman,
745 const GrProcessor& proc) {
746 const GrRectBlurEffect& rbe = proc.cast<GrRectBlurEffect>();
747 SkRect rect = rbe.getRect();
748
749 pdman.set4f(fProxyRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
750 pdman.set1f(fProfileSizeUniform, SkScalarCeilToScalar(6*rbe.getSigma()));
751 }
752
CreateBlurProfileTexture(GrTextureProvider * textureProvider,float sigma,GrTexture ** blurProfileTexture)753 bool GrRectBlurEffect::CreateBlurProfileTexture(GrTextureProvider* textureProvider, float sigma,
754 GrTexture **blurProfileTexture) {
755 GrSurfaceDesc texDesc;
756
757 unsigned int profileSize = SkScalarCeilToInt(6*sigma);
758
759 texDesc.fWidth = profileSize;
760 texDesc.fHeight = 1;
761 texDesc.fConfig = kAlpha_8_GrPixelConfig;
762
763 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
764 GrUniqueKey key;
765 GrUniqueKey::Builder builder(&key, kDomain, 1);
766 builder[0] = profileSize;
767 builder.finish();
768
769 uint8_t *profile = NULL;
770 SkAutoTDeleteArray<uint8_t> ada(NULL);
771
772 *blurProfileTexture = textureProvider->findAndRefTextureByUniqueKey(key);
773
774 if (NULL == *blurProfileTexture) {
775
776 SkBlurMask::ComputeBlurProfile(sigma, &profile);
777 ada.reset(profile);
778
779 *blurProfileTexture = textureProvider->createTexture(texDesc, true, profile, 0);
780
781 if (NULL == *blurProfileTexture) {
782 return false;
783 }
784 textureProvider->assignUniqueKeyToTexture(key, *blurProfileTexture);
785 }
786
787 return true;
788 }
789
GrRectBlurEffect(const SkRect & rect,float sigma,GrTexture * blur_profile)790 GrRectBlurEffect::GrRectBlurEffect(const SkRect& rect, float sigma,
791 GrTexture *blur_profile)
792 : fRect(rect),
793 fSigma(sigma),
794 fBlurProfileAccess(blur_profile) {
795 this->initClassID<GrRectBlurEffect>();
796 this->addTextureAccess(&fBlurProfileAccess);
797 this->setWillReadFragmentPosition();
798 }
799
~GrRectBlurEffect()800 GrRectBlurEffect::~GrRectBlurEffect() {
801 }
802
getGLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const803 void GrRectBlurEffect::getGLProcessorKey(const GrGLSLCaps& caps,
804 GrProcessorKeyBuilder* b) const {
805 GrGLRectBlurEffect::GenKey(*this, caps, b);
806 }
807
createGLInstance() const808 GrGLFragmentProcessor* GrRectBlurEffect::createGLInstance() const {
809 return SkNEW_ARGS(GrGLRectBlurEffect, (*this));
810 }
811
onIsEqual(const GrFragmentProcessor & sBase) const812 bool GrRectBlurEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
813 const GrRectBlurEffect& s = sBase.cast<GrRectBlurEffect>();
814 return this->getSigma() == s.getSigma() && this->getRect() == s.getRect();
815 }
816
onComputeInvariantOutput(GrInvariantOutput * inout) const817 void GrRectBlurEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
818 inout->mulByUnknownSingleComponent();
819 }
820
821 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRectBlurEffect);
822
TestCreate(SkRandom * random,GrContext * context,const GrDrawTargetCaps &,GrTexture **)823 GrFragmentProcessor* GrRectBlurEffect::TestCreate(SkRandom* random,
824 GrContext* context,
825 const GrDrawTargetCaps&,
826 GrTexture**) {
827 float sigma = random->nextRangeF(3,8);
828 float width = random->nextRangeF(200,300);
829 float height = random->nextRangeF(200,300);
830 return GrRectBlurEffect::Create(context->textureProvider(), SkRect::MakeWH(width, height),
831 sigma);
832 }
833
834
directFilterMaskGPU(GrContext * context,GrRenderTarget * rt,GrPaint * grp,const GrClip & clip,const SkMatrix & viewMatrix,const SkStrokeRec & strokeRec,const SkPath & path) const835 bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrContext* context,
836 GrRenderTarget* rt,
837 GrPaint* grp,
838 const GrClip& clip,
839 const SkMatrix& viewMatrix,
840 const SkStrokeRec& strokeRec,
841 const SkPath& path) const {
842 if (fBlurStyle != kNormal_SkBlurStyle) {
843 return false;
844 }
845
846 SkRect rect;
847 if (!path.isRect(&rect)) {
848 return false;
849 }
850
851 if (!strokeRec.isFillStyle()) {
852 return false;
853 }
854
855 SkMatrix ctm = viewMatrix;
856 SkScalar xformedSigma = this->computeXformedSigma(ctm);
857
858 int pad=SkScalarCeilToInt(6*xformedSigma)/2;
859 rect.outset(SkIntToScalar(pad), SkIntToScalar(pad));
860
861 SkAutoTUnref<GrFragmentProcessor> fp(GrRectBlurEffect::Create(
862 context->textureProvider(), rect, xformedSigma));
863 if (!fp) {
864 return false;
865 }
866
867 grp->addCoverageProcessor(fp);
868
869 SkMatrix inverse;
870 if (!viewMatrix.invert(&inverse)) {
871 return false;
872 }
873 context->drawNonAARectWithLocalMatrix(rt, clip, *grp, SkMatrix::I(), rect, inverse);
874 return true;
875 }
876
877 class GrRRectBlurEffect : public GrFragmentProcessor {
878 public:
879
880 static GrFragmentProcessor* Create(GrContext* context, float sigma, const SkRRect&);
881
~GrRRectBlurEffect()882 virtual ~GrRRectBlurEffect() {};
name() const883 const char* name() const override { return "GrRRectBlur"; }
884
getRRect() const885 const SkRRect& getRRect() const { return fRRect; }
getSigma() const886 float getSigma() const { return fSigma; }
887
888 virtual void getGLProcessorKey(const GrGLSLCaps& caps,
889 GrProcessorKeyBuilder* b) const override;
890
891 GrGLFragmentProcessor* createGLInstance() const override;
892
893 private:
894 GrRRectBlurEffect(float sigma, const SkRRect&, GrTexture* profileTexture);
895
896 bool onIsEqual(const GrFragmentProcessor& other) const override;
897
898 void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
899
900 SkRRect fRRect;
901 float fSigma;
902 GrTextureAccess fNinePatchAccess;
903
904 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
905
906 typedef GrFragmentProcessor INHERITED;
907 };
908
909
Create(GrContext * context,float sigma,const SkRRect & rrect)910 GrFragmentProcessor* GrRRectBlurEffect::Create(GrContext* context, float sigma,
911 const SkRRect& rrect) {
912 if (!rrect.isSimpleCircular()) {
913 return NULL;
914 }
915
916 // Make sure we can successfully ninepatch this rrect -- the blur sigma has to be
917 // sufficiently small relative to both the size of the corner radius and the
918 // width (and height) of the rrect.
919
920 unsigned int blurRadius = 3*SkScalarCeilToInt(sigma-1/6.0f);
921 unsigned int cornerRadius = SkScalarCeilToInt(rrect.getSimpleRadii().x());
922 if (cornerRadius + blurRadius > rrect.width()/2 ||
923 cornerRadius + blurRadius > rrect.height()/2) {
924 return NULL;
925 }
926
927 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
928 GrUniqueKey key;
929 GrUniqueKey::Builder builder(&key, kDomain, 2);
930 builder[0] = blurRadius;
931 builder[1] = cornerRadius;
932 builder.finish();
933
934 SkAutoTUnref<GrTexture> blurNinePatchTexture(
935 context->textureProvider()->findAndRefTextureByUniqueKey(key));
936
937 if (!blurNinePatchTexture) {
938 SkMask mask;
939
940 unsigned int smallRectSide = 2*(blurRadius + cornerRadius) + 1;
941
942 mask.fBounds = SkIRect::MakeWH(smallRectSide, smallRectSide);
943 mask.fFormat = SkMask::kA8_Format;
944 mask.fRowBytes = mask.fBounds.width();
945 mask.fImage = SkMask::AllocImage(mask.computeTotalImageSize());
946 SkAutoMaskFreeImage amfi(mask.fImage);
947
948 memset(mask.fImage, 0, mask.computeTotalImageSize());
949
950 SkRect smallRect;
951 smallRect.setWH(SkIntToScalar(smallRectSide), SkIntToScalar(smallRectSide));
952
953 SkRRect smallRRect;
954 smallRRect.setRectXY(smallRect, SkIntToScalar(cornerRadius), SkIntToScalar(cornerRadius));
955
956 SkPath path;
957 path.addRRect( smallRRect );
958
959 SkDraw::DrawToMask(path, &mask.fBounds, NULL, NULL, &mask,
960 SkMask::kJustRenderImage_CreateMode, SkPaint::kFill_Style);
961
962 SkMask blurredMask;
963 SkBlurMask::BoxBlur(&blurredMask, mask, sigma, kNormal_SkBlurStyle, kHigh_SkBlurQuality,
964 NULL, true );
965
966 unsigned int texSide = smallRectSide + 2*blurRadius;
967 GrSurfaceDesc texDesc;
968 texDesc.fWidth = texSide;
969 texDesc.fHeight = texSide;
970 texDesc.fConfig = kAlpha_8_GrPixelConfig;
971
972 blurNinePatchTexture.reset(
973 context->textureProvider()->createTexture(texDesc, true, blurredMask.fImage, 0));
974 SkMask::FreeImage(blurredMask.fImage);
975 if (!blurNinePatchTexture) {
976 return NULL;
977 }
978 context->textureProvider()->assignUniqueKeyToTexture(key, blurNinePatchTexture);
979 }
980 return SkNEW_ARGS(GrRRectBlurEffect, (sigma, rrect, blurNinePatchTexture));
981 }
982
onComputeInvariantOutput(GrInvariantOutput * inout) const983 void GrRRectBlurEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
984 inout->mulByUnknownSingleComponent();
985 }
986
GrRRectBlurEffect(float sigma,const SkRRect & rrect,GrTexture * ninePatchTexture)987 GrRRectBlurEffect::GrRRectBlurEffect(float sigma, const SkRRect& rrect, GrTexture *ninePatchTexture)
988 : fRRect(rrect),
989 fSigma(sigma),
990 fNinePatchAccess(ninePatchTexture) {
991 this->initClassID<GrRRectBlurEffect>();
992 this->addTextureAccess(&fNinePatchAccess);
993 this->setWillReadFragmentPosition();
994 }
995
onIsEqual(const GrFragmentProcessor & other) const996 bool GrRRectBlurEffect::onIsEqual(const GrFragmentProcessor& other) const {
997 const GrRRectBlurEffect& rrbe = other.cast<GrRRectBlurEffect>();
998 return fRRect.getSimpleRadii().fX == rrbe.fRRect.getSimpleRadii().fX && fSigma == rrbe.fSigma;
999 }
1000
1001 //////////////////////////////////////////////////////////////////////////////
1002
1003 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRRectBlurEffect);
1004
TestCreate(SkRandom * random,GrContext * context,const GrDrawTargetCaps & caps,GrTexture * [])1005 GrFragmentProcessor* GrRRectBlurEffect::TestCreate(SkRandom* random,
1006 GrContext* context,
1007 const GrDrawTargetCaps& caps,
1008 GrTexture*[]) {
1009 SkScalar w = random->nextRangeScalar(100.f, 1000.f);
1010 SkScalar h = random->nextRangeScalar(100.f, 1000.f);
1011 SkScalar r = random->nextRangeF(1.f, 9.f);
1012 SkScalar sigma = random->nextRangeF(1.f,10.f);
1013 SkRRect rrect;
1014 rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
1015 return GrRRectBlurEffect::Create(context, sigma, rrect);
1016 }
1017
1018 //////////////////////////////////////////////////////////////////////////////
1019
1020 class GrGLRRectBlurEffect : public GrGLFragmentProcessor {
1021 public:
GrGLRRectBlurEffect(const GrProcessor &)1022 GrGLRRectBlurEffect(const GrProcessor&) {}
1023
1024 virtual void emitCode(GrGLFPBuilder*,
1025 const GrFragmentProcessor&,
1026 const char* outputColor,
1027 const char* inputColor,
1028 const TransformedCoordsArray&,
1029 const TextureSamplerArray&) override;
1030
1031 void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
1032
1033 private:
1034 GrGLProgramDataManager::UniformHandle fProxyRectUniform;
1035 GrGLProgramDataManager::UniformHandle fCornerRadiusUniform;
1036 GrGLProgramDataManager::UniformHandle fBlurRadiusUniform;
1037 typedef GrGLFragmentProcessor INHERITED;
1038 };
1039
emitCode(GrGLFPBuilder * builder,const GrFragmentProcessor &,const char * outputColor,const char * inputColor,const TransformedCoordsArray &,const TextureSamplerArray & samplers)1040 void GrGLRRectBlurEffect::emitCode(GrGLFPBuilder* builder,
1041 const GrFragmentProcessor&,
1042 const char* outputColor,
1043 const char* inputColor,
1044 const TransformedCoordsArray&,
1045 const TextureSamplerArray& samplers) {
1046 const char *rectName;
1047 const char *cornerRadiusName;
1048 const char *blurRadiusName;
1049
1050 // The proxy rect has left, top, right, and bottom edges correspond to
1051 // components x, y, z, and w, respectively.
1052
1053 fProxyRectUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
1054 kVec4f_GrSLType,
1055 kDefault_GrSLPrecision,
1056 "proxyRect",
1057 &rectName);
1058 fCornerRadiusUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
1059 kFloat_GrSLType,
1060 kDefault_GrSLPrecision,
1061 "cornerRadius",
1062 &cornerRadiusName);
1063 fBlurRadiusUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
1064 kFloat_GrSLType,
1065 kDefault_GrSLPrecision,
1066 "blurRadius",
1067 &blurRadiusName);
1068
1069 GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
1070 const char* fragmentPos = fsBuilder->fragmentPosition();
1071
1072 // warp the fragment position to the appropriate part of the 9patch blur texture
1073
1074 fsBuilder->codeAppendf("\t\tvec2 rectCenter = (%s.xy + %s.zw)/2.0;\n", rectName, rectName);
1075 fsBuilder->codeAppendf("\t\tvec2 translatedFragPos = %s.xy - %s.xy;\n", fragmentPos, rectName);
1076 fsBuilder->codeAppendf("\t\tfloat threshold = %s + 2.0*%s;\n", cornerRadiusName, blurRadiusName );
1077 fsBuilder->codeAppendf("\t\tvec2 middle = %s.zw - %s.xy - 2.0*threshold;\n", rectName, rectName );
1078
1079 fsBuilder->codeAppendf("\t\tif (translatedFragPos.x >= threshold && translatedFragPos.x < (middle.x+threshold)) {\n" );
1080 fsBuilder->codeAppendf("\t\t\ttranslatedFragPos.x = threshold;\n");
1081 fsBuilder->codeAppendf("\t\t} else if (translatedFragPos.x >= (middle.x + threshold)) {\n");
1082 fsBuilder->codeAppendf("\t\t\ttranslatedFragPos.x -= middle.x - 1.0;\n");
1083 fsBuilder->codeAppendf("\t\t}\n");
1084
1085 fsBuilder->codeAppendf("\t\tif (translatedFragPos.y > threshold && translatedFragPos.y < (middle.y+threshold)) {\n" );
1086 fsBuilder->codeAppendf("\t\t\ttranslatedFragPos.y = threshold;\n");
1087 fsBuilder->codeAppendf("\t\t} else if (translatedFragPos.y >= (middle.y + threshold)) {\n");
1088 fsBuilder->codeAppendf("\t\t\ttranslatedFragPos.y -= middle.y - 1.0;\n");
1089 fsBuilder->codeAppendf("\t\t}\n");
1090
1091 fsBuilder->codeAppendf("\t\tvec2 proxyDims = vec2(2.0*threshold+1.0);\n");
1092 fsBuilder->codeAppendf("\t\tvec2 texCoord = translatedFragPos / proxyDims;\n");
1093
1094 fsBuilder->codeAppendf("\t%s = ", outputColor);
1095 fsBuilder->appendTextureLookupAndModulate(inputColor, samplers[0], "texCoord");
1096 fsBuilder->codeAppend(";\n");
1097 }
1098
setData(const GrGLProgramDataManager & pdman,const GrProcessor & proc)1099 void GrGLRRectBlurEffect::setData(const GrGLProgramDataManager& pdman,
1100 const GrProcessor& proc) {
1101 const GrRRectBlurEffect& brre = proc.cast<GrRRectBlurEffect>();
1102 SkRRect rrect = brre.getRRect();
1103
1104 float blurRadius = 3.f*SkScalarCeilToScalar(brre.getSigma()-1/6.0f);
1105 pdman.set1f(fBlurRadiusUniform, blurRadius);
1106
1107 SkRect rect = rrect.getBounds();
1108 rect.outset(blurRadius, blurRadius);
1109 pdman.set4f(fProxyRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
1110
1111 SkScalar radius = 0;
1112 SkASSERT(rrect.isSimpleCircular() || rrect.isRect());
1113 radius = rrect.getSimpleRadii().fX;
1114 pdman.set1f(fCornerRadiusUniform, radius);
1115 }
1116
getGLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const1117 void GrRRectBlurEffect::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
1118 GrGLRRectBlurEffect::GenKey(*this, caps, b);
1119 }
1120
createGLInstance() const1121 GrGLFragmentProcessor* GrRRectBlurEffect::createGLInstance() const {
1122 return SkNEW_ARGS(GrGLRRectBlurEffect, (*this));
1123 }
1124
directFilterRRectMaskGPU(GrContext * context,GrRenderTarget * rt,GrPaint * grp,const GrClip & clip,const SkMatrix & viewMatrix,const SkStrokeRec & strokeRec,const SkRRect & rrect) const1125 bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context,
1126 GrRenderTarget* rt,
1127 GrPaint* grp,
1128 const GrClip& clip,
1129 const SkMatrix& viewMatrix,
1130 const SkStrokeRec& strokeRec,
1131 const SkRRect& rrect) const {
1132 if (fBlurStyle != kNormal_SkBlurStyle) {
1133 return false;
1134 }
1135
1136 if (!strokeRec.isFillStyle()) {
1137 return false;
1138 }
1139
1140 SkRect proxy_rect = rrect.rect();
1141 SkMatrix ctm = viewMatrix;
1142 SkScalar xformedSigma = this->computeXformedSigma(ctm);
1143 float extra=3.f*SkScalarCeilToScalar(xformedSigma-1/6.0f);
1144 proxy_rect.outset(extra, extra);
1145
1146 SkAutoTUnref<GrFragmentProcessor> fp(GrRRectBlurEffect::Create(context, xformedSigma, rrect));
1147 if (!fp) {
1148 return false;
1149 }
1150
1151 grp->addCoverageProcessor(fp);
1152
1153 SkMatrix inverse;
1154 if (!viewMatrix.invert(&inverse)) {
1155 return false;
1156 }
1157 context->drawNonAARectWithLocalMatrix(rt, clip, *grp, SkMatrix::I(), proxy_rect, inverse);
1158 return true;
1159 }
1160
canFilterMaskGPU(const SkRect & srcBounds,const SkIRect & clipBounds,const SkMatrix & ctm,SkRect * maskRect) const1161 bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds,
1162 const SkIRect& clipBounds,
1163 const SkMatrix& ctm,
1164 SkRect* maskRect) const {
1165 SkScalar xformedSigma = this->computeXformedSigma(ctm);
1166 if (xformedSigma <= 0) {
1167 return false;
1168 }
1169
1170 static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64);
1171 static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32);
1172
1173 if (srcBounds.width() <= kMIN_GPU_BLUR_SIZE &&
1174 srcBounds.height() <= kMIN_GPU_BLUR_SIZE &&
1175 xformedSigma <= kMIN_GPU_BLUR_SIGMA) {
1176 // We prefer to blur small rect with small radius via CPU.
1177 return false;
1178 }
1179
1180 if (NULL == maskRect) {
1181 // don't need to compute maskRect
1182 return true;
1183 }
1184
1185 float sigma3 = 3 * SkScalarToFloat(xformedSigma);
1186
1187 SkRect clipRect = SkRect::Make(clipBounds);
1188 SkRect srcRect(srcBounds);
1189
1190 // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
1191 srcRect.outset(sigma3, sigma3);
1192 clipRect.outset(sigma3, sigma3);
1193 if (!srcRect.intersect(clipRect)) {
1194 srcRect.setEmpty();
1195 }
1196 *maskRect = srcRect;
1197 return true;
1198 }
1199
filterMaskGPU(GrTexture * src,const SkMatrix & ctm,const SkRect & maskRect,GrTexture ** result,bool canOverwriteSrc) const1200 bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src,
1201 const SkMatrix& ctm,
1202 const SkRect& maskRect,
1203 GrTexture** result,
1204 bool canOverwriteSrc) const {
1205 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
1206
1207 GrContext* context = src->getContext();
1208
1209 SkScalar xformedSigma = this->computeXformedSigma(ctm);
1210 SkASSERT(xformedSigma > 0);
1211
1212 // If we're doing a normal blur, we can clobber the pathTexture in the
1213 // gaussianBlur. Otherwise, we need to save it for later compositing.
1214 bool isNormalBlur = (kNormal_SkBlurStyle == fBlurStyle);
1215 *result = SkGpuBlurUtils::GaussianBlur(context, src, isNormalBlur && canOverwriteSrc,
1216 clipRect, false, xformedSigma, xformedSigma);
1217 if (NULL == *result) {
1218 return false;
1219 }
1220
1221 if (!isNormalBlur) {
1222 GrPaint paint;
1223 SkMatrix matrix;
1224 matrix.setIDiv(src->width(), src->height());
1225 // Blend pathTexture over blurTexture.
1226 paint.addCoverageProcessor(GrSimpleTextureEffect::Create(src, matrix))->unref();
1227 if (kInner_SkBlurStyle == fBlurStyle) {
1228 // inner: dst = dst * src
1229 paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op);
1230 } else if (kSolid_SkBlurStyle == fBlurStyle) {
1231 // solid: dst = src + dst - src * dst
1232 // = src + (1 - src) * dst
1233 paint.setCoverageSetOpXPFactory(SkRegion::kUnion_Op);
1234 } else if (kOuter_SkBlurStyle == fBlurStyle) {
1235 // outer: dst = dst * (1 - src)
1236 // = 0 * src + (1 - src) * dst
1237 paint.setCoverageSetOpXPFactory(SkRegion::kDifference_Op);
1238 }
1239 context->drawRect((*result)->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(),
1240 clipRect);
1241 }
1242
1243 return true;
1244 }
1245
1246 #endif // SK_SUPPORT_GPU
1247
1248
1249 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const1250 void SkBlurMaskFilterImpl::toString(SkString* str) const {
1251 str->append("SkBlurMaskFilterImpl: (");
1252
1253 str->append("sigma: ");
1254 str->appendScalar(fSigma);
1255 str->append(" ");
1256
1257 static const char* gStyleName[kLastEnum_SkBlurStyle + 1] = {
1258 "normal", "solid", "outer", "inner"
1259 };
1260
1261 str->appendf("style: %s ", gStyleName[fBlurStyle]);
1262 str->append("flags: (");
1263 if (fBlurFlags) {
1264 bool needSeparator = false;
1265 SkAddFlagToString(str,
1266 SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag),
1267 "IgnoreXform", &needSeparator);
1268 SkAddFlagToString(str,
1269 SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag),
1270 "HighQuality", &needSeparator);
1271 } else {
1272 str->append("None");
1273 }
1274 str->append("))");
1275 }
1276 #endif
1277
1278 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
1279 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
1280 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
1281