From 17ad2bd077af50dcaca18cf00607b194a1491a44 Mon Sep 17 00:00:00 2001 From: "robertphillips@google.com" Date: Tue, 30 Jul 2013 12:15:19 +0000 Subject: [PATCH] Fix quickReject computation for blurs https://codereview.chromium.org/17035007/ git-svn-id: http://skia.googlecode.com/svn/trunk@10428 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gm/blurquickreject.cpp | 82 ++++++++++++++++++++++++++++++++++++++++ gyp/gmslides.gypi | 1 + src/effects/SkBlurMask.cpp | 8 +--- src/effects/SkBlurMask.h | 7 ++++ src/effects/SkBlurMaskFilter.cpp | 35 +++++++++++++---- 5 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 gm/blurquickreject.cpp diff --git a/gm/blurquickreject.cpp b/gm/blurquickreject.cpp new file mode 100644 index 0000000..b1406f3 --- /dev/null +++ b/gm/blurquickreject.cpp @@ -0,0 +1,82 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" +#include "SkCanvas.h" +#include "SkBlurMaskFilter.h" + +// This GM tests out the quick reject bounds of the blur mask filter. It draws +// four blurred rects around a central clip. The blurred rect geometry outset +// by the blur radius does not overlap the clip rect so, if the blur clipping +// just uses the radius, they will be clipped out (and the result will differ +// from the result if quick reject were disabled. If the blur clipping uses +// the correct 3 sigma bound then the images with and without quick rejecting +// will be the same. +class BlurQuickRejectGM : public skiagm::GM { +public: + BlurQuickRejectGM() {} + +protected: + virtual SkString onShortName() SK_OVERRIDE { + return SkString("blurquickreject"); + } + + virtual SkISize onISize() SK_OVERRIDE { + return SkISize::Make(kWidth, kHeight); + } + + virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { + static const SkScalar kBlurRadius = SkIntToScalar(20); + static const SkScalar kBoxSize = SkIntToScalar(100); + + SkRect clipRect = SkRect::MakeXYWH(0, 0, kBoxSize, kBoxSize); + SkRect blurRects[] = { + { -kBoxSize - (kBlurRadius+1), 0, -(kBlurRadius+1), kBoxSize }, + { 0, -kBoxSize - (kBlurRadius+1), kBoxSize, -(kBlurRadius+1) }, + { kBoxSize+kBlurRadius+1, 0, 2*kBoxSize+kBlurRadius+1, kBoxSize }, + { 0, kBoxSize+kBlurRadius+1, kBoxSize, 2*kBoxSize+kBlurRadius+1 } + }; + SkColor colors[] = { + SK_ColorRED, + SK_ColorGREEN, + SK_ColorBLUE, + SK_ColorYELLOW, + }; + SkASSERT(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(blurRects)); + + SkPaint hairlinePaint; + hairlinePaint.setStyle(SkPaint::kStroke_Style); + hairlinePaint.setColor(SK_ColorWHITE); + hairlinePaint.setStrokeWidth(0); + + SkPaint blurPaint; + blurPaint.setFilterBitmap(true); + SkMaskFilter* mf = SkBlurMaskFilter::Create(kBlurRadius, + SkBlurMaskFilter::kNormal_BlurStyle); + blurPaint.setMaskFilter(mf)->unref(); + + canvas->clear(SK_ColorBLACK); + canvas->save(); + canvas->translate(kBoxSize, kBoxSize); + canvas->drawRect(clipRect, hairlinePaint); + canvas->clipRect(clipRect); + for (int i = 0; i < SK_ARRAY_COUNT(blurRects); ++i) { + blurPaint.setColor(colors[i]); + canvas->drawRect(blurRects[i], blurPaint); + canvas->drawRect(blurRects[i], hairlinePaint); + } + canvas->restore(); + } + +private: + static const int kWidth = 300; + static const int kHeight = 300; + + typedef GM INHERITED; +}; + +DEF_GM( return new BlurQuickRejectGM(); ) diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index 275568a..3af7d73 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -18,6 +18,7 @@ '../gm/bleed.cpp', '../gm/blend.cpp', '../gm/blurs.cpp', + '../gm/blurquickreject.cpp', '../gm/blurrect.cpp', '../gm/circles.cpp', '../gm/colorfilterimagefilter.cpp', diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp index 0089bab..c946c5e 100644 --- a/src/effects/SkBlurMask.cpp +++ b/src/effects/SkBlurMask.cpp @@ -12,13 +12,7 @@ #include "SkTemplates.h" #include "SkEndian.h" -// scale factor for the blur radius to match the behavior of the all existing blur -// code (both on the CPU and the GPU). This magic constant is 1/sqrt(3). - -// TODO: get rid of this fudge factor and move any required fudging up into -// the calling library - -#define kBlurRadiusFudgeFactor SkFloatToScalar( .57735f ) +const SkScalar SkBlurMask::kBlurRadiusFudgeFactor = SkFloatToScalar(.57735f); #define UNROLL_SEPARABLE_LOOPS diff --git a/src/effects/SkBlurMask.h b/src/effects/SkBlurMask.h index 9c625ad..36d7800 100644 --- a/src/effects/SkBlurMask.h +++ b/src/effects/SkBlurMask.h @@ -43,6 +43,13 @@ public: static bool BlurGroundTruth(SkMask* dst, const SkMask& src, SkScalar provided_radius, Style style, SkIPoint* margin = NULL); + + // scale factor for the blur radius to match the behavior of the all existing blur + // code (both on the CPU and the GPU). This magic constant is 1/sqrt(3). + // TODO: get rid of this fudge factor and move any required fudging up into + // the calling library + static const SkScalar kBlurRadiusFudgeFactor; + }; #endif diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp index f1fee06..bb7aa4e 100644 --- a/src/effects/SkBlurMaskFilter.cpp +++ b/src/effects/SkBlurMaskFilter.cpp @@ -60,7 +60,7 @@ private: // To avoid unseemly allocation requests (esp. for finite platforms like // handset) we limit the radius so something manageable. (as opposed to // a request like 10,000) - static const SkScalar kMAX_RADIUS; + static const SkScalar kMAX_BLUR_RADIUS; // This constant approximates the scaling done in the software path's // "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)). // IMHO, it actually should be 1: we blur "less" than we should do @@ -82,15 +82,15 @@ private: SkScalar xformedRadius = ignoreTransform ? fRadius : ctm.mapRadius(fRadius); - return SkMinScalar(xformedRadius, kMAX_RADIUS); + return SkMinScalar(xformedRadius, kMAX_BLUR_RADIUS); } #endif typedef SkMaskFilter INHERITED; }; -const SkScalar SkBlurMaskFilterImpl::kMAX_RADIUS = SkIntToScalar(128); -const SkScalar SkBlurMaskFilterImpl::kBLUR_SIGMA_SCALE = 0.6f; +const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_RADIUS = SkIntToScalar(128); +const SkScalar SkBlurMaskFilterImpl::kBLUR_SIGMA_SCALE = SkFloatToScalar(0.6f); SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius, SkBlurMaskFilter::BlurStyle style, @@ -139,7 +139,7 @@ bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, radius = matrix.mapRadius(fRadius); } - radius = SkMinScalar(radius, kMAX_RADIUS); + radius = SkMinScalar(radius, kMAX_BLUR_RADIUS); SkBlurMask::Quality blurQuality = (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality; @@ -158,7 +158,7 @@ bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r, radius = matrix.mapRadius(fRadius); } - radius = SkMinScalar(radius, kMAX_RADIUS); + radius = SkMinScalar(radius, kMAX_BLUR_RADIUS); return SkBlurMask::BlurRect(dst, r, radius, (SkBlurMask::Style)fBlurStyle, margin, createMode); @@ -334,8 +334,27 @@ SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) const { - dst->set(src.fLeft - fRadius, src.fTop - fRadius, - src.fRight + fRadius, src.fBottom + fRadius); + SkScalar gpuPad, rasterPad; + + { + // GPU path + SkScalar sigma = SkScalarMul(fRadius, kBLUR_SIGMA_SCALE); + gpuPad = sigma * 3.0f; + } + + { + // raster path + SkScalar radius = SkScalarMul(fRadius, SkBlurMask::kBlurRadiusFudgeFactor); + + radius = (radius + .5f) * 2.f; + + rasterPad = SkIntToScalar(SkScalarRoundToInt(radius * 3)/2); + } + + SkScalar pad = SkMaxScalar(gpuPad, rasterPad); + + dst->set(src.fLeft - pad, src.fTop - pad, + src.fRight + pad, src.fBottom + pad); } SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer) -- 2.7.4