Plumb fast rectangle blur code into the skia mask filter

Review URL: https://codereview.chromium.org/12387099

git-svn-id: http://skia.googlecode.com/svn/trunk@8074 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
humper@google.com 2013-03-11 20:16:28 +00:00
parent ed268bfed3
commit 7c5d7b7813
3 changed files with 100 additions and 29 deletions

View File

@ -1185,23 +1185,28 @@ static float gaussianIntegral(float x) {
return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x);
}
/*
compute_profile allocates and fills in an array of floating
// Compute the size of the array allocated for the profile.
static int compute_profile_size(SkScalar radius) {
return SkScalarRoundToInt(radius * 3);
}
/* compute_profile allocates and fills in an array of floating
point values between 0 and 255 for the profile signature of
a blurred half-plane with the given blur radius. Since we're
going to be doing screened multiplications (i.e., 1 - (1-x)(1-y))
all the time, we actually fill in the profile pre-inverted
(already done 255-x).
The function returns the size of the array allocated for the
profile. It's the responsibility of the caller to delete the
It's the responsibility of the caller to delete the
memory returned in profile_out.
*/
static int compute_profile(SkScalar radius, unsigned int **profile_out) {
int size = SkScalarRoundToInt(radius * 3);
static void compute_profile(SkScalar radius, unsigned int **profile_out) {
int size = compute_profile_size(radius);
int center = size >> 1;
unsigned int *profile = SkNEW_ARRAY(unsigned int, size);
float invr = 1.f/radius;
@ -1214,7 +1219,6 @@ static int compute_profile(SkScalar radius, unsigned int **profile_out) {
}
*profile_out = profile;
return size;
}
// TODO MAYBE: Maintain a profile cache to avoid recomputing this for
@ -1236,19 +1240,16 @@ static inline unsigned int profile_lookup( unsigned int *profile, int loc, int b
bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
SkScalar provided_radius, Style style,
SkIPoint *margin) {
SkIPoint *margin, SkMask::CreateMode createMode) {
int profile_size;
unsigned int *profile;
float radius = SkScalarToFloat( SkScalarMul( provided_radius, kBlurRadiusFudgeFactor ) );
float radius = SkScalarToFloat(SkScalarMul(provided_radius, kBlurRadiusFudgeFactor));
// adjust blur radius to match interpretation from boxfilter code
radius = (radius + .5f) *2.f;
profile_size = compute_profile( radius, &profile );
SkAutoTDeleteArray<unsigned int> ada(profile);
radius = (radius + .5f) * 2.f;
profile_size = compute_profile_size(radius);
int pad = profile_size/2;
if (margin) {
margin->set( pad, pad );
@ -1259,20 +1260,35 @@ bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
int shadow_right = (int)(src.width()) + pad;
int shadow_bottom = (int)(src.height()) + pad;
dst->fBounds.set(shadow_left, shadow_top, shadow_right, shadow_bottom);
dst->fBounds.set(shadow_left + src.fLeft,
shadow_top + src.fTop,
shadow_right + src.fRight,
shadow_bottom + src.fBottom);
dst->fRowBytes = dst->fBounds.width();
dst->fFormat = SkMask::kA8_Format;
dst->fImage = NULL;
int sw = SkScalarFloorToInt(src.width());
int sh = SkScalarFloorToInt(src.height());
if (createMode == SkMask::kJustComputeBounds_CreateMode) {
if (style == kInner_Style) {
dst->fBounds.set(0, 0, sw, sh); // restore trimmed bounds
dst->fRowBytes = sw;
}
return true;
}
unsigned int *profile = NULL;
compute_profile(radius, &profile);
SkAutoTDeleteArray<unsigned int> ada(profile);
size_t dstSize = dst->computeImageSize();
if (0 == dstSize) {
return false; // too big to allocate, abort
}
int sw = SkScalarFloorToInt(src.width());
int sh = SkScalarFloorToInt(src.height());
uint8_t* dp = SkMask::AllocImage(dstSize);
dst->fImage = dp;

View File

@ -11,6 +11,7 @@
#define SkBlurMask_DEFINED
#include "SkShader.h"
#include "SkMask.h"
class SkBlurMask {
public:
@ -30,7 +31,8 @@ public:
static bool BlurRect(SkMask *dst, const SkRect &src,
SkScalar radius, Style style,
SkIPoint *margin = NULL);
SkIPoint *margin = NULL,
SkMask::CreateMode createMode=SkMask::kComputeBoundsAndRenderImage_CreateMode);
static bool Blur(SkMask* dst, const SkMask& src,
SkScalar radius, Style style, Quality quality,
SkIPoint* margin = NULL);

View File

@ -10,6 +10,9 @@
#include "SkBlurMask.h"
#include "SkFlattenableBuffers.h"
#include "SkMaskFilter.h"
#include "SkBounder.h"
#include "SkRasterClip.h"
#include "SkRTConf.h"
class SkBlurMaskFilterImpl : public SkMaskFilter {
public:
@ -20,6 +23,7 @@ public:
virtual SkMask::Format getFormat() const SK_OVERRIDE;
virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
SkIPoint* margin) const SK_OVERRIDE;
virtual BlurType asABlur(BlurInfo*) const SK_OVERRIDE;
virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
@ -29,6 +33,9 @@ protected:
virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
const SkIRect& clipBounds,
NinePatch*) const SK_OVERRIDE;
bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
SkIPoint* margin, SkMask::CreateMode createMode) const;
private:
SkScalar fRadius;
@ -106,6 +113,26 @@ bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
#endif
}
bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
const SkMatrix& matrix,
SkIPoint* margin, SkMask::CreateMode createMode) const{
SkScalar radius;
if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
radius = fRadius;
} else {
radius = matrix.mapRadius(fRadius);
}
// 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 MAX_RADIUS = SkIntToScalar(128);
radius = SkMinScalar(radius, MAX_RADIUS);
return SkBlurMask::BlurRect(dst, r, radius, (SkBlurMask::Style)fBlurStyle,
margin, createMode);
}
#include "SkCanvas.h"
static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) {
@ -150,6 +177,14 @@ static bool rect_exceeds(const SkRect& r, SkScalar v) {
r.width() > v || r.height() > v;
}
SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch",
#ifdef SK_IGNORE_FAST_RECT_BLUR
false,
#else
true,
#endif
"Use the faster analytic blur approach for ninepatch rects" );
SkMaskFilter::FilterReturn
SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
const SkMatrix& matrix,
@ -177,7 +212,18 @@ SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
srcM.fImage = NULL;
srcM.fFormat = SkMask::kA8_Format;
srcM.fRowBytes = 0;
if (!this->filterMask(&dstM, srcM, matrix, &margin)) {
bool filterResult = false;
if (count == 1 && c_analyticBlurNinepatch) {
// special case for fast rect blur
// don't actually do the blur the first time, just compute the correct size
filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
SkMask::kJustComputeBounds_CreateMode);
} else {
filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
}
if (!filterResult) {
return kFalse_FilterReturn;
}
@ -235,14 +281,21 @@ SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
SkASSERT(!smallR[1].isEmpty());
}
if (!drawRectsIntoMask(smallR, count, &srcM)) {
return kFalse_FilterReturn;
}
if (count > 1 || !c_analyticBlurNinepatch) {
if (!drawRectsIntoMask(smallR, count, &srcM)) {
return kFalse_FilterReturn;
}
SkAutoMaskFreeImage amf(srcM.fImage);
SkAutoMaskFreeImage amf(srcM.fImage);
if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
return kFalse_FilterReturn;
if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
return kFalse_FilterReturn;
}
} else {
if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
return kFalse_FilterReturn;
}
}
patch->fMask.fBounds.offsetTo(0, 0);
patch->fOuterRect = dstM.fBounds;