Complete the implementation of the faster blur; now supports all blur styles and matches the boxfilter approximation visually. Also change the interpretation of the blur radius to be sigma/2; need to add SK_IGNORE_BLUR_RADIUS_CORRECTNESS to chromium GYP to avoid immediate layout test failures over there.
Review URL: https://codereview.appspot.com/7307076 git-svn-id: http://skia.googlecode.com/svn/trunk@7793 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
f33612b923
commit
a99a92ceba
@ -74,13 +74,13 @@ private:
|
||||
|
||||
class BlurRectDirectBench: public BlurRectBench {
|
||||
public:
|
||||
BlurRectDirectBench(void *param, SkScalar rad) : BlurRectBench(param, rad) {
|
||||
BlurRectDirectBench(void *param, SkScalar rad) : INHERITED(param, rad) {
|
||||
SkString name;
|
||||
|
||||
if (SkScalarFraction(rad) != 0) {
|
||||
name.printf("blurrect_direct_%.2f", SkScalarToFloat(rad));
|
||||
} else {
|
||||
name.printf("blurrect_direct_%d", SkScalarRound(rad));
|
||||
name.printf("blurrect_direct_%d", SkScalarRoundToInt(rad));
|
||||
}
|
||||
|
||||
setName(name);
|
||||
@ -88,23 +88,17 @@ class BlurRectDirectBench: public BlurRectBench {
|
||||
protected:
|
||||
virtual void makeBlurryRect(const SkRect& r) SK_OVERRIDE {
|
||||
SkMask mask;
|
||||
SkBlurMask::BlurRect(&mask, r, radius(), SkBlurMask::kNormal_Style,
|
||||
SkBlurMask::kHigh_Quality);
|
||||
SkBlurMask::BlurRect(&mask, r, this->radius(), SkBlurMask::kNormal_Style);
|
||||
SkMask::FreeImage(mask.fImage);
|
||||
}
|
||||
private:
|
||||
typedef BlurRectBench INHERITED;
|
||||
};
|
||||
|
||||
class BlurRectSeparableBench: public BlurRectBench {
|
||||
SkMask fSrcMask;
|
||||
|
||||
public:
|
||||
BlurRectSeparableBench(void *param, SkScalar rad) : BlurRectBench(param, rad) {
|
||||
SkString name;
|
||||
if (SkScalarFraction(rad) != 0) {
|
||||
name.printf("blurrect_separable_%.2f", SkScalarToFloat(rad));
|
||||
} else {
|
||||
name.printf("blurrect_separable_%d", SkScalarRound(rad));
|
||||
}
|
||||
setName(name);
|
||||
BlurRectSeparableBench(void *param, SkScalar rad) : INHERITED(param, rad) {
|
||||
fSrcMask.fImage = NULL;
|
||||
}
|
||||
|
||||
@ -123,21 +117,106 @@ protected:
|
||||
|
||||
memset(fSrcMask.fImage, 0xff, fSrcMask.computeTotalImageSize());
|
||||
}
|
||||
|
||||
SkMask fSrcMask;
|
||||
private:
|
||||
typedef BlurRectBench INHERITED;
|
||||
};
|
||||
|
||||
class BlurRectBoxFilterBench: public BlurRectSeparableBench {
|
||||
public:
|
||||
BlurRectBoxFilterBench(void *param, SkScalar rad) : INHERITED(param, rad) {
|
||||
SkString name;
|
||||
if (SkScalarFraction(rad) != 0) {
|
||||
name.printf("blurrect_boxfilter_%.2f", SkScalarToFloat(rad));
|
||||
} else {
|
||||
name.printf("blurrect_boxfilter_%d", SkScalarRoundToInt(rad));
|
||||
}
|
||||
setName(name);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
virtual void makeBlurryRect(const SkRect& r) SK_OVERRIDE {
|
||||
SkMask mask;
|
||||
SkBlurMask::BlurSeparable(&mask, fSrcMask, radius(),
|
||||
mask.fImage = NULL;
|
||||
SkBlurMask::BlurSeparable(&mask, fSrcMask, this->radius(),
|
||||
SkBlurMask::kNormal_Style,
|
||||
SkBlurMask::kHigh_Quality);
|
||||
SkMask::FreeImage(mask.fImage);
|
||||
}
|
||||
private:
|
||||
typedef BlurRectSeparableBench INHERITED;
|
||||
};
|
||||
|
||||
DEF_BENCH(return new BlurRectSeparableBench(p, SMALL);)
|
||||
DEF_BENCH(return new BlurRectSeparableBench(p, BIG);)
|
||||
DEF_BENCH(return new BlurRectSeparableBench(p, REALBIG);)
|
||||
DEF_BENCH(return new BlurRectSeparableBench(p, REAL);)
|
||||
class BlurRectGaussianBench: public BlurRectSeparableBench {
|
||||
public:
|
||||
BlurRectGaussianBench(void *param, SkScalar rad) : INHERITED(param, rad) {
|
||||
SkString name;
|
||||
if (SkScalarFraction(rad) != 0) {
|
||||
name.printf("blurrect_gaussian_%.2f", SkScalarToFloat(rad));
|
||||
} else {
|
||||
name.printf("blurrect_gaussian_%d", SkScalarRoundToInt(rad));
|
||||
}
|
||||
setName(name);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
virtual void makeBlurryRect(const SkRect& r) SK_OVERRIDE {
|
||||
SkMask mask;
|
||||
mask.fImage = NULL;
|
||||
SkBlurMask::BlurGroundTruth(&mask, fSrcMask, this->radius(),
|
||||
SkBlurMask::kNormal_Style);
|
||||
SkMask::FreeImage(mask.fImage);
|
||||
}
|
||||
private:
|
||||
typedef BlurRectSeparableBench INHERITED;
|
||||
};
|
||||
|
||||
DEF_BENCH(return new BlurRectBoxFilterBench(p, SMALL);)
|
||||
DEF_BENCH(return new BlurRectBoxFilterBench(p, BIG);)
|
||||
DEF_BENCH(return new BlurRectBoxFilterBench(p, REALBIG);)
|
||||
DEF_BENCH(return new BlurRectBoxFilterBench(p, REAL);)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SMALL);)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, BIG);)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, REALBIG);)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, REAL);)
|
||||
DEF_BENCH(return new BlurRectDirectBench(p, SMALL);)
|
||||
DEF_BENCH(return new BlurRectDirectBench(p, BIG);)
|
||||
DEF_BENCH(return new BlurRectDirectBench(p, REALBIG);)
|
||||
DEF_BENCH(return new BlurRectDirectBench(p, REAL);)
|
||||
|
||||
DEF_BENCH(return new BlurRectDirectBench(p, SkIntToScalar(5));)
|
||||
DEF_BENCH(return new BlurRectDirectBench(p, SkIntToScalar(20));)
|
||||
|
||||
DEF_BENCH(return new BlurRectBoxFilterBench(p, SkIntToScalar(5));)
|
||||
DEF_BENCH(return new BlurRectBoxFilterBench(p, SkIntToScalar(20));)
|
||||
|
||||
#if 0
|
||||
// disable Gaussian benchmarks; the algorithm works well enough
|
||||
// and serves as a baseline for ground truth, but it's too slow
|
||||
// to use in production for non-trivial radii, so no real point
|
||||
// in having the bots benchmark it all the time.
|
||||
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(1));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(2));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(3));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(4));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(5));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(6));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(7));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(8));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(9));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(10));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(11));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(12));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(13));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(14));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(15));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(16));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(17));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(18));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(19));)
|
||||
DEF_BENCH(return new BlurRectGaussianBench(p, SkIntToScalar(20));)
|
||||
#endif
|
||||
|
241
gm/blurrect.cpp
241
gm/blurrect.cpp
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
* Copyright 2012 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 "SkBlurMaskFilter.h"
|
||||
@ -65,19 +65,18 @@ static const char* gBlurStyle2Name[] = {
|
||||
};
|
||||
|
||||
class BlurRectGM : public skiagm::GM {
|
||||
SkAutoTUnref<SkMaskFilter> fMaskFilter;
|
||||
SkString fName;
|
||||
PaintProc fPProc;
|
||||
SkAlpha fAlpha;
|
||||
SkAutoTUnref<SkMaskFilter> fMaskFilter;
|
||||
SkString fName;
|
||||
PaintProc fPProc;
|
||||
SkAlpha fAlpha;
|
||||
public:
|
||||
BlurRectGM(const char name[], PaintProc pproc, U8CPU alpha,
|
||||
SkBlurMaskFilter::BlurStyle bs) :
|
||||
fMaskFilter(SkBlurMaskFilter::Create(STROKE_WIDTH/2, bs,
|
||||
SkBlurMaskFilter::kHighQuality_BlurFlag))
|
||||
, fName(name)
|
||||
, fPProc(pproc)
|
||||
, fAlpha(SkToU8(alpha))
|
||||
{
|
||||
SkBlurMaskFilter::kHighQuality_BlurFlag))
|
||||
, fName(name)
|
||||
, fPProc(pproc)
|
||||
, fAlpha(SkToU8(alpha)) {
|
||||
fName.appendf("_%s", gBlurStyle2Name[bs]);
|
||||
}
|
||||
|
||||
@ -131,7 +130,7 @@ private:
|
||||
canvas->translate(0, r.height() * 4/3);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef GM INHERITED;
|
||||
};
|
||||
|
||||
@ -139,17 +138,28 @@ class BlurRectCompareGM : public skiagm::GM {
|
||||
SkString fName;
|
||||
unsigned int fRectWidth, fRectHeight;
|
||||
SkScalar fRadius;
|
||||
SkBlurMask::Style fStyle;
|
||||
public:
|
||||
BlurRectCompareGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, float radius)
|
||||
BlurRectCompareGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, float radius, SkBlurMask::Style style)
|
||||
: fName(name)
|
||||
, fRectWidth(rectWidth)
|
||||
, fRectHeight(rectHeight)
|
||||
, fRadius(radius)
|
||||
{}
|
||||
, fStyle(style)
|
||||
{}
|
||||
|
||||
int width() const { return fRectWidth; }
|
||||
int height() const { return fRectHeight; }
|
||||
SkScalar radius() const { return fRadius; }
|
||||
int width() const {
|
||||
return fRectWidth;
|
||||
}
|
||||
int height() const {
|
||||
return fRectHeight;
|
||||
}
|
||||
SkScalar radius() const {
|
||||
return fRadius;
|
||||
}
|
||||
SkBlurMask::Style style() const {
|
||||
return fStyle;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual SkString onShortName() {
|
||||
@ -160,21 +170,34 @@ protected:
|
||||
return SkISize::Make(640, 480);
|
||||
}
|
||||
|
||||
virtual void makeMask(SkMask *m, const SkRect&) = 0;
|
||||
virtual bool makeMask(SkMask *m, const SkRect&) = 0;
|
||||
|
||||
virtual void onDraw(SkCanvas* canvas) {
|
||||
SkRect r;
|
||||
r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight));
|
||||
SkRect r;
|
||||
r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight));
|
||||
|
||||
SkMask mask;
|
||||
SkISize canvas_size = canvas->getDeviceSize();
|
||||
int center_x = (canvas_size.fWidth - r.width())/2;
|
||||
int center_y = (canvas_size.fHeight - r.height())/2;
|
||||
|
||||
this->makeMask(&mask, r);
|
||||
SkAutoMaskFreeImage amfi(mask.fImage);
|
||||
SkMask mask;
|
||||
|
||||
SkBitmap bm;
|
||||
bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height());
|
||||
bm.setPixels(mask.fImage);
|
||||
canvas->drawBitmap(bm, 50, 50, NULL);
|
||||
if (!this->makeMask(&mask, r)) {
|
||||
SkPaint paint;
|
||||
r.offset( center_x, center_y );
|
||||
canvas->drawRect(r,paint);
|
||||
return;
|
||||
}
|
||||
SkAutoMaskFreeImage amfi(mask.fImage);
|
||||
|
||||
SkBitmap bm;
|
||||
bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height());
|
||||
bm.setPixels(mask.fImage);
|
||||
|
||||
center_x = (canvas_size.fWidth - mask.fBounds.width())/2;
|
||||
center_y = (canvas_size.fHeight - mask.fBounds.height())/2;
|
||||
|
||||
canvas->drawBitmap(bm, center_x, center_y, NULL);
|
||||
}
|
||||
|
||||
virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
|
||||
@ -186,33 +209,94 @@ private:
|
||||
class BlurRectFastGM: public BlurRectCompareGM {
|
||||
public:
|
||||
BlurRectFastGM(const char name[], unsigned int rect_width,
|
||||
unsigned int rect_height, float blur_radius) :
|
||||
BlurRectCompareGM(name, rect_width, rect_height, blur_radius) {}
|
||||
unsigned int rect_height, float blur_radius,
|
||||
SkBlurMask::Style style) :
|
||||
INHERITED(name, rect_width, rect_height, blur_radius, style)
|
||||
{
|
||||
|
||||
}
|
||||
protected:
|
||||
virtual void makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
|
||||
SkBlurMask::BlurRect(m, r, radius(), SkBlurMask::kNormal_Style,
|
||||
SkBlurMask::kHigh_Quality );
|
||||
virtual bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
|
||||
return SkBlurMask::BlurRect(m, r, this->radius(), this->style());
|
||||
}
|
||||
private:
|
||||
typedef BlurRectCompareGM INHERITED;
|
||||
};
|
||||
|
||||
class BlurRectSlowGM: public BlurRectCompareGM {
|
||||
public:
|
||||
BlurRectSlowGM(const char name[], unsigned int rect_width, unsigned int rect_height, float blur_radius) :
|
||||
BlurRectCompareGM( name, rect_width, rect_height, blur_radius ) {}
|
||||
BlurRectSlowGM(const char name[], unsigned int rect_width, unsigned int rect_height,
|
||||
float blur_radius, SkBlurMask::Style style) :
|
||||
INHERITED(name, rect_width, rect_height, blur_radius, style)
|
||||
{
|
||||
|
||||
}
|
||||
protected:
|
||||
virtual void makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
|
||||
virtual bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
|
||||
SkMask src;
|
||||
r.roundOut(&src.fBounds);
|
||||
src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop); // move to origin
|
||||
src.fFormat = SkMask::kA8_Format;
|
||||
src.fRowBytes = src.fBounds.width();
|
||||
src.fImage = SkMask::AllocImage( src.computeTotalImageSize() );
|
||||
src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
|
||||
SkAutoMaskFreeImage amfi(src.fImage);
|
||||
|
||||
memset(src.fImage, 0xff, src.computeTotalImageSize());
|
||||
|
||||
SkBlurMask::BlurSeparable(m, src, radius()/2, SkBlurMask::kNormal_Style, SkBlurMask::kHigh_Quality);
|
||||
return SkBlurMask::BlurSeparable(m, src, this->radius(), this->style(), this->getQuality());
|
||||
}
|
||||
|
||||
virtual SkBlurMask::Quality getQuality() {
|
||||
return SkBlurMask::kHigh_Quality;
|
||||
}
|
||||
private:
|
||||
typedef BlurRectCompareGM INHERITED;
|
||||
};
|
||||
|
||||
class BlurRectSlowLowGM: public BlurRectSlowGM {
|
||||
public:
|
||||
BlurRectSlowLowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
|
||||
float blurRadius, SkBlurMask::Style style) :
|
||||
INHERITED(name, rectWidth, rectHeight, blurRadius, style)
|
||||
{
|
||||
|
||||
}
|
||||
protected:
|
||||
virtual SkBlurMask::Quality getQuality() SK_OVERRIDE {
|
||||
return SkBlurMask::kLow_Quality;
|
||||
}
|
||||
private:
|
||||
typedef BlurRectSlowGM INHERITED;
|
||||
};
|
||||
|
||||
class BlurRectGroundTruthGM: public BlurRectCompareGM {
|
||||
public:
|
||||
BlurRectGroundTruthGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
|
||||
float blurRadius, SkBlurMask::Style style) :
|
||||
INHERITED(name, rectWidth, rectHeight, blurRadius, style)
|
||||
{
|
||||
|
||||
}
|
||||
protected:
|
||||
virtual bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
|
||||
SkMask src;
|
||||
r.roundOut(&src.fBounds);
|
||||
src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop); // move to origin
|
||||
src.fFormat = SkMask::kA8_Format;
|
||||
src.fRowBytes = src.fBounds.width();
|
||||
src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
|
||||
SkAutoMaskFreeImage amfi(src.fImage);
|
||||
|
||||
memset(src.fImage, 0xff, src.computeTotalImageSize());
|
||||
|
||||
return SkBlurMask::BlurGroundTruth(m, src, this->radius(), this->style());
|
||||
}
|
||||
|
||||
virtual SkBlurMask::Quality getQuality() {
|
||||
return SkBlurMask::kHigh_Quality;
|
||||
}
|
||||
private:
|
||||
typedef BlurRectCompareGM INHERITED;
|
||||
};
|
||||
|
||||
|
||||
@ -223,12 +307,73 @@ DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kSolid_Bl
|
||||
DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kOuter_BlurStyle);)
|
||||
DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kInner_BlurStyle);)
|
||||
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_fast_100_100_10", 100, 100, 10);)
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_fast_100_100_2", 100, 100, 2);)
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_fast_10_10_100", 10, 10, 100);)
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_fast_10_100_10", 10, 100, 10);)
|
||||
// regular size rects, blurs should be small enough not to completely overlap.
|
||||
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_slow_100_100_10", 100, 100, 10);)
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_slow_100_100_2", 100, 100, 2);)
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_slow_10_10_100", 10, 10, 100);)
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_slow_10_100_10", 10, 100, 10);)
|
||||
DEF_GM(return new BlurRectFastGM( "blurrect_25_100_2_normal_fast", 25, 100, 2, SkBlurMask::kNormal_Style);)
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_25_100_20_normal_fast", 25, 100, 20, SkBlurMask::kNormal_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM( "blurrect_25_100_2_normal_slow", 25, 100, 2, SkBlurMask::kNormal_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_25_100_20_normal_slow", 25, 100, 20, SkBlurMask::kNormal_Style);)
|
||||
DEF_GM(return new BlurRectFastGM( "blurrect_25_100_2_inner_fast", 25, 100, 2, SkBlurMask::kInner_Style);)
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_25_100_20_inner_fast", 25, 100, 20, SkBlurMask::kInner_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM( "blurrect_25_100_2_inner_slow", 25, 100, 2, SkBlurMask::kInner_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_25_100_20_inner_slow", 25, 100, 20, SkBlurMask::kInner_Style);)
|
||||
DEF_GM(return new BlurRectFastGM( "blurrect_25_100_2_outer_fast", 25, 100, 2, SkBlurMask::kOuter_Style);)
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_25_100_20_outer_fast", 25, 100, 20, SkBlurMask::kOuter_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM( "blurrect_25_100_2_outer_slow", 25, 100, 2, SkBlurMask::kOuter_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_25_100_20_outer_slow", 25, 100, 20, SkBlurMask::kOuter_Style);)
|
||||
|
||||
// skinny tall rects, blurs overlap in X but not y
|
||||
|
||||
DEF_GM(return new BlurRectFastGM( "blurrect_5_100_2_normal_fast", 5, 100, 2 , SkBlurMask::kNormal_Style);)
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_5_100_20_normal_fast", 5, 100, 20, SkBlurMask::kNormal_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM( "blurrect_5_100_2_normal_slow", 5, 100, 2 , SkBlurMask::kNormal_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_5_100_20_normal_slow", 5, 100, 20, SkBlurMask::kNormal_Style);)
|
||||
DEF_GM(return new BlurRectFastGM( "blurrect_5_100_2_inner_fast", 5, 100, 2 , SkBlurMask::kInner_Style);)
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_5_100_20_inner_fast", 5, 100, 20, SkBlurMask::kInner_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM( "blurrect_5_100_2_inner_slow", 5, 100, 2 , SkBlurMask::kInner_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_5_100_20_inner_slow", 5, 100, 20, SkBlurMask::kInner_Style);)
|
||||
DEF_GM(return new BlurRectFastGM( "blurrect_5_100_2_outer_fast", 5, 100, 2 , SkBlurMask::kOuter_Style);)
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_5_100_20_outer_fast", 5, 100, 20, SkBlurMask::kOuter_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM( "blurrect_5_100_2_outer_slow", 5, 100, 2 , SkBlurMask::kOuter_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_5_100_20_outer_slow", 5, 100, 20, SkBlurMask::kOuter_Style);)
|
||||
|
||||
// tiny rects, blurs overlap in X and Y
|
||||
|
||||
DEF_GM(return new BlurRectFastGM( "blurrect_5_5_2_normal_fast", 5, 5, 2 , SkBlurMask::kNormal_Style);)
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_5_5_20_normal_fast", 5, 5, 20, SkBlurMask::kNormal_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM( "blurrect_5_5_2_normal_slow", 5, 5, 2 , SkBlurMask::kNormal_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_5_5_20_normal_slow", 5, 5, 20, SkBlurMask::kNormal_Style);)
|
||||
DEF_GM(return new BlurRectFastGM( "blurrect_5_5_2_inner_fast", 5, 5, 2 , SkBlurMask::kInner_Style);)
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_5_5_20_inner_fast", 5, 5, 20, SkBlurMask::kInner_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM( "blurrect_5_5_2_inner_slow", 5, 5, 2 , SkBlurMask::kInner_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_5_5_20_inner_slow", 5, 5, 20, SkBlurMask::kInner_Style);)
|
||||
DEF_GM(return new BlurRectFastGM( "blurrect_5_5_2_outer_fast", 5, 5, 2 , SkBlurMask::kOuter_Style);)
|
||||
DEF_GM(return new BlurRectFastGM("blurrect_5_5_20_outer_fast", 5, 5, 20, SkBlurMask::kOuter_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM( "blurrect_5_5_2_outer_slow", 5, 5, 2 , SkBlurMask::kOuter_Style);)
|
||||
DEF_GM(return new BlurRectSlowGM("blurrect_5_5_20_outer_slow", 5, 5, 20, SkBlurMask::kOuter_Style);)
|
||||
|
||||
|
||||
#if 0
|
||||
// dont' need to GM the gaussian convolution; it's slow and intended
|
||||
// as a ground truth comparison only. Leaving these here in case we
|
||||
// ever want to turn these back on for debugging reasons.
|
||||
DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_1_simple", 25, 100, 1);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_2_simple", 25, 100, 2);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_3_simple", 25, 100, 3);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_4_simple", 25, 100, 4);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_5_simple", 25, 100, 5);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_6_simple", 25, 100, 6);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_7_simple", 25, 100, 7);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_8_simple", 25, 100, 8);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_9_simple", 25, 100, 9);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_10_simple", 25, 100, 10);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_11_simple", 25, 100, 11);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_12_simple", 25, 100, 12);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_13_simple", 25, 100, 13);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_14_simple", 25, 100, 14);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_15_simple", 25, 100, 15);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_16_simple", 25, 100, 16);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_17_simple", 25, 100, 17);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_18_simple", 25, 100, 18);)
|
||||
DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_19_simple", 25, 100, 19);)
|
||||
#endif
|
||||
|
@ -210,7 +210,7 @@ static int boxBlur(const uint8_t* src, int src_y_stride, uint8_t* dst,
|
||||
RIGHT_BORDER_ITER
|
||||
}
|
||||
#undef RIGHT_BORDER_ITER
|
||||
for (int x = 0; x < leftRadius - rightRadius; x++) {
|
||||
for (int x = 0; x < leftRadius - rightRadius; ++x) {
|
||||
*dptr = 0;
|
||||
dptr += dst_x_stride;
|
||||
}
|
||||
@ -326,7 +326,7 @@ static int boxBlurInterp(const uint8_t* src, int src_y_stride, uint8_t* dst,
|
||||
}
|
||||
#endif
|
||||
|
||||
for (;x < border; x++) {
|
||||
for (;x < border; ++x) {
|
||||
LEFT_BORDER_ITER
|
||||
}
|
||||
#undef LEFT_BORDER_ITER
|
||||
@ -395,7 +395,7 @@ static int boxBlurInterp(const uint8_t* src, int src_y_stride, uint8_t* dst,
|
||||
RIGHT_BORDER_ITER
|
||||
}
|
||||
#endif
|
||||
for (; x < border; x++) {
|
||||
for (; x < border; ++x) {
|
||||
RIGHT_BORDER_ITER
|
||||
}
|
||||
#undef RIGHT_BORDER_ITER
|
||||
@ -519,17 +519,19 @@ static void kernel_clamped(uint8_t dst[], int rx, int ry, const uint32_t sum[],
|
||||
int prev_y = -2*ry;
|
||||
int next_y = 1;
|
||||
|
||||
for (int y = 0; y < dh; y++) {
|
||||
for (int y = 0; y < dh; ++y) {
|
||||
int py = SkClampPos(prev_y) * sumStride;
|
||||
int ny = SkFastMin32(next_y, sh) * sumStride;
|
||||
|
||||
int prev_x = -2*rx;
|
||||
int next_x = 1;
|
||||
|
||||
for (int x = 0; x < dw; x++) {
|
||||
for (int x = 0; x < dw; ++x) {
|
||||
int px = SkClampPos(prev_x);
|
||||
int nx = SkFastMin32(next_x, sw);
|
||||
|
||||
// TODO: should we be adding 1/2 (1 << 23) to round to the
|
||||
// nearest integer here?
|
||||
uint32_t tmp = sum[px+py] + sum[nx+ny] - sum[nx+py] - sum[px+ny];
|
||||
*dst++ = SkToU8(tmp * scale >> 24);
|
||||
|
||||
@ -549,7 +551,7 @@ static void kernel_clamped(uint8_t dst[], int rx, int ry, const uint32_t sum[],
|
||||
*
|
||||
* The inner loop is conceptually simple; we break it into several sections
|
||||
* to improve performance. Here's the original version:
|
||||
for (int x = 0; x < dw; x++) {
|
||||
for (int x = 0; x < dw; ++x) {
|
||||
int px = SkClampPos(prev_x);
|
||||
int nx = SkFastMin32(next_x, sw);
|
||||
|
||||
@ -585,7 +587,7 @@ static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
|
||||
|
||||
SkASSERT(2*rx <= dw - 2*rx);
|
||||
|
||||
for (int y = 0; y < dh; y++) {
|
||||
for (int y = 0; y < dh; ++y) {
|
||||
int py = SkClampPos(prev_y) * sumStride;
|
||||
int ny = SkFastMin32(next_y, sh) * sumStride;
|
||||
|
||||
@ -593,7 +595,7 @@ static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
|
||||
int next_x = 1;
|
||||
int x = 0;
|
||||
|
||||
for (; x < 2*rx; x++) {
|
||||
for (; x < 2*rx; ++x) {
|
||||
SkASSERT(prev_x <= 0);
|
||||
SkASSERT(next_x <= sw);
|
||||
|
||||
@ -631,7 +633,7 @@ static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
|
||||
}
|
||||
#endif
|
||||
|
||||
for (; x < dw - 2*rx; x++) {
|
||||
for (; x < dw - 2*rx; ++x) {
|
||||
SkASSERT(prev_x >= 0);
|
||||
SkASSERT(next_x <= sw);
|
||||
|
||||
@ -642,7 +644,7 @@ static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
|
||||
next_x += 1;
|
||||
}
|
||||
|
||||
for (; x < dw; x++) {
|
||||
for (; x < dw; ++x) {
|
||||
SkASSERT(prev_x >= 0);
|
||||
SkASSERT(next_x > sw);
|
||||
|
||||
@ -666,17 +668,17 @@ static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t sum[],
|
||||
* is wider than the source image.
|
||||
*/
|
||||
static void kernel_interp_clamped(uint8_t dst[], int rx, int ry,
|
||||
const uint32_t sum[], int sw, int sh, U8CPU outer_weight) {
|
||||
const uint32_t sum[], int sw, int sh, U8CPU outerWeight) {
|
||||
SkASSERT(2*rx > sw);
|
||||
|
||||
int inner_weight = 255 - outer_weight;
|
||||
int innerWeight = 255 - outerWeight;
|
||||
|
||||
// round these guys up if they're bigger than 127
|
||||
outer_weight += outer_weight >> 7;
|
||||
inner_weight += inner_weight >> 7;
|
||||
outerWeight += outerWeight >> 7;
|
||||
innerWeight += innerWeight >> 7;
|
||||
|
||||
uint32_t outer_scale = (outer_weight << 16) / ((2*rx + 1)*(2*ry + 1));
|
||||
uint32_t inner_scale = (inner_weight << 16) / ((2*rx - 1)*(2*ry - 1));
|
||||
uint32_t outerScale = (outerWeight << 16) / ((2*rx + 1)*(2*ry + 1));
|
||||
uint32_t innerScale = (innerWeight << 16) / ((2*rx - 1)*(2*ry - 1));
|
||||
|
||||
int sumStride = sw + 1;
|
||||
|
||||
@ -686,7 +688,7 @@ static void kernel_interp_clamped(uint8_t dst[], int rx, int ry,
|
||||
int prev_y = -2*ry;
|
||||
int next_y = 1;
|
||||
|
||||
for (int y = 0; y < dh; y++) {
|
||||
for (int y = 0; y < dh; ++y) {
|
||||
int py = SkClampPos(prev_y) * sumStride;
|
||||
int ny = SkFastMin32(next_y, sh) * sumStride;
|
||||
|
||||
@ -696,19 +698,19 @@ static void kernel_interp_clamped(uint8_t dst[], int rx, int ry,
|
||||
int prev_x = -2*rx;
|
||||
int next_x = 1;
|
||||
|
||||
for (int x = 0; x < dw; x++) {
|
||||
for (int x = 0; x < dw; ++x) {
|
||||
int px = SkClampPos(prev_x);
|
||||
int nx = SkFastMin32(next_x, sw);
|
||||
|
||||
int ipx = SkClampPos(prev_x + 1);
|
||||
int inx = SkClampMax(next_x - 1, sw);
|
||||
|
||||
uint32_t outer_sum = sum[px+py] + sum[nx+ny]
|
||||
uint32_t outerSum = sum[px+py] + sum[nx+ny]
|
||||
- sum[nx+py] - sum[px+ny];
|
||||
uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny]
|
||||
uint32_t innerSum = sum[ipx+ipy] + sum[inx+iny]
|
||||
- sum[inx+ipy] - sum[ipx+iny];
|
||||
*dst++ = SkToU8((outer_sum * outer_scale
|
||||
+ inner_sum * inner_scale) >> 24);
|
||||
*dst++ = SkToU8((outerSum * outerScale
|
||||
+ innerSum * innerScale) >> 24);
|
||||
|
||||
prev_x += 1;
|
||||
next_x += 1;
|
||||
@ -726,19 +728,19 @@ static void kernel_interp_clamped(uint8_t dst[], int rx, int ry,
|
||||
*
|
||||
* The inner loop is conceptually simple; we break it into several variants
|
||||
* to improve performance. Here's the original version:
|
||||
for (int x = 0; x < dw; x++) {
|
||||
for (int x = 0; x < dw; ++x) {
|
||||
int px = SkClampPos(prev_x);
|
||||
int nx = SkFastMin32(next_x, sw);
|
||||
|
||||
int ipx = SkClampPos(prev_x + 1);
|
||||
int inx = SkClampMax(next_x - 1, sw);
|
||||
|
||||
uint32_t outer_sum = sum[px+py] + sum[nx+ny]
|
||||
uint32_t outerSum = sum[px+py] + sum[nx+ny]
|
||||
- sum[nx+py] - sum[px+ny];
|
||||
uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny]
|
||||
uint32_t innerSum = sum[ipx+ipy] + sum[inx+iny]
|
||||
- sum[inx+ipy] - sum[ipx+iny];
|
||||
*dst++ = SkToU8((outer_sum * outer_scale
|
||||
+ inner_sum * inner_scale) >> 24);
|
||||
*dst++ = SkToU8((outerSum * outerScale
|
||||
+ innerSum * innerScale) >> 24);
|
||||
|
||||
prev_x += 1;
|
||||
next_x += 1;
|
||||
@ -751,23 +753,23 @@ static void kernel_interp_clamped(uint8_t dst[], int rx, int ry,
|
||||
* speedup.
|
||||
*/
|
||||
static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
|
||||
const uint32_t sum[], int sw, int sh, U8CPU outer_weight) {
|
||||
const uint32_t sum[], int sw, int sh, U8CPU outerWeight) {
|
||||
SkASSERT(rx > 0 && ry > 0);
|
||||
SkASSERT(outer_weight <= 255);
|
||||
SkASSERT(outerWeight <= 255);
|
||||
|
||||
if (2*rx > sw) {
|
||||
kernel_interp_clamped(dst, rx, ry, sum, sw, sh, outer_weight);
|
||||
kernel_interp_clamped(dst, rx, ry, sum, sw, sh, outerWeight);
|
||||
return;
|
||||
}
|
||||
|
||||
int inner_weight = 255 - outer_weight;
|
||||
int innerWeight = 255 - outerWeight;
|
||||
|
||||
// round these guys up if they're bigger than 127
|
||||
outer_weight += outer_weight >> 7;
|
||||
inner_weight += inner_weight >> 7;
|
||||
outerWeight += outerWeight >> 7;
|
||||
innerWeight += innerWeight >> 7;
|
||||
|
||||
uint32_t outer_scale = (outer_weight << 16) / ((2*rx + 1)*(2*ry + 1));
|
||||
uint32_t inner_scale = (inner_weight << 16) / ((2*rx - 1)*(2*ry - 1));
|
||||
uint32_t outerScale = (outerWeight << 16) / ((2*rx + 1)*(2*ry + 1));
|
||||
uint32_t innerScale = (innerWeight << 16) / ((2*rx - 1)*(2*ry - 1));
|
||||
|
||||
int sumStride = sw + 1;
|
||||
|
||||
@ -779,7 +781,7 @@ static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
|
||||
|
||||
SkASSERT(2*rx <= dw - 2*rx);
|
||||
|
||||
for (int y = 0; y < dh; y++) {
|
||||
for (int y = 0; y < dh; ++y) {
|
||||
int py = SkClampPos(prev_y) * sumStride;
|
||||
int ny = SkFastMin32(next_y, sh) * sumStride;
|
||||
|
||||
@ -790,7 +792,7 @@ static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
|
||||
int next_x = 1;
|
||||
int x = 0;
|
||||
|
||||
for (; x < 2*rx; x++) {
|
||||
for (; x < 2*rx; ++x) {
|
||||
SkASSERT(prev_x < 0);
|
||||
SkASSERT(next_x <= sw);
|
||||
|
||||
@ -800,12 +802,12 @@ static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
|
||||
int ipx = 0;
|
||||
int inx = next_x - 1;
|
||||
|
||||
uint32_t outer_sum = sum[px+py] + sum[nx+ny]
|
||||
uint32_t outerSum = sum[px+py] + sum[nx+ny]
|
||||
- sum[nx+py] - sum[px+ny];
|
||||
uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny]
|
||||
uint32_t innerSum = sum[ipx+ipy] + sum[inx+iny]
|
||||
- sum[inx+ipy] - sum[ipx+iny];
|
||||
*dst++ = SkToU8((outer_sum * outer_scale
|
||||
+ inner_sum * inner_scale) >> 24);
|
||||
*dst++ = SkToU8((outerSum * outerScale
|
||||
+ innerSum * innerScale) >> 24);
|
||||
|
||||
prev_x += 1;
|
||||
next_x += 1;
|
||||
@ -825,42 +827,42 @@ static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
|
||||
SkASSERT(prev_x >= 0);
|
||||
SkASSERT(next_x <= sw);
|
||||
|
||||
uint32_t outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
|
||||
uint32_t inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
|
||||
*dst++ = SkToU8((outer_sum * outer_scale
|
||||
+ inner_sum * inner_scale) >> 24);
|
||||
outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
|
||||
inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
|
||||
*dst++ = SkToU8((outer_sum * outer_scale
|
||||
+ inner_sum * inner_scale) >> 24);
|
||||
outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
|
||||
inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
|
||||
*dst++ = SkToU8((outer_sum * outer_scale
|
||||
+ inner_sum * inner_scale) >> 24);
|
||||
outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
|
||||
inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
|
||||
*dst++ = SkToU8((outer_sum * outer_scale
|
||||
+ inner_sum * inner_scale) >> 24);
|
||||
uint32_t outerSum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
|
||||
uint32_t innerSum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
|
||||
*dst++ = SkToU8((outerSum * outerScale
|
||||
+ innerSum * innerScale) >> 24);
|
||||
outerSum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
|
||||
innerSum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
|
||||
*dst++ = SkToU8((outerSum * outerScale
|
||||
+ innerSum * innerScale) >> 24);
|
||||
outerSum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
|
||||
innerSum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
|
||||
*dst++ = SkToU8((outerSum * outerScale
|
||||
+ innerSum * innerScale) >> 24);
|
||||
outerSum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
|
||||
innerSum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
|
||||
*dst++ = SkToU8((outerSum * outerScale
|
||||
+ innerSum * innerScale) >> 24);
|
||||
|
||||
prev_x += 4;
|
||||
next_x += 4;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (; x < dw - 2*rx; x++) {
|
||||
for (; x < dw - 2*rx; ++x) {
|
||||
SkASSERT(prev_x >= 0);
|
||||
SkASSERT(next_x <= sw);
|
||||
|
||||
uint32_t outer_sum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
|
||||
uint32_t inner_sum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
|
||||
*dst++ = SkToU8((outer_sum * outer_scale
|
||||
+ inner_sum * inner_scale) >> 24);
|
||||
uint32_t outerSum = sum[i0++] + sum[i1++] - sum[i2++] - sum[i3++];
|
||||
uint32_t innerSum = sum[i4++] + sum[i5++] - sum[i6++] - sum[i7++];
|
||||
*dst++ = SkToU8((outerSum * outerScale
|
||||
+ innerSum * innerScale) >> 24);
|
||||
|
||||
prev_x += 1;
|
||||
next_x += 1;
|
||||
}
|
||||
|
||||
for (; x < dw; x++) {
|
||||
for (; x < dw; ++x) {
|
||||
SkASSERT(prev_x >= 0);
|
||||
SkASSERT(next_x > sw);
|
||||
|
||||
@ -870,12 +872,12 @@ static void apply_kernel_interp(uint8_t dst[], int rx, int ry,
|
||||
int ipx = prev_x + 1;
|
||||
int inx = sw;
|
||||
|
||||
uint32_t outer_sum = sum[px+py] + sum[nx+ny]
|
||||
uint32_t outerSum = sum[px+py] + sum[nx+ny]
|
||||
- sum[nx+py] - sum[px+ny];
|
||||
uint32_t inner_sum = sum[ipx+ipy] + sum[inx+iny]
|
||||
uint32_t innerSum = sum[ipx+ipy] + sum[inx+iny]
|
||||
- sum[inx+ipy] - sum[ipx+iny];
|
||||
*dst++ = SkToU8((outer_sum * outer_scale
|
||||
+ inner_sum * inner_scale) >> 24);
|
||||
*dst++ = SkToU8((outerSum * outerScale
|
||||
+ innerSum * innerScale) >> 24);
|
||||
|
||||
prev_x += 1;
|
||||
next_x += 1;
|
||||
@ -955,6 +957,7 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
|
||||
SkScalar radius, Style style, Quality quality,
|
||||
SkIPoint* margin, bool separable)
|
||||
{
|
||||
|
||||
if (src.fFormat != SkMask::kA8_Format) {
|
||||
return false;
|
||||
}
|
||||
@ -963,16 +966,27 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
|
||||
if (radius < SkIntToScalar(3)) {
|
||||
quality = kLow_Quality;
|
||||
}
|
||||
|
||||
// highQuality: use three box blur passes as a cheap way to approximate a Gaussian blur
|
||||
|
||||
// highQuality: use three box blur passes as a cheap way
|
||||
// to approximate a Gaussian blur
|
||||
int passCount = (kHigh_Quality == quality) ? 3 : 1;
|
||||
SkScalar passRadius = (kHigh_Quality == quality) ? SkScalarMul( radius, kBlurRadiusFudgeFactor): radius;
|
||||
SkScalar passRadius = (kHigh_Quality == quality) ?
|
||||
SkScalarMul( radius, kBlurRadiusFudgeFactor):
|
||||
radius;
|
||||
|
||||
#ifndef SK_IGNORE_BLUR_RADIUS_CORRECTNESS
|
||||
// multiply the given radius by sqrt(2)/2 to convert
|
||||
// from (2x) standard deviation to needed box width
|
||||
const SkScalar radiusMultiplier = SkFloatToScalar(0.707f);
|
||||
SkScalar boxWidth = SkScalarMul(passRadius, radiusMultiplier);
|
||||
passRadius = SkScalarMul(boxWidth,SK_ScalarHalf) - SK_ScalarHalf;
|
||||
#endif
|
||||
|
||||
int rx = SkScalarCeil(passRadius);
|
||||
int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255);
|
||||
int outerWeight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255);
|
||||
|
||||
SkASSERT(rx >= 0);
|
||||
SkASSERT((unsigned)outer_weight <= 255);
|
||||
SkASSERT((unsigned)outerWeight <= 255);
|
||||
if (rx <= 0) {
|
||||
return false;
|
||||
}
|
||||
@ -981,11 +995,13 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
|
||||
|
||||
int padx = passCount * rx;
|
||||
int pady = passCount * ry;
|
||||
|
||||
if (margin) {
|
||||
margin->set(padx, pady);
|
||||
}
|
||||
dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady,
|
||||
src.fBounds.fRight + padx, src.fBounds.fBottom + pady);
|
||||
|
||||
dst->fRowBytes = dst->fBounds.width();
|
||||
dst->fFormat = SkMask::kA8_Format;
|
||||
dst->fImage = NULL;
|
||||
@ -1000,7 +1016,6 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
|
||||
int sh = src.fBounds.height();
|
||||
const uint8_t* sp = src.fImage;
|
||||
uint8_t* dp = SkMask::AllocImage(dstSize);
|
||||
|
||||
SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);
|
||||
|
||||
// build the blurry destination
|
||||
@ -1008,8 +1023,8 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
|
||||
SkAutoTMalloc<uint8_t> tmpBuffer(dstSize);
|
||||
uint8_t* tp = tmpBuffer.get();
|
||||
int w = sw, h = sh;
|
||||
|
||||
if (outer_weight == 255) {
|
||||
|
||||
if (outerWeight == 255) {
|
||||
int loRadius, hiRadius;
|
||||
get_adjusted_radii(passRadius, &loRadius, &hiRadius);
|
||||
if (kHigh_Quality == quality) {
|
||||
@ -1028,16 +1043,16 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
|
||||
} else {
|
||||
if (kHigh_Quality == quality) {
|
||||
// Do three X blurs, with a transpose on the final one.
|
||||
w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, false, outer_weight);
|
||||
w = boxBlurInterp(tp, w, dp, rx, w, h, false, outer_weight);
|
||||
w = boxBlurInterp(dp, w, tp, rx, w, h, true, outer_weight);
|
||||
w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, false, outerWeight);
|
||||
w = boxBlurInterp(tp, w, dp, rx, w, h, false, outerWeight);
|
||||
w = boxBlurInterp(dp, w, tp, rx, w, h, true, outerWeight);
|
||||
// Do three Y blurs, with a transpose on the final one.
|
||||
h = boxBlurInterp(tp, h, dp, ry, h, w, false, outer_weight);
|
||||
h = boxBlurInterp(dp, h, tp, ry, h, w, false, outer_weight);
|
||||
h = boxBlurInterp(tp, h, dp, ry, h, w, true, outer_weight);
|
||||
h = boxBlurInterp(tp, h, dp, ry, h, w, false, outerWeight);
|
||||
h = boxBlurInterp(dp, h, tp, ry, h, w, false, outerWeight);
|
||||
h = boxBlurInterp(tp, h, dp, ry, h, w, true, outerWeight);
|
||||
} else {
|
||||
w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, true, outer_weight);
|
||||
h = boxBlurInterp(tp, h, dp, ry, h, w, true, outer_weight);
|
||||
w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, true, outerWeight);
|
||||
h = boxBlurInterp(tp, h, dp, ry, h, w, true, outerWeight);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1048,10 +1063,10 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
|
||||
|
||||
//pass1: sp is source, dp is destination
|
||||
build_sum_buffer(sumBuffer, sw, sh, sp, src.fRowBytes);
|
||||
if (outer_weight == 255) {
|
||||
if (outerWeight == 255) {
|
||||
apply_kernel(dp, rx, ry, sumBuffer, sw, sh);
|
||||
} else {
|
||||
apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight);
|
||||
apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outerWeight);
|
||||
}
|
||||
|
||||
if (kHigh_Quality == quality) {
|
||||
@ -1060,21 +1075,21 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
|
||||
int tmp_sh = sh + 2 * ry;
|
||||
SkAutoTMalloc<uint8_t> tmpBuffer(dstSize);
|
||||
build_sum_buffer(sumBuffer, tmp_sw, tmp_sh, dp, tmp_sw);
|
||||
if (outer_weight == 255)
|
||||
if (outerWeight == 255)
|
||||
apply_kernel(tmpBuffer.get(), rx, ry, sumBuffer, tmp_sw, tmp_sh);
|
||||
else
|
||||
apply_kernel_interp(tmpBuffer.get(), rx, ry, sumBuffer,
|
||||
tmp_sw, tmp_sh, outer_weight);
|
||||
tmp_sw, tmp_sh, outerWeight);
|
||||
|
||||
//pass3: tmpBuffer is source, dp is destination
|
||||
tmp_sw += 2 * rx;
|
||||
tmp_sh += 2 * ry;
|
||||
build_sum_buffer(sumBuffer, tmp_sw, tmp_sh, tmpBuffer.get(), tmp_sw);
|
||||
if (outer_weight == 255)
|
||||
if (outerWeight == 255)
|
||||
apply_kernel(dp, rx, ry, sumBuffer, tmp_sw, tmp_sh);
|
||||
else
|
||||
apply_kernel_interp(dp, rx, ry, sumBuffer, tmp_sw, tmp_sh,
|
||||
outer_weight);
|
||||
outerWeight);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1126,26 +1141,43 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
|
||||
quadratic function:
|
||||
|
||||
0 x <= -1.5
|
||||
9/8 + 3/2 x + 1/2 x^2 -1.5 < x <= 1.5
|
||||
9/8 + 3/2 x + 1/2 x^2 -1.5 < x <= -.5
|
||||
3/4 - x^2 -.5 < x <= .5
|
||||
9/8 - 3/2 x + 1/2 x^2 0.5 < x <= 1.5
|
||||
0 1.5 < x
|
||||
|
||||
Mathematica:
|
||||
|
||||
g[x_] := Piecewise [ {
|
||||
{9/8 + 3/2 x + 1/2 x^2 , -1.5 < x <= -.5},
|
||||
{3/4 - x^2 , -.5 < x <= .5},
|
||||
{9/8 - 3/2 x + 1/2 x^2 , 0.5 < x <= 1.5}
|
||||
}, 0]
|
||||
|
||||
To get the profile curve of the blurred step function at the rectangle
|
||||
edge, we evaluate the indefinite integral, which is piecewise cubic:
|
||||
|
||||
0 x <= -1.5
|
||||
5/8 + 9/8 x + 3/4 x^2 + 1/6 x^3 -1.5 < x <= -0.5
|
||||
9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3 -1.5 < x <= -0.5
|
||||
1/2 + 3/4 x - 1/3 x^3 -.5 < x <= .5
|
||||
3/8 + 9/8 x - 3/4 x^2 + 1/6 x^3 .5 < x <= 1.5
|
||||
7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3 .5 < x <= 1.5
|
||||
1 1.5 < x
|
||||
|
||||
in Mathematica code:
|
||||
|
||||
gi[x_] := Piecewise[ {
|
||||
{ 0 , x <= -1.5 },
|
||||
{ 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3, -1.5 < x <= -0.5 },
|
||||
{ 1/2 + 3/4 x - 1/3 x^3 , -.5 < x <= .5},
|
||||
{ 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3, .5 < x <= 1.5}
|
||||
},1]
|
||||
*/
|
||||
|
||||
static float gaussian_integral( float x ) {
|
||||
if ( x > 1.5f ) {
|
||||
static float gaussianIntegral(float x) {
|
||||
if (x > 1.5f) {
|
||||
return 0.0f;
|
||||
}
|
||||
if ( x < -1.5f ) {
|
||||
if (x < -1.5f) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
@ -1153,7 +1185,7 @@ static float gaussian_integral( float x ) {
|
||||
float x3 = x2*x;
|
||||
|
||||
if ( x > 0.5f ) {
|
||||
return 0.5625f - ( x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
|
||||
return 0.5625f - (x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
|
||||
}
|
||||
if ( x > -0.5f ) {
|
||||
return 0.5f - (0.75f * x - x3 / 3.0f);
|
||||
@ -1174,19 +1206,19 @@ static float gaussian_integral( float x ) {
|
||||
memory returned in profile_out.
|
||||
*/
|
||||
|
||||
static int compute_profile( SkScalar radius, unsigned int **profile_out ) {
|
||||
int size = SkScalarFloorToInt(radius * 3 + 1);
|
||||
static int compute_profile(SkScalar radius, unsigned int **profile_out) {
|
||||
int size = SkScalarRoundToInt(radius * 3);
|
||||
int center = size >> 1;
|
||||
|
||||
unsigned int *profile = SkNEW_ARRAY(unsigned int, size);
|
||||
|
||||
float invr = 1.0f/radius;
|
||||
float invr = 1.f/radius;
|
||||
|
||||
profile[0] = 255;
|
||||
for (int x = 1 ; x < size ; x++) {
|
||||
float scaled_x = ( center - x ) * invr;
|
||||
float gi = gaussian_integral( scaled_x );
|
||||
profile[x] = 255 - (uint8_t) ( 255.f * gi );
|
||||
for (int x = 1 ; x < size ; ++x) {
|
||||
float scaled_x = (center - x - .5) * invr;
|
||||
float gi = gaussianIntegral(scaled_x);
|
||||
profile[x] = 255 - (uint8_t) (255.f * gi);
|
||||
}
|
||||
|
||||
*profile_out = profile;
|
||||
@ -1200,29 +1232,49 @@ static int compute_profile( SkScalar radius, unsigned int **profile_out ) {
|
||||
// Implementation adapted from Michael Herf's approach:
|
||||
// http://stereopsis.com/shadowrect/
|
||||
|
||||
static inline unsigned int profile_lookup( unsigned int *profile, int loc, int blurred_width, int sharp_width ) {
|
||||
int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge?
|
||||
int ox = dx >> 1;
|
||||
if (ox < 0) {
|
||||
ox = 0;
|
||||
}
|
||||
|
||||
return profile[ox];
|
||||
}
|
||||
|
||||
bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
|
||||
SkScalar provided_radius, Style style, Quality quality,
|
||||
SkScalar provided_radius, Style style,
|
||||
SkIPoint *margin) {
|
||||
int profile_size;
|
||||
unsigned int *profile;
|
||||
|
||||
|
||||
float radius = SkScalarToFloat( SkScalarMul( provided_radius, kBlurRadiusFudgeFactor ) );
|
||||
|
||||
#ifndef SK_IGNORE_BLUR_RADIUS_CORRECTNESS
|
||||
float stddev = SkScalarToFloat( radius ) /2.0f;
|
||||
radius = stddev * 1.414f;
|
||||
#endif
|
||||
|
||||
profile_size = compute_profile( radius, &profile );
|
||||
|
||||
SkAutoTDeleteArray<unsigned int> ada(profile);
|
||||
|
||||
int pad = (int) (radius * 1.5f + 1);
|
||||
int pad = profile_size/2;
|
||||
if (margin) {
|
||||
margin->set( pad, pad );
|
||||
}
|
||||
dst->fBounds = SkIRect::MakeWH(SkScalarFloorToInt(src.width()), SkScalarFloorToInt(src.height()));
|
||||
dst->fBounds.outset(pad, pad);
|
||||
|
||||
int shadow_left = -pad;
|
||||
int shadow_top = -pad;
|
||||
int shadow_right = src.width() + pad;
|
||||
int shadow_bottom = src.height() + pad;
|
||||
|
||||
dst->fBounds.set(shadow_left, shadow_top, shadow_right, shadow_bottom);
|
||||
|
||||
dst->fRowBytes = dst->fBounds.width();
|
||||
dst->fFormat = SkMask::kA8_Format;
|
||||
dst->fImage = NULL;
|
||||
|
||||
|
||||
size_t dstSize = dst->computeImageSize();
|
||||
if (0 == dstSize) {
|
||||
return false; // too big to allocate, abort
|
||||
@ -1235,8 +1287,8 @@ bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
|
||||
|
||||
dst->fImage = dp;
|
||||
|
||||
int dst_height = dst->fBounds.height();
|
||||
int dst_width = dst->fBounds.width();
|
||||
int dstHeight = dst->fBounds.height();
|
||||
int dstWidth = dst->fBounds.width();
|
||||
|
||||
// nearest odd number less than the profile size represents the center
|
||||
// of the (2x scaled) profile
|
||||
@ -1246,29 +1298,210 @@ bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
|
||||
int h = sh - center;
|
||||
|
||||
uint8_t *outptr = dp;
|
||||
|
||||
SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth);
|
||||
|
||||
for (int y = 0 ; y < dst_height ; y++)
|
||||
{
|
||||
// time to fill in a scanline of the blurry rectangle.
|
||||
// to avoid floating point math, everything is multiplied by
|
||||
// 2 where needed. This keeps things nice and integer-oriented.
|
||||
|
||||
int dy = abs((y << 1) - dst_height) - h; // how far are we from the original edge?
|
||||
int oy = dy >> 1;
|
||||
if (oy < 0) oy = 0;
|
||||
|
||||
unsigned int profile_y = profile[oy];
|
||||
|
||||
for (int x = 0 ; x < (dst_width << 1) ; x += 2) {
|
||||
int dx = abs( x - dst_width ) - w;
|
||||
int ox = dx >> 1;
|
||||
if (ox < 0) ox = 0;
|
||||
|
||||
unsigned int maskval = SkMulDiv255Round(profile[ox], profile_y);
|
||||
for (int x = 0 ; x < dstWidth ; ++x) {
|
||||
if (profile_size <= sw) {
|
||||
horizontalScanline[x] = profile_lookup(profile, x, dstWidth, w);
|
||||
} else {
|
||||
float span = float(sw)/radius;
|
||||
float giX = 1.5 - (x+.5)/radius;
|
||||
horizontalScanline[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0 ; y < dstHeight ; ++y) {
|
||||
unsigned int profile_y;
|
||||
if (profile_size <= sh) {
|
||||
profile_y = profile_lookup(profile, y, dstHeight, h);
|
||||
} else {
|
||||
float span = float(sh)/radius;
|
||||
float giY = 1.5 - (y+.5)/radius;
|
||||
profile_y = (uint8_t) (255 * (gaussianIntegral(giY) - gaussianIntegral(giY + span)));
|
||||
}
|
||||
|
||||
for (int x = 0 ; x < dstWidth ; x++) {
|
||||
unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], profile_y);
|
||||
*(outptr++) = maskval;
|
||||
}
|
||||
}
|
||||
|
||||
if (style == kInner_Style) {
|
||||
// now we allocate the "real" dst, mirror the size of src
|
||||
size_t srcSize = src.width() * src.height();
|
||||
if (0 == srcSize) {
|
||||
return false; // too big to allocate, abort
|
||||
}
|
||||
dst->fImage = SkMask::AllocImage(srcSize);
|
||||
for (int y = 0 ; y < sh ; y++) {
|
||||
uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad;
|
||||
uint8_t *inner_scanline = dst->fImage + y*sw;
|
||||
memcpy(inner_scanline, blur_scanline, sw);
|
||||
}
|
||||
SkMask::FreeImage(dp);
|
||||
|
||||
dst->fBounds.set(0, 0, sw, sh); // restore trimmed bounds
|
||||
dst->fRowBytes = sw;
|
||||
|
||||
} else if (style == kOuter_Style) {
|
||||
for (int y = pad ; y < dstHeight-pad ; y++) {
|
||||
uint8_t *dst_scanline = dp + y*dstWidth + pad;
|
||||
memset(dst_scanline, 0, sw);
|
||||
}
|
||||
}
|
||||
// normal and solid styles are the same for analytic rect blurs, so don't
|
||||
// need to handle solid specially.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The "simple" blur is a direct implementation of separable convolution with a discrete
|
||||
// gaussian kernel. It's "ground truth" in a sense; too slow to be used, but very
|
||||
// useful for correctness comparisons.
|
||||
|
||||
bool SkBlurMask::BlurGroundTruth(SkMask* dst, const SkMask& src, SkScalar provided_radius,
|
||||
Style style, SkIPoint* margin) {
|
||||
|
||||
if (src.fFormat != SkMask::kA8_Format) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float radius = SkScalarToFloat(SkScalarMul(provided_radius, kBlurRadiusFudgeFactor));
|
||||
float stddev = SkScalarToFloat(radius) /2.0f;
|
||||
float variance = stddev * stddev;
|
||||
|
||||
int windowSize = SkScalarCeil(stddev*4);
|
||||
// round window size up to nearest odd number
|
||||
windowSize |= 1;
|
||||
|
||||
SkAutoTMalloc<float> gaussWindow(windowSize);
|
||||
|
||||
int halfWindow = windowSize >> 1;
|
||||
|
||||
gaussWindow[halfWindow] = 1;
|
||||
|
||||
float windowSum = 1;
|
||||
for (int x = 1 ; x <= halfWindow ; ++x) {
|
||||
float gaussian = expf(-x*x / variance);
|
||||
gaussWindow[halfWindow + x] = gaussWindow[halfWindow-x] = gaussian;
|
||||
windowSum += 2*gaussian;
|
||||
}
|
||||
|
||||
// leave the filter un-normalized for now; we will divide by the normalization
|
||||
// sum later;
|
||||
|
||||
int pad = halfWindow;
|
||||
if (margin) {
|
||||
margin->set( pad, pad );
|
||||
}
|
||||
|
||||
dst->fBounds = src.fBounds;
|
||||
dst->fBounds.outset(pad, pad);
|
||||
|
||||
dst->fRowBytes = dst->fBounds.width();
|
||||
dst->fFormat = SkMask::kA8_Format;
|
||||
dst->fImage = NULL;
|
||||
|
||||
if (src.fImage) {
|
||||
|
||||
size_t dstSize = dst->computeImageSize();
|
||||
if (0 == dstSize) {
|
||||
return false; // too big to allocate, abort
|
||||
}
|
||||
|
||||
int srcWidth = src.fBounds.width();
|
||||
int srcHeight = src.fBounds.height();
|
||||
int dstWidth = dst->fBounds.width();
|
||||
|
||||
const uint8_t* srcPixels = src.fImage;
|
||||
uint8_t* dstPixels = SkMask::AllocImage(dstSize);
|
||||
SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dstPixels);
|
||||
|
||||
// do the actual blur. First, make a padded copy of the source.
|
||||
// use double pad so we never have to check if we're outside anything
|
||||
|
||||
int padWidth = srcWidth + 4*pad;
|
||||
int padHeight = srcHeight;
|
||||
int padSize = padWidth * padHeight;
|
||||
|
||||
SkAutoTMalloc<uint8_t> padPixels(padSize);
|
||||
memset(padPixels, 0, padSize);
|
||||
|
||||
for (int y = 0 ; y < srcHeight; ++y) {
|
||||
uint8_t* padptr = padPixels + y * padWidth + 2*pad;
|
||||
const uint8_t* srcptr = srcPixels + y * srcWidth;
|
||||
memcpy(padptr, srcptr, srcWidth);
|
||||
}
|
||||
|
||||
// blur in X, transposing the result into a temporary floating point buffer.
|
||||
// also double-pad the intermediate result so that the second blur doesn't
|
||||
// have to do extra conditionals.
|
||||
|
||||
int tmpWidth = padHeight + 4*pad;
|
||||
int tmpHeight = padWidth - 2*pad;
|
||||
int tmpSize = tmpWidth * tmpHeight;
|
||||
|
||||
SkAutoTMalloc<float> tmpImage(tmpSize);
|
||||
memset(tmpImage, 0, tmpSize*sizeof(tmpImage[0]));
|
||||
|
||||
for (int y = 0 ; y < padHeight ; ++y) {
|
||||
uint8_t *srcScanline = padPixels + y*padWidth;
|
||||
for (int x = pad ; x < padWidth - pad ; ++x) {
|
||||
float *outPixel = tmpImage + (x-pad)*tmpWidth + y + 2*pad; // transposed output
|
||||
uint8_t *windowCenter = srcScanline + x;
|
||||
for (int i = -pad ; i <= pad ; ++i) {
|
||||
*outPixel += gaussWindow[pad+i]*windowCenter[i];
|
||||
}
|
||||
*outPixel /= windowSum;
|
||||
}
|
||||
}
|
||||
|
||||
// blur in Y; now filling in the actual desired destination. We have to do
|
||||
// the transpose again; these transposes guarantee that we read memory in
|
||||
// linear order.
|
||||
|
||||
for (int y = 0 ; y < tmpHeight ; ++y) {
|
||||
float *srcScanline = tmpImage + y*tmpWidth;
|
||||
for (int x = pad ; x < tmpWidth - pad ; ++x) {
|
||||
float *windowCenter = srcScanline + x;
|
||||
float finalValue = 0;
|
||||
for (int i = -pad ; i <= pad ; ++i) {
|
||||
finalValue += gaussWindow[pad+i]*windowCenter[i];
|
||||
}
|
||||
finalValue /= windowSum;
|
||||
uint8_t *outPixel = dstPixels + (x-pad)*dstWidth + y; // transposed output
|
||||
int integerPixel = int(finalValue + 0.5f);
|
||||
*outPixel = SkClampMax( SkClampPos(integerPixel), 255 );
|
||||
}
|
||||
}
|
||||
|
||||
dst->fImage = dstPixels;
|
||||
// if need be, alloc the "real" dst (same size as src) and copy/merge
|
||||
// the blur into it (applying the src)
|
||||
if (style == kInner_Style) {
|
||||
// now we allocate the "real" dst, mirror the size of src
|
||||
size_t srcSize = src.computeImageSize();
|
||||
if (0 == srcSize) {
|
||||
return false; // too big to allocate, abort
|
||||
}
|
||||
dst->fImage = SkMask::AllocImage(srcSize);
|
||||
merge_src_with_blur(dst->fImage, src.fRowBytes,
|
||||
srcPixels, src.fRowBytes,
|
||||
dstPixels + pad*dst->fRowBytes + pad,
|
||||
dst->fRowBytes, srcWidth, srcHeight);
|
||||
SkMask::FreeImage(dstPixels);
|
||||
} else if (style != kNormal_Style) {
|
||||
clamp_with_orig(dstPixels + pad*dst->fRowBytes + pad,
|
||||
dst->fRowBytes, srcPixels, src.fRowBytes, srcWidth, srcHeight, style);
|
||||
}
|
||||
(void)autoCall.detach();
|
||||
}
|
||||
|
||||
if (style == kInner_Style) {
|
||||
dst->fBounds = src.fBounds; // restore trimmed bounds
|
||||
dst->fRowBytes = src.fRowBytes;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -29,15 +29,23 @@ public:
|
||||
};
|
||||
|
||||
static bool BlurRect(SkMask *dst, const SkRect &src,
|
||||
SkScalar radius, Style style, Quality quality,
|
||||
SkScalar radius, Style style,
|
||||
SkIPoint *margin = NULL);
|
||||
|
||||
static bool Blur(SkMask* dst, const SkMask& src,
|
||||
SkScalar radius, Style style, Quality quality,
|
||||
SkIPoint* margin = NULL);
|
||||
static bool BlurSeparable(SkMask* dst, const SkMask& src,
|
||||
SkScalar radius, Style style, Quality quality,
|
||||
SkIPoint* margin = NULL);
|
||||
|
||||
|
||||
// the "ground truth" blur does a gaussian convolution; it's slow
|
||||
// but useful for comparison purposes.
|
||||
|
||||
static bool BlurGroundTruth(SkMask* dst, const SkMask& src,
|
||||
SkScalar provided_radius, Style style,
|
||||
SkIPoint* margin = NULL);
|
||||
|
||||
private:
|
||||
static bool Blur(SkMask* dst, const SkMask& src,
|
||||
SkScalar radius, Style style, Quality quality,
|
||||
|
Loading…
Reference in New Issue
Block a user