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