3 * Copyright 2006 The Android Open Source Project
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
9 #include "SkBlurMaskFilter.h"
10 #include "SkBlurMask.h"
11 #include "SkGpuBlurUtils.h"
12 #include "SkReadBuffer.h"
13 #include "SkWriteBuffer.h"
14 #include "SkMaskFilter.h"
17 #include "SkStringUtils.h"
18 #include "SkStrokeRec.h"
21 #include "GrContext.h"
22 #include "GrTexture.h"
24 #include "gl/GrGLEffect.h"
25 #include "effects/GrSimpleTextureEffect.h"
26 #include "GrTBackendEffectFactory.h"
27 #include "SkGrPixelRef.h"
30 class SkBlurMaskFilterImpl : public SkMaskFilter {
32 SkBlurMaskFilterImpl(SkScalar sigma, SkBlurMaskFilter::BlurStyle, uint32_t flags);
34 // overrides from SkMaskFilter
35 virtual SkMask::Format getFormat() const SK_OVERRIDE;
36 virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
37 SkIPoint* margin) const SK_OVERRIDE;
40 virtual bool canFilterMaskGPU(const SkRect& devBounds,
41 const SkIRect& clipBounds,
43 SkRect* maskRect) const SK_OVERRIDE;
44 virtual bool directFilterMaskGPU(GrContext* context,
46 const SkStrokeRec& strokeRec,
47 const SkPath& path) const SK_OVERRIDE;
49 virtual bool filterMaskGPU(GrTexture* src,
51 const SkRect& maskRect,
53 bool canOverwriteSrc) const SK_OVERRIDE;
56 virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
58 SkDEVCODE(virtual void toString(SkString* str) const SK_OVERRIDE;)
59 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
62 virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
63 const SkIRect& clipBounds,
64 NinePatch*) const SK_OVERRIDE;
66 virtual FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&,
67 const SkIRect& clipBounds,
68 NinePatch*) const SK_OVERRIDE;
70 bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
71 SkIPoint* margin, SkMask::CreateMode createMode) const;
74 // To avoid unseemly allocation requests (esp. for finite platforms like
75 // handset) we limit the radius so something manageable. (as opposed to
76 // a request like 10,000)
77 static const SkScalar kMAX_BLUR_SIGMA;
80 SkBlurMaskFilter::BlurStyle fBlurStyle;
83 SkBlurMaskFilterImpl(SkReadBuffer&);
84 virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
86 SkScalar computeXformedSigma(const SkMatrix& ctm) const {
87 bool ignoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
89 SkScalar xformedSigma = ignoreTransform ? fSigma : ctm.mapRadius(fSigma);
90 return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA);
93 typedef SkMaskFilter INHERITED;
96 const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128);
98 SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius,
99 SkBlurMaskFilter::BlurStyle style,
101 // use !(radius > 0) instead of radius <= 0 to reject NaN values
102 if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount
103 || flags > SkBlurMaskFilter::kAll_BlurFlag) {
107 SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
109 return SkNEW_ARGS(SkBlurMaskFilterImpl, (sigma, style, flags));
112 SkMaskFilter* SkBlurMaskFilter::Create(SkBlurMaskFilter::BlurStyle style,
115 // use !(sigma > 0) instead of sigma <= 0 to reject NaN values
116 if (!(sigma > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount
117 || flags > SkBlurMaskFilter::kAll_BlurFlag) {
121 return SkNEW_ARGS(SkBlurMaskFilterImpl, (sigma, style, flags));
124 ///////////////////////////////////////////////////////////////////////////////
126 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma,
127 SkBlurMaskFilter::BlurStyle style,
129 : fSigma(sigma), fBlurStyle(style), fBlurFlags(flags) {
133 fGamma = new U8[256];
135 SkBlurMask::BuildSqrGamma(fGamma, gammaScale);
137 SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale);
140 SkASSERT(fSigma >= 0);
141 SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount);
142 SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
145 SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
146 return SkMask::kA8_Format;
149 bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
150 const SkMatrix& matrix,
151 SkIPoint* margin) const{
152 SkScalar sigma = this->computeXformedSigma(matrix);
154 SkBlurMask::Quality blurQuality =
155 (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
156 SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality;
158 return SkBlurMask::BoxBlur(dst, src, sigma, (SkBlurMask::Style)fBlurStyle,
159 blurQuality, margin);
162 bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
163 const SkMatrix& matrix,
164 SkIPoint* margin, SkMask::CreateMode createMode) const{
165 SkScalar sigma = computeXformedSigma(matrix);
167 return SkBlurMask::BlurRect(sigma, dst, r, (SkBlurMask::Style)fBlurStyle,
171 #include "SkCanvas.h"
173 static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) {
174 SkASSERT(mask != NULL);
176 bounds.roundOut(&mask->fBounds);
177 mask->fRowBytes = SkAlign4(mask->fBounds.width());
178 mask->fFormat = SkMask::kA8_Format;
179 const size_t size = mask->computeImageSize();
180 mask->fImage = SkMask::AllocImage(size);
181 if (NULL == mask->fImage) {
185 // FIXME: use sk_calloc in AllocImage?
186 sk_bzero(mask->fImage, size);
190 static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) {
191 if (!prepare_to_draw_into_mask(rrect.rect(), mask)) {
195 // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a
196 // clean way to share more code?
198 bitmap.setConfig(SkBitmap::kA8_Config,
199 mask->fBounds.width(), mask->fBounds.height(),
201 bitmap.setPixels(mask->fImage);
203 SkCanvas canvas(bitmap);
204 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
205 -SkIntToScalar(mask->fBounds.top()));
208 paint.setAntiAlias(true);
209 canvas.drawRRect(rrect, paint);
213 static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) {
214 if (!prepare_to_draw_into_mask(rects[0], mask)) {
219 bitmap.setConfig(SkBitmap::kA8_Config,
220 mask->fBounds.width(), mask->fBounds.height(),
222 bitmap.setPixels(mask->fImage);
224 SkCanvas canvas(bitmap);
225 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
226 -SkIntToScalar(mask->fBounds.top()));
229 paint.setAntiAlias(true);
232 canvas.drawRect(rects[0], paint);
234 // todo: do I need a fast way to do this?
236 path.addRect(rects[0]);
237 path.addRect(rects[1]);
238 path.setFillType(SkPath::kEvenOdd_FillType);
239 canvas.drawPath(path, paint);
244 static bool rect_exceeds(const SkRect& r, SkScalar v) {
245 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
246 r.width() > v || r.height() > v;
249 SkMaskFilter::FilterReturn
250 SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix,
251 const SkIRect& clipBounds,
252 NinePatch* patch) const {
253 SkASSERT(patch != NULL);
254 switch (rrect.getType()) {
255 case SkRRect::kUnknown_Type:
256 // Unknown should never be returned.
259 case SkRRect::kEmpty_Type:
261 return kFalse_FilterReturn;
263 case SkRRect::kRect_Type:
264 // We should have caught this earlier.
267 case SkRRect::kOval_Type:
268 // The nine patch special case does not handle ovals, and we
269 // already have code for rectangles.
270 return kUnimplemented_FilterReturn;
272 case SkRRect::kSimple_Type:
274 case SkRRect::kComplex_Type:
275 // These can take advantage of this fast path.
279 // TODO: report correct metrics for innerstyle, where we do not grow the
280 // total bounds, but we do need an inset the size of our blur-radius
281 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
282 return kUnimplemented_FilterReturn;
285 // TODO: take clipBounds into account to limit our coordinates up front
286 // for now, just skip too-large src rects (to take the old code path).
287 if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) {
288 return kUnimplemented_FilterReturn;
293 rrect.rect().roundOut(&srcM.fBounds);
295 srcM.fFormat = SkMask::kA8_Format;
298 if (!this->filterMask(&dstM, srcM, matrix, &margin)) {
299 return kFalse_FilterReturn;
302 // Now figure out the appropriate width and height of the smaller round rectangle
303 // to stretch. It will take into account the larger radius per side as well as double
304 // the margin, to account for inner and outer blur.
305 const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
306 const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
307 const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
308 const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
310 const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX);
311 const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX);
313 // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover
314 // any fractional space on either side plus 1 for the part to stretch.
315 const SkScalar stretchSize = SkIntToScalar(3);
317 const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize;
318 if (totalSmallWidth >= rrect.rect().width()) {
319 // There is no valid piece to stretch.
320 return kUnimplemented_FilterReturn;
323 const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY);
324 const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY);
326 const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize;
327 if (totalSmallHeight >= rrect.rect().height()) {
328 // There is no valid piece to stretch.
329 return kUnimplemented_FilterReturn;
332 SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight);
336 radii[SkRRect::kUpperLeft_Corner] = UL;
337 radii[SkRRect::kUpperRight_Corner] = UR;
338 radii[SkRRect::kLowerRight_Corner] = LR;
339 radii[SkRRect::kLowerLeft_Corner] = LL;
340 smallRR.setRectRadii(smallR, radii);
342 if (!draw_rrect_into_mask(smallRR, &srcM)) {
343 return kFalse_FilterReturn;
346 SkAutoMaskFreeImage amf(srcM.fImage);
348 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
349 return kFalse_FilterReturn;
352 patch->fMask.fBounds.offsetTo(0, 0);
353 patch->fOuterRect = dstM.fBounds;
354 patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1;
355 patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1;
356 return kTrue_FilterReturn;
359 #ifdef SK_IGNORE_FAST_RECT_BLUR
360 SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", false, "Use the faster analytic blur approach for ninepatch rects" );
362 SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", true, "Use the faster analytic blur approach for ninepatch rects" );
365 SkMaskFilter::FilterReturn
366 SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
367 const SkMatrix& matrix,
368 const SkIRect& clipBounds,
369 NinePatch* patch) const {
370 if (count < 1 || count > 2) {
371 return kUnimplemented_FilterReturn;
374 // TODO: report correct metrics for innerstyle, where we do not grow the
375 // total bounds, but we do need an inset the size of our blur-radius
376 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle ||
377 SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) {
378 return kUnimplemented_FilterReturn;
381 // TODO: take clipBounds into account to limit our coordinates up front
382 // for now, just skip too-large src rects (to take the old code path).
383 if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
384 return kUnimplemented_FilterReturn;
389 rects[0].roundOut(&srcM.fBounds);
391 srcM.fFormat = SkMask::kA8_Format;
394 bool filterResult = false;
395 if (count == 1 && c_analyticBlurNinepatch) {
396 // special case for fast rect blur
397 // don't actually do the blur the first time, just compute the correct size
398 filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
399 SkMask::kJustComputeBounds_CreateMode);
401 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
405 return kFalse_FilterReturn;
409 * smallR is the smallest version of 'rect' that will still guarantee that
410 * we get the same blur results on all edges, plus 1 center row/col that is
411 * representative of the extendible/stretchable edges of the ninepatch.
412 * Since our actual edge may be fractional we inset 1 more to be sure we
413 * don't miss any interior blur.
414 * x is an added pixel of blur, and { and } are the (fractional) edge
415 * pixels from the original rect.
417 * x x { x x .... x x } x x
419 * Thus, in this case, we inset by a total of 5 (on each side) beginning
420 * with our outer-rect (dstM.fBounds)
425 // +2 is from +1 for each edge (to account for possible fractional edges
426 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
427 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
431 innerIR = srcM.fBounds;
432 center.set(smallW, smallH);
434 SkASSERT(2 == count);
435 rects[1].roundIn(&innerIR);
436 center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
437 smallH + (innerIR.top() - srcM.fBounds.top()));
440 // +1 so we get a clean, stretchable, center row/col
444 // we want the inset amounts to be integral, so we don't change any
445 // fractional phase on the fRight or fBottom of our smallR.
446 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
447 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
448 if (dx < 0 || dy < 0) {
449 // we're too small, relative to our blur, to break into nine-patch,
450 // so we ask to have our normal filterMask() be called.
451 return kUnimplemented_FilterReturn;
454 smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
455 if (smallR[0].width() < 2 || smallR[0].height() < 2) {
456 return kUnimplemented_FilterReturn;
459 smallR[1].set(rects[1].left(), rects[1].top(),
460 rects[1].right() - dx, rects[1].bottom() - dy);
461 SkASSERT(!smallR[1].isEmpty());
464 if (count > 1 || !c_analyticBlurNinepatch) {
465 if (!draw_rects_into_mask(smallR, count, &srcM)) {
466 return kFalse_FilterReturn;
469 SkAutoMaskFreeImage amf(srcM.fImage);
471 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
472 return kFalse_FilterReturn;
475 if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
476 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
477 return kFalse_FilterReturn;
480 patch->fMask.fBounds.offsetTo(0, 0);
481 patch->fOuterRect = dstM.fBounds;
482 patch->fCenter = center;
483 return kTrue_FilterReturn;
486 void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
488 SkScalar pad = 3.0f * fSigma;
490 dst->set(src.fLeft - pad, src.fTop - pad,
491 src.fRight + pad, src.fBottom + pad);
494 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkReadBuffer& buffer)
495 : SkMaskFilter(buffer) {
496 fSigma = buffer.readScalar();
497 fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt();
498 fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag;
499 SkASSERT(fSigma >= 0);
500 SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount);
503 void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
504 this->INHERITED::flatten(buffer);
505 buffer.writeScalar(fSigma);
506 buffer.writeInt(fBlurStyle);
507 buffer.writeUInt(fBlurFlags);
512 class GrGLRectBlurEffect;
514 class GrRectBlurEffect : public GrEffect {
516 virtual ~GrRectBlurEffect();
518 static const char* Name() { return "RectBlur"; }
520 typedef GrGLRectBlurEffect GLEffect;
522 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
523 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
526 * Create a simple filter effect with custom bicubic coefficients.
528 static GrEffectRef* Create(GrContext *context, const SkRect& rect,
530 GrTexture *horizontalScanline = NULL, *verticalScanline = NULL;
531 bool createdScanlines = CreateScanlineTextures(context, sigma,
532 SkScalarCeilToInt(rect.width()),
533 SkScalarCeilToInt(rect.height()),
534 &horizontalScanline, &verticalScanline);
535 SkAutoTUnref<GrTexture> hunref(horizontalScanline), vunref(verticalScanline);
536 if (!createdScanlines) {
539 AutoEffectUnref effect(SkNEW_ARGS(GrRectBlurEffect, (rect, sigma,
540 horizontalScanline, verticalScanline)));
541 return CreateEffectRef(effect);
544 unsigned int getWidth() const { return fWidth; }
545 unsigned int getHeight() const { return fHeight; }
546 float getSigma() const { return fSigma; }
547 const GrCoordTransform& getTransform() const { return fTransform; }
550 GrRectBlurEffect(const SkRect& rect, float sigma,
551 GrTexture *horizontal_scanline, GrTexture *vertical_scanline);
552 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
554 static bool CreateScanlineTextures(GrContext *context, float sigma,
555 unsigned int width, unsigned int height,
556 GrTexture **horizontalScanline,
557 GrTexture **verticalScanline);
559 unsigned int fWidth, fHeight;
561 GrTextureAccess fHorizontalScanlineAccess;
562 GrTextureAccess fVerticalScanlineAccess;
563 GrCoordTransform fTransform;
565 GR_DECLARE_EFFECT_TEST;
567 typedef GrEffect INHERITED;
570 class GrGLRectBlurEffect : public GrGLEffect {
572 GrGLRectBlurEffect(const GrBackendEffectFactory& factory,
573 const GrDrawEffect&);
574 virtual void emitCode(GrGLShaderBuilder*,
577 const char* outputColor,
578 const char* inputColor,
579 const TransformedCoordsArray&,
580 const TextureSamplerArray&) SK_OVERRIDE;
582 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
585 typedef GrGLUniformManager::UniformHandle UniformHandle;
587 UniformHandle fWidthUni;
588 UniformHandle fHeightUni;
590 typedef GrGLEffect INHERITED;
593 GrGLRectBlurEffect::GrGLRectBlurEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
594 : INHERITED(factory) {
597 void GrGLRectBlurEffect::emitCode(GrGLShaderBuilder* builder,
600 const char* outputColor,
601 const char* inputColor,
602 const TransformedCoordsArray& coords,
603 const TextureSamplerArray& samplers) {
605 SkString texture_coords = builder->ensureFSCoords2D(coords, 0);
608 builder->fsCodeAppendf("\tvec4 src=%s;\n", inputColor);
610 builder->fsCodeAppendf("\tvec4 src=vec4(1)\n;");
613 builder->fsCodeAppendf("\tvec4 horiz = ");
614 builder->fsAppendTextureLookup( samplers[0], texture_coords.c_str() );
615 builder->fsCodeAppendf(";\n");
616 builder->fsCodeAppendf("\tvec4 vert = ");
617 builder->fsAppendTextureLookup( samplers[1], texture_coords.c_str() );
618 builder->fsCodeAppendf(";\n");
620 builder->fsCodeAppendf("\tfloat final = (horiz*vert).r;\n");
621 builder->fsCodeAppendf("\t%s = final*src;\n", outputColor);
624 void GrGLRectBlurEffect::setData(const GrGLUniformManager& uman,
625 const GrDrawEffect& drawEffect) {
628 bool GrRectBlurEffect::CreateScanlineTextures(GrContext *context, float sigma,
629 unsigned int width, unsigned int height,
630 GrTexture **horizontalScanline,
631 GrTexture **verticalScanline) {
632 GrTextureParams params;
633 GrTextureDesc texDesc;
635 unsigned int profile_size = SkScalarFloorToInt(6*sigma);
637 texDesc.fWidth = width;
639 texDesc.fConfig = kAlpha_8_GrPixelConfig;
641 static const GrCacheID::Domain gBlurProfileDomain = GrCacheID::GenerateDomain();
643 memset(&key, 0, sizeof(key));
644 key.fData32[0] = profile_size;
645 key.fData32[1] = width;
647 GrCacheID horizontalCacheID(gBlurProfileDomain, key);
649 uint8_t *profile = NULL;
650 SkAutoTDeleteArray<uint8_t> ada(NULL);
652 *horizontalScanline = context->findAndRefTexture(texDesc, horizontalCacheID, ¶ms);
654 if (NULL == *horizontalScanline) {
656 SkBlurMask::ComputeBlurProfile(sigma, &profile);
659 SkAutoTMalloc<uint8_t> horizontalPixels(width);
660 SkBlurMask::ComputeBlurredScanline(horizontalPixels, profile, width, sigma);
662 *horizontalScanline = context->createTexture(¶ms, texDesc, horizontalCacheID,
663 horizontalPixels, 0);
665 if (NULL == *horizontalScanline) {
671 texDesc.fHeight = height;
673 key.fData32[2] = height;
674 GrCacheID verticalCacheID(gBlurProfileDomain, key);
676 *verticalScanline = context->findAndRefTexture(texDesc, verticalCacheID, ¶ms);
677 if (NULL == *verticalScanline) {
678 if (NULL == profile) {
679 SkBlurMask::ComputeBlurProfile(sigma, &profile);
683 SkAutoTMalloc<uint8_t> verticalPixels(height);
684 SkBlurMask::ComputeBlurredScanline(verticalPixels, profile, height, sigma);
686 *verticalScanline = context->createTexture(¶ms, texDesc, verticalCacheID,
689 if (NULL == *verticalScanline) {
690 SkSafeSetNull(*horizontalScanline);
698 GrRectBlurEffect::GrRectBlurEffect(const SkRect& rect, float sigma,
699 GrTexture *horizontal_scanline, GrTexture *vertical_scanline)
701 fWidth(horizontal_scanline->width()),
702 fHeight(vertical_scanline->width()),
704 fHorizontalScanlineAccess(horizontal_scanline),
705 fVerticalScanlineAccess(vertical_scanline) {
707 mat.setRectToRect(rect, SkRect::MakeWH(1,1), SkMatrix::kFill_ScaleToFit);
708 fTransform.reset(kLocal_GrCoordSet, mat);
709 this->addTextureAccess(&fHorizontalScanlineAccess);
710 this->addTextureAccess(&fVerticalScanlineAccess);
711 this->addCoordTransform(&fTransform);
714 GrRectBlurEffect::~GrRectBlurEffect() {
717 const GrBackendEffectFactory& GrRectBlurEffect::getFactory() const {
718 return GrTBackendEffectFactory<GrRectBlurEffect>::getInstance();
721 bool GrRectBlurEffect::onIsEqual(const GrEffect& sBase) const {
722 const GrRectBlurEffect& s = CastEffect<GrRectBlurEffect>(sBase);
723 return this->getWidth() == s.getWidth() &&
724 this->getHeight() == s.getHeight() &&
725 this->getSigma() == s.getSigma() &&
726 this->getTransform() == s.getTransform();
729 void GrRectBlurEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
734 GR_DEFINE_EFFECT_TEST(GrRectBlurEffect);
736 GrEffectRef* GrRectBlurEffect::TestCreate(SkRandom* random,
738 const GrDrawTargetCaps&,
740 float sigma = random->nextRangeF(3,8);
741 float width = random->nextRangeF(200,300);
742 float height = random->nextRangeF(200,300);
743 return GrRectBlurEffect::Create(context, SkRect::MakeWH(width, height), sigma);
747 bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrContext* context,
749 const SkStrokeRec& strokeRec,
750 const SkPath& path) const {
751 if (fBlurStyle != SkBlurMaskFilter::kNormal_BlurStyle) {
756 if (!path.isRect(&rect)) {
760 if (!strokeRec.isFillStyle()) {
764 SkMatrix ctm = context->getMatrix();
765 SkScalar xformedSigma = this->computeXformedSigma(ctm);
766 rect.outset(3*xformedSigma, 3*xformedSigma);
768 SkAutoTUnref<GrEffectRef> effect(GrRectBlurEffect::Create(
769 context, rect, xformedSigma));
774 GrContext::AutoMatrix am;
775 if (!am.setIdentity(context, grp)) {
780 grp->addCoverageEffect(effect);
782 context->drawRect(*grp, rect);
786 bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds,
787 const SkIRect& clipBounds,
789 SkRect* maskRect) const {
790 SkScalar xformedSigma = this->computeXformedSigma(ctm);
791 if (xformedSigma <= 0) {
795 static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64);
796 static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32);
798 if (srcBounds.width() <= kMIN_GPU_BLUR_SIZE &&
799 srcBounds.height() <= kMIN_GPU_BLUR_SIZE &&
800 xformedSigma <= kMIN_GPU_BLUR_SIGMA) {
801 // We prefer to blur small rect with small radius via CPU.
805 if (NULL == maskRect) {
806 // don't need to compute maskRect
810 float sigma3 = 3 * SkScalarToFloat(xformedSigma);
812 SkRect clipRect = SkRect::Make(clipBounds);
813 SkRect srcRect(srcBounds);
815 // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
816 srcRect.outset(sigma3, sigma3);
817 clipRect.outset(sigma3, sigma3);
818 srcRect.intersect(clipRect);
823 bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src,
825 const SkRect& maskRect,
827 bool canOverwriteSrc) const {
828 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
830 GrContext* context = src->getContext();
832 GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
834 SkScalar xformedSigma = this->computeXformedSigma(ctm);
835 SkASSERT(xformedSigma > 0);
837 // If we're doing a normal blur, we can clobber the pathTexture in the
838 // gaussianBlur. Otherwise, we need to save it for later compositing.
839 bool isNormalBlur = (SkBlurMaskFilter::kNormal_BlurStyle == fBlurStyle);
840 *result = SkGpuBlurUtils::GaussianBlur(context, src, isNormalBlur && canOverwriteSrc,
841 clipRect, false, xformedSigma, xformedSigma);
842 if (NULL == *result) {
847 context->setIdentityMatrix();
850 matrix.setIDiv(src->width(), src->height());
851 // Blend pathTexture over blurTexture.
852 GrContext::AutoRenderTarget art(context, (*result)->asRenderTarget());
853 paint.addColorEffect(GrSimpleTextureEffect::Create(src, matrix))->unref();
854 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
855 // inner: dst = dst * src
856 paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
857 } else if (SkBlurMaskFilter::kSolid_BlurStyle == fBlurStyle) {
858 // solid: dst = src + dst - src * dst
859 // = (1 - dst) * src + 1 * dst
860 paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff);
861 } else if (SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) {
862 // outer: dst = dst * (1 - src)
863 // = 0 * src + (1 - src) * dst
864 paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
866 context->drawRect(paint, clipRect);
872 #endif // SK_SUPPORT_GPU
876 void SkBlurMaskFilterImpl::toString(SkString* str) const {
877 str->append("SkBlurMaskFilterImpl: (");
879 str->append("sigma: ");
880 str->appendScalar(fSigma);
883 static const char* gStyleName[SkBlurMaskFilter::kBlurStyleCount] = {
884 "normal", "solid", "outer", "inner"
887 str->appendf("style: %s ", gStyleName[fBlurStyle]);
888 str->append("flags: (");
890 bool needSeparator = false;
891 SkAddFlagToString(str,
892 SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag),
893 "IgnoreXform", &needSeparator);
894 SkAddFlagToString(str,
895 SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag),
896 "HighQuality", &needSeparator);
904 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
905 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
906 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END