/* * 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 "SkBlurMask.h" #include "SkBlurMaskFilter.h" #include "SkCanvas.h" #include "SkPath.h" #define STROKE_WIDTH SkIntToScalar(10) typedef void (*Proc)(SkCanvas*, const SkRect&, const SkPaint&); static void fill_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { canvas->drawRect(r, p); } static void draw_donut(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { SkRect rect; SkPath path; rect = r; rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2); path.addRect(rect); rect = r; rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2); path.addRect(rect); path.setFillType(SkPath::kEvenOdd_FillType); canvas->drawPath(path, p); } static void draw_donut_skewed(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { SkRect rect; SkPath path; rect = r; rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2); path.addRect(rect); rect = r; rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2); rect.offset(7, -7); path.addRect(rect); path.setFillType(SkPath::kEvenOdd_FillType); canvas->drawPath(path, p); } #include "SkGradientShader.h" typedef void (*PaintProc)(SkPaint*, SkScalar width); class BlurRectGM : public skiagm::GM { SkAutoTUnref fMaskFilters[SkBlurMaskFilter::kBlurStyleCount]; SkString fName; SkAlpha fAlpha; public: BlurRectGM(const char name[], U8CPU alpha) : fName(name) , fAlpha(SkToU8(alpha)) { } protected: virtual void onOnceBeforeDraw() SK_OVERRIDE { for (int i = 0; i < SkBlurMaskFilter::kBlurStyleCount; ++i) { fMaskFilters[i].reset(SkBlurMaskFilter::Create((SkBlurMaskFilter::BlurStyle) i, SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(STROKE_WIDTH/2)), SkBlurMaskFilter::kHighQuality_BlurFlag)); } } virtual SkString onShortName() { return fName; } virtual SkISize onISize() { return SkISize::Make(440, 820); } virtual void onDraw(SkCanvas* canvas) { canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2); SkRect r = { 0, 0, 100, 50 }; SkScalar scales[] = { SK_Scalar1, 0.6f }; for (size_t s = 0; s < SK_ARRAY_COUNT(scales); ++s) { canvas->save(); for (size_t f = 0; f < SK_ARRAY_COUNT(fMaskFilters); ++f) { SkPaint paint; paint.setMaskFilter(fMaskFilters[f]); paint.setAlpha(fAlpha); static const Proc procs[] = { fill_rect, draw_donut, draw_donut_skewed }; canvas->save(); canvas->scale(scales[s], scales[s]); this->drawProcs(canvas, r, paint, false, procs, SK_ARRAY_COUNT(procs)); canvas->translate(r.width() * 4/3, 0); this->drawProcs(canvas, r, paint, true, procs, SK_ARRAY_COUNT(procs)); canvas->restore(); canvas->translate(0, SK_ARRAY_COUNT(procs) * r.height() * 4/3 * scales[s]); } canvas->restore(); canvas->translate(2 * r.width() * 4/3 * scales[s], 0); } } virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; } private: void drawProcs(SkCanvas* canvas, const SkRect& r, const SkPaint& paint, bool doClip, const Proc procs[], size_t procsCount) { SkAutoCanvasRestore acr(canvas, true); for (size_t i = 0; i < procsCount; ++i) { if (doClip) { SkRect clipRect(r); clipRect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2); canvas->save(); canvas->clipRect(r); } procs[i](canvas, r, paint); if (doClip) { canvas->restore(); } canvas->translate(0, r.height() * 4/3); } } private: typedef GM INHERITED; }; 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, 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; } SkBlurMask::Style style() const { return fStyle; } protected: virtual SkString onShortName() { return fName; } virtual SkISize onISize() { return SkISize::Make(640, 480); } virtual bool makeMask(SkMask *m, const SkRect&) = 0; virtual void onDraw(SkCanvas* canvas) { SkRect r; r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight)); SkISize canvas_size = canvas->getDeviceSize(); int center_x = (canvas_size.fWidth - (int)(r.width()))/2; int center_y = (canvas_size.fHeight - (int)(r.height()))/2; SkMask mask; if (!this->makeMask(&mask, r)) { SkPaint paint; r.offset( SkIntToScalar(center_x), SkIntToScalar(center_y) ); canvas->drawRect(r,paint); return; } SkAutoMaskFreeImage amfi(mask.fImage); SkBitmap bm; bm.installMaskPixels(mask); center_x = (canvas_size.fWidth - mask.fBounds.width())/2; center_y = (canvas_size.fHeight - mask.fBounds.height())/2; canvas->drawBitmap(bm, SkIntToScalar(center_x), SkIntToScalar(center_y), NULL); } virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; } private: typedef GM INHERITED; }; class BlurRectFastGM: public BlurRectCompareGM { public: BlurRectFastGM(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 { return SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(this->radius()), m, r, this->style()); } private: typedef BlurRectCompareGM INHERITED; }; class BlurRectSlowGM: public BlurRectCompareGM { public: BlurRectSlowGM(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::BoxBlur(m, src, SkBlurMask::ConvertRadiusToSigma(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(SkBlurMask::ConvertRadiusToSigma(this->radius()), m, src, this->style()); } virtual SkBlurMask::Quality getQuality() { return SkBlurMask::kHigh_Quality; } private: typedef BlurRectCompareGM INHERITED; }; ////////////////////////////////////////////////////////////////////////////// DEF_GM(return new BlurRectGM("blurrects", 0xFF);) static const SkScalar kBig = 20; static const SkScalar kSmall = 2; // regular size rects, blurs should be small enough not to completely overlap. DEF_GM(return new BlurRectFastGM( "blurrect_25_100_2_normal_fast", 25, 100, kSmall, SkBlurMask::kNormal_Style);) DEF_GM(return new BlurRectFastGM("blurrect_25_100_20_normal_fast", 25, 100, kBig, SkBlurMask::kNormal_Style);) DEF_GM(return new BlurRectSlowGM( "blurrect_25_100_2_normal_slow", 25, 100, kSmall, SkBlurMask::kNormal_Style);) DEF_GM(return new BlurRectSlowGM("blurrect_25_100_20_normal_slow", 25, 100, kBig, SkBlurMask::kNormal_Style);) DEF_GM(return new BlurRectFastGM( "blurrect_25_100_2_inner_fast", 25, 100, kSmall, SkBlurMask::kInner_Style);) DEF_GM(return new BlurRectFastGM("blurrect_25_100_20_inner_fast", 25, 100, kBig, SkBlurMask::kInner_Style);) DEF_GM(return new BlurRectSlowGM( "blurrect_25_100_2_inner_slow", 25, 100, kSmall, SkBlurMask::kInner_Style);) DEF_GM(return new BlurRectSlowGM("blurrect_25_100_20_inner_slow", 25, 100, kBig, SkBlurMask::kInner_Style);) DEF_GM(return new BlurRectFastGM( "blurrect_25_100_2_outer_fast", 25, 100, kSmall, SkBlurMask::kOuter_Style);) DEF_GM(return new BlurRectFastGM("blurrect_25_100_20_outer_fast", 25, 100, kBig, SkBlurMask::kOuter_Style);) DEF_GM(return new BlurRectSlowGM( "blurrect_25_100_2_outer_slow", 25, 100, kSmall, SkBlurMask::kOuter_Style);) DEF_GM(return new BlurRectSlowGM("blurrect_25_100_20_outer_slow", 25, 100, kBig, 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, kSmall, SkBlurMask::kNormal_Style);) DEF_GM(return new BlurRectFastGM("blurrect_5_100_20_normal_fast", 5, 100, kBig, SkBlurMask::kNormal_Style);) DEF_GM(return new BlurRectSlowGM( "blurrect_5_100_2_normal_slow", 5, 100, kSmall, SkBlurMask::kNormal_Style);) DEF_GM(return new BlurRectSlowGM("blurrect_5_100_20_normal_slow", 5, 100, kBig, SkBlurMask::kNormal_Style);) DEF_GM(return new BlurRectFastGM( "blurrect_5_100_2_inner_fast", 5, 100, kSmall, SkBlurMask::kInner_Style);) DEF_GM(return new BlurRectFastGM("blurrect_5_100_20_inner_fast", 5, 100, kBig, SkBlurMask::kInner_Style);) DEF_GM(return new BlurRectSlowGM( "blurrect_5_100_2_inner_slow", 5, 100, kSmall, SkBlurMask::kInner_Style);) DEF_GM(return new BlurRectSlowGM("blurrect_5_100_20_inner_slow", 5, 100, kBig, SkBlurMask::kInner_Style);) DEF_GM(return new BlurRectFastGM( "blurrect_5_100_2_outer_fast", 5, 100, kSmall, SkBlurMask::kOuter_Style);) DEF_GM(return new BlurRectFastGM("blurrect_5_100_20_outer_fast", 5, 100, kBig, SkBlurMask::kOuter_Style);) DEF_GM(return new BlurRectSlowGM( "blurrect_5_100_2_outer_slow", 5, 100, kSmall, SkBlurMask::kOuter_Style);) DEF_GM(return new BlurRectSlowGM("blurrect_5_100_20_outer_slow", 5, 100, kBig, SkBlurMask::kOuter_Style);) // tiny rects, blurs overlap in X and Y DEF_GM(return new BlurRectFastGM( "blurrect_5_5_2_normal_fast", 5, 5, kSmall, SkBlurMask::kNormal_Style);) DEF_GM(return new BlurRectFastGM("blurrect_5_5_20_normal_fast", 5, 5, kBig, SkBlurMask::kNormal_Style);) DEF_GM(return new BlurRectSlowGM( "blurrect_5_5_2_normal_slow", 5, 5, kSmall, SkBlurMask::kNormal_Style);) DEF_GM(return new BlurRectSlowGM("blurrect_5_5_20_normal_slow", 5, 5, kBig, SkBlurMask::kNormal_Style);) DEF_GM(return new BlurRectFastGM( "blurrect_5_5_2_inner_fast", 5, 5, kSmall, SkBlurMask::kInner_Style);) DEF_GM(return new BlurRectFastGM("blurrect_5_5_20_inner_fast", 5, 5, kBig, SkBlurMask::kInner_Style);) DEF_GM(return new BlurRectSlowGM( "blurrect_5_5_2_inner_slow", 5, 5, kSmall, SkBlurMask::kInner_Style);) DEF_GM(return new BlurRectSlowGM("blurrect_5_5_20_inner_slow", 5, 5, kBig, SkBlurMask::kInner_Style);) DEF_GM(return new BlurRectFastGM( "blurrect_5_5_2_outer_fast", 5, 5, kSmall, SkBlurMask::kOuter_Style);) DEF_GM(return new BlurRectFastGM("blurrect_5_5_20_outer_fast", 5, 5, kBig, SkBlurMask::kOuter_Style);) DEF_GM(return new BlurRectSlowGM( "blurrect_5_5_2_outer_slow", 5, 5, kSmall, SkBlurMask::kOuter_Style);) DEF_GM(return new BlurRectSlowGM("blurrect_5_5_20_outer_slow", 5, 5, kBig, 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