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 "SkFlattenableBuffers.h" 13 #include "SkMaskFilter.h" 14 #include "SkRRect.h" 15 #include "SkRTConf.h" 16 #include "SkStringUtils.h" 17 #include "SkStrokeRec.h" 18 19 #if SK_SUPPORT_GPU 20 #include "GrContext.h" 21 #include "GrTexture.h" 22 #include "effects/GrSimpleTextureEffect.h" 23 #include "SkGrPixelRef.h" 24 #endif 25 26 class SkBlurMaskFilterImpl : public SkMaskFilter { 27 public: 28 SkBlurMaskFilterImpl(SkScalar sigma, SkBlurMaskFilter::BlurStyle, uint32_t flags); 29 30 // overrides from SkMaskFilter 31 virtual SkMask::Format getFormat() const SK_OVERRIDE; 32 virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, 33 SkIPoint* margin) const SK_OVERRIDE; 34 35 #if SK_SUPPORT_GPU 36 virtual bool canFilterMaskGPU(const SkRect& devBounds, 37 const SkIRect& clipBounds, 38 const SkMatrix& ctm, 39 SkRect* maskRect) const SK_OVERRIDE; 40 virtual bool filterMaskGPU(GrTexture* src, 41 const SkRect& maskRect, 42 GrTexture** result, 43 bool canOverwriteSrc) const; 44 #endif 45 46 virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE; 47 48 SkDEVCODE(virtual void toString(SkString* str) const SK_OVERRIDE;) 49 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl) 50 51 protected: 52 virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&, 53 const SkIRect& clipBounds, 54 NinePatch*) const SK_OVERRIDE; 55 56 virtual FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&, 57 const SkIRect& clipBounds, 58 NinePatch*) const SK_OVERRIDE; 59 60 bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix, 61 SkIPoint* margin, SkMask::CreateMode createMode) const; 62 63 private: 64 // To avoid unseemly allocation requests (esp. for finite platforms like 65 // handset) we limit the radius so something manageable. (as opposed to 66 // a request like 10,000) 67 static const SkScalar kMAX_BLUR_SIGMA; 68 69 SkScalar fSigma; 70 SkBlurMaskFilter::BlurStyle fBlurStyle; 71 uint32_t fBlurFlags; 72 73 SkBlurMaskFilterImpl(SkFlattenableReadBuffer&); 74 virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE; 75 76 SkScalar computeXformedSigma(const SkMatrix& ctm) const { 77 bool ignoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag); 78 79 SkScalar xformedSigma = ignoreTransform ? fSigma : ctm.mapRadius(fSigma); 80 return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA); 81 } 82 83 typedef SkMaskFilter INHERITED; 84 }; 85 86 const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128); 87 88 SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius, 89 SkBlurMaskFilter::BlurStyle style, 90 uint32_t flags) { 91 // use !(radius > 0) instead of radius <= 0 to reject NaN values 92 if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount 93 || flags > SkBlurMaskFilter::kAll_BlurFlag) { 94 return NULL; 95 } 96 97 SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); 98 99 return SkNEW_ARGS(SkBlurMaskFilterImpl, (sigma, style, flags)); 100 } 101 102 SkMaskFilter* SkBlurMaskFilter::Create(SkBlurMaskFilter::BlurStyle style, 103 SkScalar sigma, 104 uint32_t flags) { 105 // use !(sigma > 0) instead of sigma <= 0 to reject NaN values 106 if (!(sigma > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount 107 || flags > SkBlurMaskFilter::kAll_BlurFlag) { 108 return NULL; 109 } 110 111 return SkNEW_ARGS(SkBlurMaskFilterImpl, (sigma, style, flags)); 112 } 113 114 /////////////////////////////////////////////////////////////////////////////// 115 116 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, 117 SkBlurMaskFilter::BlurStyle style, 118 uint32_t flags) 119 : fSigma(sigma), fBlurStyle(style), fBlurFlags(flags) { 120 #if 0 121 fGamma = NULL; 122 if (gammaScale) { 123 fGamma = new U8[256]; 124 if (gammaScale > 0) 125 SkBlurMask::BuildSqrGamma(fGamma, gammaScale); 126 else 127 SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale); 128 } 129 #endif 130 SkASSERT(fSigma >= 0); 131 SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount); 132 SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag); 133 } 134 135 SkMask::Format SkBlurMaskFilterImpl::getFormat() const { 136 return SkMask::kA8_Format; 137 } 138 139 bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, 140 const SkMatrix& matrix, 141 SkIPoint* margin) const{ 142 SkScalar sigma = this->computeXformedSigma(matrix); 143 144 SkBlurMask::Quality blurQuality = 145 (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? 146 SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality; 147 148 return SkBlurMask::BoxBlur(dst, src, sigma, (SkBlurMask::Style)fBlurStyle, 149 blurQuality, margin); 150 } 151 152 bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r, 153 const SkMatrix& matrix, 154 SkIPoint* margin, SkMask::CreateMode createMode) const{ 155 SkScalar sigma = computeXformedSigma(matrix); 156 157 return SkBlurMask::BlurRect(sigma, dst, r, (SkBlurMask::Style)fBlurStyle, 158 margin, createMode); 159 } 160 161 #include "SkCanvas.h" 162 163 static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) { 164 SkASSERT(mask != NULL); 165 166 bounds.roundOut(&mask->fBounds); 167 mask->fRowBytes = SkAlign4(mask->fBounds.width()); 168 mask->fFormat = SkMask::kA8_Format; 169 const size_t size = mask->computeImageSize(); 170 mask->fImage = SkMask::AllocImage(size); 171 if (NULL == mask->fImage) { 172 return false; 173 } 174 175 // FIXME: use sk_calloc in AllocImage? 176 sk_bzero(mask->fImage, size); 177 return true; 178 } 179 180 static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) { 181 if (!prepare_to_draw_into_mask(rrect.rect(), mask)) { 182 return false; 183 } 184 185 // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a 186 // clean way to share more code? 187 SkBitmap bitmap; 188 bitmap.setConfig(SkBitmap::kA8_Config, 189 mask->fBounds.width(), mask->fBounds.height(), 190 mask->fRowBytes); 191 bitmap.setPixels(mask->fImage); 192 193 SkCanvas canvas(bitmap); 194 canvas.translate(-SkIntToScalar(mask->fBounds.left()), 195 -SkIntToScalar(mask->fBounds.top())); 196 197 SkPaint paint; 198 paint.setAntiAlias(true); 199 canvas.drawRRect(rrect, paint); 200 return true; 201 } 202 203 static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) { 204 if (!prepare_to_draw_into_mask(rects[0], mask)) { 205 return false; 206 } 207 208 SkBitmap bitmap; 209 bitmap.setConfig(SkBitmap::kA8_Config, 210 mask->fBounds.width(), mask->fBounds.height(), 211 mask->fRowBytes); 212 bitmap.setPixels(mask->fImage); 213 214 SkCanvas canvas(bitmap); 215 canvas.translate(-SkIntToScalar(mask->fBounds.left()), 216 -SkIntToScalar(mask->fBounds.top())); 217 218 SkPaint paint; 219 paint.setAntiAlias(true); 220 221 if (1 == count) { 222 canvas.drawRect(rects[0], paint); 223 } else { 224 // todo: do I need a fast way to do this? 225 SkPath path; 226 path.addRect(rects[0]); 227 path.addRect(rects[1]); 228 path.setFillType(SkPath::kEvenOdd_FillType); 229 canvas.drawPath(path, paint); 230 } 231 return true; 232 } 233 234 static bool rect_exceeds(const SkRect& r, SkScalar v) { 235 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v || 236 r.width() > v || r.height() > v; 237 } 238 239 SkMaskFilter::FilterReturn 240 SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix, 241 const SkIRect& clipBounds, 242 NinePatch* patch) const { 243 SkASSERT(patch != NULL); 244 switch (rrect.getType()) { 245 case SkRRect::kUnknown_Type: 246 // Unknown should never be returned. 247 SkASSERT(false); 248 // Fall through. 249 case SkRRect::kEmpty_Type: 250 // Nothing to draw. 251 return kFalse_FilterReturn; 252 253 case SkRRect::kRect_Type: 254 // We should have caught this earlier. 255 SkASSERT(false); 256 // Fall through. 257 case SkRRect::kOval_Type: 258 // The nine patch special case does not handle ovals, and we 259 // already have code for rectangles. 260 return kUnimplemented_FilterReturn; 261 262 case SkRRect::kSimple_Type: 263 // Fall through. 264 case SkRRect::kComplex_Type: 265 // These can take advantage of this fast path. 266 break; 267 } 268 269 // TODO: report correct metrics for innerstyle, where we do not grow the 270 // total bounds, but we do need an inset the size of our blur-radius 271 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) { 272 return kUnimplemented_FilterReturn; 273 } 274 275 // TODO: take clipBounds into account to limit our coordinates up front 276 // for now, just skip too-large src rects (to take the old code path). 277 if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) { 278 return kUnimplemented_FilterReturn; 279 } 280 281 SkIPoint margin; 282 SkMask srcM, dstM; 283 rrect.rect().roundOut(&srcM.fBounds); 284 srcM.fImage = NULL; 285 srcM.fFormat = SkMask::kA8_Format; 286 srcM.fRowBytes = 0; 287 288 if (!this->filterMask(&dstM, srcM, matrix, &margin)) { 289 return kFalse_FilterReturn; 290 } 291 292 // Now figure out the appropriate width and height of the smaller round rectangle 293 // to stretch. It will take into account the larger radius per side as well as double 294 // the margin, to account for inner and outer blur. 295 const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner); 296 const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner); 297 const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner); 298 const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner); 299 300 const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX); 301 const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX); 302 303 // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover 304 // any fractional space on either side plus 1 for the part to stretch. 305 const SkScalar stretchSize = SkIntToScalar(3); 306 307 const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize; 308 if (totalSmallWidth >= rrect.rect().width()) { 309 // There is no valid piece to stretch. 310 return kUnimplemented_FilterReturn; 311 } 312 313 const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY); 314 const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY); 315 316 const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize; 317 if (totalSmallHeight >= rrect.rect().height()) { 318 // There is no valid piece to stretch. 319 return kUnimplemented_FilterReturn; 320 } 321 322 SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight); 323 324 SkRRect smallRR; 325 SkVector radii[4]; 326 radii[SkRRect::kUpperLeft_Corner] = UL; 327 radii[SkRRect::kUpperRight_Corner] = UR; 328 radii[SkRRect::kLowerRight_Corner] = LR; 329 radii[SkRRect::kLowerLeft_Corner] = LL; 330 smallRR.setRectRadii(smallR, radii); 331 332 if (!draw_rrect_into_mask(smallRR, &srcM)) { 333 return kFalse_FilterReturn; 334 } 335 336 SkAutoMaskFreeImage amf(srcM.fImage); 337 338 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) { 339 return kFalse_FilterReturn; 340 } 341 342 patch->fMask.fBounds.offsetTo(0, 0); 343 patch->fOuterRect = dstM.fBounds; 344 patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1; 345 patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1; 346 return kTrue_FilterReturn; 347 } 348 349 #ifdef SK_IGNORE_FAST_RECT_BLUR 350 SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", false, "Use the faster analytic blur approach for ninepatch rects" ); 351 #else 352 SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", true, "Use the faster analytic blur approach for ninepatch rects" ); 353 #endif 354 355 SkMaskFilter::FilterReturn 356 SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, 357 const SkMatrix& matrix, 358 const SkIRect& clipBounds, 359 NinePatch* patch) const { 360 if (count < 1 || count > 2) { 361 return kUnimplemented_FilterReturn; 362 } 363 364 // TODO: report correct metrics for innerstyle, where we do not grow the 365 // total bounds, but we do need an inset the size of our blur-radius 366 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle || 367 SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) { 368 return kUnimplemented_FilterReturn; 369 } 370 371 // TODO: take clipBounds into account to limit our coordinates up front 372 // for now, just skip too-large src rects (to take the old code path). 373 if (rect_exceeds(rects[0], SkIntToScalar(32767))) { 374 return kUnimplemented_FilterReturn; 375 } 376 377 SkIPoint margin; 378 SkMask srcM, dstM; 379 rects[0].roundOut(&srcM.fBounds); 380 srcM.fImage = NULL; 381 srcM.fFormat = SkMask::kA8_Format; 382 srcM.fRowBytes = 0; 383 384 bool filterResult = false; 385 if (count == 1 && c_analyticBlurNinepatch) { 386 // special case for fast rect blur 387 // don't actually do the blur the first time, just compute the correct size 388 filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin, 389 SkMask::kJustComputeBounds_CreateMode); 390 } else { 391 filterResult = this->filterMask(&dstM, srcM, matrix, &margin); 392 } 393 394 if (!filterResult) { 395 return kFalse_FilterReturn; 396 } 397 398 /* 399 * smallR is the smallest version of 'rect' that will still guarantee that 400 * we get the same blur results on all edges, plus 1 center row/col that is 401 * representative of the extendible/stretchable edges of the ninepatch. 402 * Since our actual edge may be fractional we inset 1 more to be sure we 403 * don't miss any interior blur. 404 * x is an added pixel of blur, and { and } are the (fractional) edge 405 * pixels from the original rect. 406 * 407 * x x { x x .... x x } x x 408 * 409 * Thus, in this case, we inset by a total of 5 (on each side) beginning 410 * with our outer-rect (dstM.fBounds) 411 */ 412 SkRect smallR[2]; 413 SkIPoint center; 414 415 // +2 is from +1 for each edge (to account for possible fractional edges 416 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2; 417 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2; 418 SkIRect innerIR; 419 420 if (1 == count) { 421 innerIR = srcM.fBounds; 422 center.set(smallW, smallH); 423 } else { 424 SkASSERT(2 == count); 425 rects[1].roundIn(&innerIR); 426 center.set(smallW + (innerIR.left() - srcM.fBounds.left()), 427 smallH + (innerIR.top() - srcM.fBounds.top())); 428 } 429 430 // +1 so we get a clean, stretchable, center row/col 431 smallW += 1; 432 smallH += 1; 433 434 // we want the inset amounts to be integral, so we don't change any 435 // fractional phase on the fRight or fBottom of our smallR. 436 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW); 437 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH); 438 if (dx < 0 || dy < 0) { 439 // we're too small, relative to our blur, to break into nine-patch, 440 // so we ask to have our normal filterMask() be called. 441 return kUnimplemented_FilterReturn; 442 } 443 444 smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy); 445 if (smallR[0].width() < 2 || smallR[0].height() < 2) { 446 return kUnimplemented_FilterReturn; 447 } 448 if (2 == count) { 449 smallR[1].set(rects[1].left(), rects[1].top(), 450 rects[1].right() - dx, rects[1].bottom() - dy); 451 SkASSERT(!smallR[1].isEmpty()); 452 } 453 454 if (count > 1 || !c_analyticBlurNinepatch) { 455 if (!draw_rects_into_mask(smallR, count, &srcM)) { 456 return kFalse_FilterReturn; 457 } 458 459 SkAutoMaskFreeImage amf(srcM.fImage); 460 461 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) { 462 return kFalse_FilterReturn; 463 } 464 } else { 465 if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin, 466 SkMask::kComputeBoundsAndRenderImage_CreateMode)) { 467 return kFalse_FilterReturn; 468 } 469 } 470 patch->fMask.fBounds.offsetTo(0, 0); 471 patch->fOuterRect = dstM.fBounds; 472 patch->fCenter = center; 473 return kTrue_FilterReturn; 474 } 475 476 void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, 477 SkRect* dst) const { 478 SkScalar pad = 3.0f * fSigma; 479 480 dst->set(src.fLeft - pad, src.fTop - pad, 481 src.fRight + pad, src.fBottom + pad); 482 } 483 484 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer) 485 : SkMaskFilter(buffer) { 486 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO 487 // TODO: when the skps are recaptured at > v15 the SkScalarAbs can be removed 488 #endif 489 fSigma = SkScalarAbs(buffer.readScalar()); 490 fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt(); 491 fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag; 492 SkASSERT(fSigma >= 0); 493 SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount); 494 } 495 496 void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const { 497 this->INHERITED::flatten(buffer); 498 buffer.writeScalar(fSigma); 499 buffer.writeInt(fBlurStyle); 500 buffer.writeUInt(fBlurFlags); 501 } 502 503 #if SK_SUPPORT_GPU 504 505 bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds, 506 const SkIRect& clipBounds, 507 const SkMatrix& ctm, 508 SkRect* maskRect) const { 509 SkScalar xformedSigma = this->computeXformedSigma(ctm); 510 if (xformedSigma <= 0) { 511 return false; 512 } 513 514 static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64); 515 static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32); 516 517 if (srcBounds.width() <= kMIN_GPU_BLUR_SIZE && 518 srcBounds.height() <= kMIN_GPU_BLUR_SIZE && 519 xformedSigma <= kMIN_GPU_BLUR_SIGMA) { 520 // We prefer to blur small rect with small radius via CPU. 521 return false; 522 } 523 524 if (NULL == maskRect) { 525 // don't need to compute maskRect 526 return true; 527 } 528 529 float sigma3 = 3 * SkScalarToFloat(xformedSigma); 530 531 SkRect clipRect = SkRect::Make(clipBounds); 532 SkRect srcRect(srcBounds); 533 534 // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area. 535 srcRect.outset(sigma3, sigma3); 536 clipRect.outset(sigma3, sigma3); 537 srcRect.intersect(clipRect); 538 *maskRect = srcRect; 539 return true; 540 } 541 542 bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src, 543 const SkRect& maskRect, 544 GrTexture** result, 545 bool canOverwriteSrc) const { 546 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height()); 547 548 GrContext* context = src->getContext(); 549 550 GrContext::AutoWideOpenIdentityDraw awo(context, NULL); 551 552 SkScalar xformedSigma = this->computeXformedSigma(context->getMatrix()); 553 SkASSERT(xformedSigma > 0); 554 555 // If we're doing a normal blur, we can clobber the pathTexture in the 556 // gaussianBlur. Otherwise, we need to save it for later compositing. 557 bool isNormalBlur = (SkBlurMaskFilter::kNormal_BlurStyle == fBlurStyle); 558 *result = SkGpuBlurUtils::GaussianBlur(context, src, isNormalBlur && canOverwriteSrc, 559 clipRect, false, xformedSigma, xformedSigma); 560 if (NULL == *result) { 561 return false; 562 } 563 564 if (!isNormalBlur) { 565 context->setIdentityMatrix(); 566 GrPaint paint; 567 SkMatrix matrix; 568 matrix.setIDiv(src->width(), src->height()); 569 // Blend pathTexture over blurTexture. 570 GrContext::AutoRenderTarget art(context, (*result)->asRenderTarget()); 571 paint.addColorEffect(GrSimpleTextureEffect::Create(src, matrix))->unref(); 572 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) { 573 // inner: dst = dst * src 574 paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff); 575 } else if (SkBlurMaskFilter::kSolid_BlurStyle == fBlurStyle) { 576 // solid: dst = src + dst - src * dst 577 // = (1 - dst) * src + 1 * dst 578 paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff); 579 } else if (SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) { 580 // outer: dst = dst * (1 - src) 581 // = 0 * src + (1 - src) * dst 582 paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff); 583 } 584 context->drawRect(paint, clipRect); 585 } 586 587 return true; 588 } 589 590 #endif // SK_SUPPORT_GPU 591 592 593 #ifdef SK_DEVELOPER 594 void SkBlurMaskFilterImpl::toString(SkString* str) const { 595 str->append("SkBlurMaskFilterImpl: ("); 596 597 str->append("sigma: "); 598 str->appendScalar(fSigma); 599 str->append(" "); 600 601 static const char* gStyleName[SkBlurMaskFilter::kBlurStyleCount] = { 602 "normal", "solid", "outer", "inner" 603 }; 604 605 str->appendf("style: %s ", gStyleName[fBlurStyle]); 606 str->append("flags: ("); 607 if (fBlurFlags) { 608 bool needSeparator = false; 609 SkAddFlagToString(str, 610 SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag), 611 "IgnoreXform", &needSeparator); 612 SkAddFlagToString(str, 613 SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag), 614 "HighQuality", &needSeparator); 615 } else { 616 str->append("None"); 617 } 618 str->append("))"); 619 } 620 #endif 621 622 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter) 623 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl) 624 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END 625