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