SkBlurMaskFilter.cpp revision 96ac2f693b3a4b7bb504ec2a13b15eeeaa5ff1fd
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 "SkRTConf.h" 15#include "SkStringUtils.h" 16#include "SkStrokeRec.h" 17 18#if SK_SUPPORT_GPU 19#include "GrContext.h" 20#include "GrTexture.h" 21#include "effects/GrSimpleTextureEffect.h" 22#include "SkGrPixelRef.h" 23#endif 24 25class SkBlurMaskFilterImpl : public SkMaskFilter { 26public: 27 SkBlurMaskFilterImpl(SkScalar sigma, SkBlurMaskFilter::BlurStyle, uint32_t flags); 28 29 // overrides from SkMaskFilter 30 virtual SkMask::Format getFormat() const SK_OVERRIDE; 31 virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, 32 SkIPoint* margin) const SK_OVERRIDE; 33 34#if SK_SUPPORT_GPU 35 virtual bool canFilterMaskGPU(const SkRect& devBounds, 36 const SkIRect& clipBounds, 37 const SkMatrix& ctm, 38 SkRect* maskRect) const SK_OVERRIDE; 39 virtual bool filterMaskGPU(GrTexture* src, 40 const SkRect& maskRect, 41 GrTexture** result, 42 bool canOverwriteSrc) const; 43#endif 44 45 virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE; 46 47 SkDEVCODE(virtual void toString(SkString* str) const SK_OVERRIDE;) 48 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl) 49 50protected: 51 virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&, 52 const SkIRect& clipBounds, 53 NinePatch*) const SK_OVERRIDE; 54 55 bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix, 56 SkIPoint* margin, SkMask::CreateMode createMode) const; 57 58private: 59 // To avoid unseemly allocation requests (esp. for finite platforms like 60 // handset) we limit the radius so something manageable. (as opposed to 61 // a request like 10,000) 62 static const SkScalar kMAX_BLUR_SIGMA; 63 64 SkScalar fSigma; 65 SkBlurMaskFilter::BlurStyle fBlurStyle; 66 uint32_t fBlurFlags; 67 68 SkBlurMaskFilterImpl(SkFlattenableReadBuffer&); 69 virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE; 70 71 SkScalar computeXformedSigma(const SkMatrix& ctm) const { 72 bool ignoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag); 73 74 SkScalar xformedSigma = ignoreTransform ? fSigma : ctm.mapRadius(fSigma); 75 return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA); 76 } 77 78 typedef SkMaskFilter INHERITED; 79}; 80 81const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128); 82 83SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius, 84 SkBlurMaskFilter::BlurStyle style, 85 uint32_t flags) { 86 // use !(radius > 0) instead of radius <= 0 to reject NaN values 87 if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount 88 || flags > SkBlurMaskFilter::kAll_BlurFlag) { 89 return NULL; 90 } 91 92 SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); 93 94 return SkNEW_ARGS(SkBlurMaskFilterImpl, (sigma, style, flags)); 95} 96 97SkMaskFilter* SkBlurMaskFilter::Create(SkBlurMaskFilter::BlurStyle style, 98 SkScalar sigma, 99 uint32_t flags) { 100 // use !(sigma > 0) instead of sigma <= 0 to reject NaN values 101 if (!(sigma > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount 102 || flags > SkBlurMaskFilter::kAll_BlurFlag) { 103 return NULL; 104 } 105 106 return SkNEW_ARGS(SkBlurMaskFilterImpl, (sigma, style, flags)); 107} 108 109/////////////////////////////////////////////////////////////////////////////// 110 111SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, 112 SkBlurMaskFilter::BlurStyle style, 113 uint32_t flags) 114 : fSigma(sigma), fBlurStyle(style), fBlurFlags(flags) { 115#if 0 116 fGamma = NULL; 117 if (gammaScale) { 118 fGamma = new U8[256]; 119 if (gammaScale > 0) 120 SkBlurMask::BuildSqrGamma(fGamma, gammaScale); 121 else 122 SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale); 123 } 124#endif 125 SkASSERT(fSigma >= 0); 126 SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount); 127 SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag); 128} 129 130SkMask::Format SkBlurMaskFilterImpl::getFormat() const { 131 return SkMask::kA8_Format; 132} 133 134bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, 135 const SkMatrix& matrix, 136 SkIPoint* margin) const{ 137 SkScalar sigma = this->computeXformedSigma(matrix); 138 139 SkBlurMask::Quality blurQuality = 140 (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? 141 SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality; 142 143 return SkBlurMask::BoxBlur(dst, src, sigma, (SkBlurMask::Style)fBlurStyle, 144 blurQuality, margin); 145} 146 147bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r, 148 const SkMatrix& matrix, 149 SkIPoint* margin, SkMask::CreateMode createMode) const{ 150 SkScalar sigma = computeXformedSigma(matrix); 151 152 return SkBlurMask::BlurRect(sigma, dst, r, (SkBlurMask::Style)fBlurStyle, 153 margin, createMode); 154} 155 156#include "SkCanvas.h" 157 158static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) { 159 rects[0].roundOut(&mask->fBounds); 160 mask->fRowBytes = SkAlign4(mask->fBounds.width()); 161 mask->fFormat = SkMask::kA8_Format; 162 size_t size = mask->computeImageSize(); 163 mask->fImage = SkMask::AllocImage(size); 164 if (NULL == mask->fImage) { 165 return false; 166 } 167 sk_bzero(mask->fImage, size); 168 169 SkBitmap bitmap; 170 bitmap.setConfig(SkBitmap::kA8_Config, 171 mask->fBounds.width(), mask->fBounds.height(), 172 mask->fRowBytes); 173 bitmap.setPixels(mask->fImage); 174 175 SkCanvas canvas(bitmap); 176 canvas.translate(-SkIntToScalar(mask->fBounds.left()), 177 -SkIntToScalar(mask->fBounds.top())); 178 179 SkPaint paint; 180 paint.setAntiAlias(true); 181 182 if (1 == count) { 183 canvas.drawRect(rects[0], paint); 184 } else { 185 // todo: do I need a fast way to do this? 186 SkPath path; 187 path.addRect(rects[0]); 188 path.addRect(rects[1]); 189 path.setFillType(SkPath::kEvenOdd_FillType); 190 canvas.drawPath(path, paint); 191 } 192 return true; 193} 194 195static bool rect_exceeds(const SkRect& r, SkScalar v) { 196 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v || 197 r.width() > v || r.height() > v; 198} 199 200#ifdef SK_IGNORE_FAST_RECT_BLUR 201SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", false, "Use the faster analytic blur approach for ninepatch rects" ); 202#else 203SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", true, "Use the faster analytic blur approach for ninepatch rects" ); 204#endif 205 206SkMaskFilter::FilterReturn 207SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, 208 const SkMatrix& matrix, 209 const SkIRect& clipBounds, 210 NinePatch* patch) const { 211 if (count < 1 || count > 2) { 212 return kUnimplemented_FilterReturn; 213 } 214 215 // TODO: report correct metrics for innerstyle, where we do not grow the 216 // total bounds, but we do need an inset the size of our blur-radius 217 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle || 218 SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) { 219 return kUnimplemented_FilterReturn; 220 } 221 222 // TODO: take clipBounds into account to limit our coordinates up front 223 // for now, just skip too-large src rects (to take the old code path). 224 if (rect_exceeds(rects[0], SkIntToScalar(32767))) { 225 return kUnimplemented_FilterReturn; 226 } 227 228 SkIPoint margin; 229 SkMask srcM, dstM; 230 rects[0].roundOut(&srcM.fBounds); 231 srcM.fImage = NULL; 232 srcM.fFormat = SkMask::kA8_Format; 233 srcM.fRowBytes = 0; 234 235 bool filterResult = false; 236 if (count == 1 && c_analyticBlurNinepatch) { 237 // special case for fast rect blur 238 // don't actually do the blur the first time, just compute the correct size 239 filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin, 240 SkMask::kJustComputeBounds_CreateMode); 241 } else { 242 filterResult = this->filterMask(&dstM, srcM, matrix, &margin); 243 } 244 245 if (!filterResult) { 246 return kFalse_FilterReturn; 247 } 248 249 /* 250 * smallR is the smallest version of 'rect' that will still guarantee that 251 * we get the same blur results on all edges, plus 1 center row/col that is 252 * representative of the extendible/stretchable edges of the ninepatch. 253 * Since our actual edge may be fractional we inset 1 more to be sure we 254 * don't miss any interior blur. 255 * x is an added pixel of blur, and { and } are the (fractional) edge 256 * pixels from the original rect. 257 * 258 * x x { x x .... x x } x x 259 * 260 * Thus, in this case, we inset by a total of 5 (on each side) beginning 261 * with our outer-rect (dstM.fBounds) 262 */ 263 SkRect smallR[2]; 264 SkIPoint center; 265 266 // +2 is from +1 for each edge (to account for possible fractional edges 267 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2; 268 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2; 269 SkIRect innerIR; 270 271 if (1 == count) { 272 innerIR = srcM.fBounds; 273 center.set(smallW, smallH); 274 } else { 275 SkASSERT(2 == count); 276 rects[1].roundIn(&innerIR); 277 center.set(smallW + (innerIR.left() - srcM.fBounds.left()), 278 smallH + (innerIR.top() - srcM.fBounds.top())); 279 } 280 281 // +1 so we get a clean, stretchable, center row/col 282 smallW += 1; 283 smallH += 1; 284 285 // we want the inset amounts to be integral, so we don't change any 286 // fractional phase on the fRight or fBottom of our smallR. 287 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW); 288 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH); 289 if (dx < 0 || dy < 0) { 290 // we're too small, relative to our blur, to break into nine-patch, 291 // so we ask to have our normal filterMask() be called. 292 return kUnimplemented_FilterReturn; 293 } 294 295 smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy); 296 if (smallR[0].width() < 2 || smallR[0].height() < 2) { 297 return kUnimplemented_FilterReturn; 298 } 299 if (2 == count) { 300 smallR[1].set(rects[1].left(), rects[1].top(), 301 rects[1].right() - dx, rects[1].bottom() - dy); 302 SkASSERT(!smallR[1].isEmpty()); 303 } 304 305 if (count > 1 || !c_analyticBlurNinepatch) { 306 if (!drawRectsIntoMask(smallR, count, &srcM)) { 307 return kFalse_FilterReturn; 308 } 309 310 SkAutoMaskFreeImage amf(srcM.fImage); 311 312 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) { 313 return kFalse_FilterReturn; 314 } 315 } else { 316 if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin, 317 SkMask::kComputeBoundsAndRenderImage_CreateMode)) { 318 return kFalse_FilterReturn; 319 } 320 } 321 patch->fMask.fBounds.offsetTo(0, 0); 322 patch->fOuterRect = dstM.fBounds; 323 patch->fCenter = center; 324 return kTrue_FilterReturn; 325} 326 327void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, 328 SkRect* dst) const { 329 SkScalar pad = 3.0f * fSigma; 330 331 dst->set(src.fLeft - pad, src.fTop - pad, 332 src.fRight + pad, src.fBottom + pad); 333} 334 335SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer) 336 : SkMaskFilter(buffer) { 337 fSigma = buffer.readScalar(); 338#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V13_AND_ALL_OTHER_INSTANCES_TOO 339 // Fixing this must be done in two stages. When the skps are recaptured in V13, 340 // remove the ConvertRadiusToSigma but retain the absolute value. 341 // At the same time, switch the code in flatten to write a positive value. 342 // When the skps are captured in V14 the absolute value can be removed. 343 if (fSigma > 0) { 344 fSigma = SkBlurMask::ConvertRadiusToSigma(fSigma); 345 } else { 346 fSigma = -fSigma; 347 } 348#endif 349 fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt(); 350 fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag; 351 SkASSERT(fSigma >= 0); 352 SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount); 353} 354 355void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const { 356 this->INHERITED::flatten(buffer); 357 buffer.writeScalar(-fSigma); 358 buffer.writeInt(fBlurStyle); 359 buffer.writeUInt(fBlurFlags); 360} 361 362#if SK_SUPPORT_GPU 363 364bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds, 365 const SkIRect& clipBounds, 366 const SkMatrix& ctm, 367 SkRect* maskRect) const { 368 SkScalar xformedSigma = this->computeXformedSigma(ctm); 369 if (xformedSigma <= 0) { 370 return false; 371 } 372 373 static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64); 374 static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32); 375 376 if (srcBounds.width() <= kMIN_GPU_BLUR_SIZE && 377 srcBounds.height() <= kMIN_GPU_BLUR_SIZE && 378 xformedSigma <= kMIN_GPU_BLUR_SIGMA) { 379 // We prefer to blur small rect with small radius via CPU. 380 return false; 381 } 382 383 if (NULL == maskRect) { 384 // don't need to compute maskRect 385 return true; 386 } 387 388 float sigma3 = 3 * SkScalarToFloat(xformedSigma); 389 390 SkRect clipRect = SkRect::Make(clipBounds); 391 SkRect srcRect(srcBounds); 392 393 // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area. 394 srcRect.outset(SkFloatToScalar(sigma3), SkFloatToScalar(sigma3)); 395 clipRect.outset(SkFloatToScalar(sigma3), SkFloatToScalar(sigma3)); 396 srcRect.intersect(clipRect); 397 *maskRect = srcRect; 398 return true; 399} 400 401bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src, 402 const SkRect& maskRect, 403 GrTexture** result, 404 bool canOverwriteSrc) const { 405 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height()); 406 407 GrContext* context = src->getContext(); 408 409 GrContext::AutoWideOpenIdentityDraw awo(context, NULL); 410 411 SkScalar xformedSigma = this->computeXformedSigma(context->getMatrix()); 412 SkASSERT(xformedSigma > 0); 413 414 // If we're doing a normal blur, we can clobber the pathTexture in the 415 // gaussianBlur. Otherwise, we need to save it for later compositing. 416 bool isNormalBlur = (SkBlurMaskFilter::kNormal_BlurStyle == fBlurStyle); 417 *result = SkGpuBlurUtils::GaussianBlur(context, src, isNormalBlur && canOverwriteSrc, 418 clipRect, false, xformedSigma, xformedSigma); 419 if (NULL == *result) { 420 return false; 421 } 422 423 if (!isNormalBlur) { 424 context->setIdentityMatrix(); 425 GrPaint paint; 426 SkMatrix matrix; 427 matrix.setIDiv(src->width(), src->height()); 428 // Blend pathTexture over blurTexture. 429 GrContext::AutoRenderTarget art(context, (*result)->asRenderTarget()); 430 paint.addColorEffect(GrSimpleTextureEffect::Create(src, matrix))->unref(); 431 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) { 432 // inner: dst = dst * src 433 paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff); 434 } else if (SkBlurMaskFilter::kSolid_BlurStyle == fBlurStyle) { 435 // solid: dst = src + dst - src * dst 436 // = (1 - dst) * src + 1 * dst 437 paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff); 438 } else if (SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) { 439 // outer: dst = dst * (1 - src) 440 // = 0 * src + (1 - src) * dst 441 paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff); 442 } 443 context->drawRect(paint, clipRect); 444 } 445 446 return true; 447} 448 449#endif // SK_SUPPORT_GPU 450 451 452#ifdef SK_DEVELOPER 453void SkBlurMaskFilterImpl::toString(SkString* str) const { 454 str->append("SkBlurMaskFilterImpl: ("); 455 456 str->append("sigma: "); 457 str->appendScalar(fSigma); 458 str->append(" "); 459 460 static const char* gStyleName[SkBlurMaskFilter::kBlurStyleCount] = { 461 "normal", "solid", "outer", "inner" 462 }; 463 464 str->appendf("style: %s ", gStyleName[fBlurStyle]); 465 str->append("flags: ("); 466 if (fBlurFlags) { 467 bool needSeparator = false; 468 SkAddFlagToString(str, 469 SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag), 470 "IgnoreXform", &needSeparator); 471 SkAddFlagToString(str, 472 SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag), 473 "HighQuality", &needSeparator); 474 } else { 475 str->append("None"); 476 } 477 str->append("))"); 478} 479#endif 480 481SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter) 482 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl) 483SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END 484