1/*
2* Copyright 2012 Google Inc.
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
8#include "gm.h"
9#include "SkBlurMaskFilter.h"
10#include "SkBlurMask.h"
11#include "SkCanvas.h"
12#include "SkPath.h"
13
14#define STROKE_WIDTH    SkIntToScalar(10)
15
16typedef void (*Proc)(SkCanvas*, const SkRect&, const SkPaint&);
17
18static void fill_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
19    canvas->drawRect(r, p);
20}
21
22static void draw_donut(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
23    SkRect  rect;
24    SkPath  path;
25
26    rect = r;
27    rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2);
28    path.addRect(rect);
29    rect = r;
30    rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
31
32    path.addRect(rect);
33    path.setFillType(SkPath::kEvenOdd_FillType);
34
35    canvas->drawPath(path, p);
36}
37
38static void draw_donut_skewed(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
39    SkRect  rect;
40    SkPath  path;
41
42    rect = r;
43    rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2);
44    path.addRect(rect);
45    rect = r;
46    rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
47
48    rect.offset(7, -7);
49
50    path.addRect(rect);
51    path.setFillType(SkPath::kEvenOdd_FillType);
52
53    canvas->drawPath(path, p);
54}
55
56#include "SkGradientShader.h"
57
58typedef void (*PaintProc)(SkPaint*, SkScalar width);
59
60static const char* gBlurStyle2Name[] = {
61    "normal",
62    "solid",
63    "outer",
64    "inner"
65};
66
67class BlurRectGM : public skiagm::GM {
68      SkAutoTUnref<SkMaskFilter> fMaskFilter;
69      SkString  fName;
70      PaintProc fPProc;
71      SkAlpha   fAlpha;
72public:
73    BlurRectGM(const char name[], PaintProc pproc, U8CPU alpha,
74               SkBlurMaskFilter::BlurStyle bs) :
75        fMaskFilter(SkBlurMaskFilter::Create(STROKE_WIDTH/2, bs,
76                    SkBlurMaskFilter::kHighQuality_BlurFlag))
77                  , fName(name)
78                  , fPProc(pproc)
79                  , fAlpha(SkToU8(alpha)) {
80        fName.appendf("_%s", gBlurStyle2Name[bs]);
81    }
82
83protected:
84    virtual SkString onShortName() {
85        return fName;
86    }
87
88    virtual SkISize onISize() {
89        return SkISize::Make(640, 480);
90    }
91
92    virtual void onDraw(SkCanvas* canvas) {
93        canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2);
94
95        SkRect  r = { 0, 0, 250, 120 };
96
97        SkPaint paint;
98        paint.setMaskFilter(fMaskFilter);
99        if (fPProc) {
100            fPProc(&paint, r.width());
101        }
102        paint.setAlpha(fAlpha);
103
104        static const Proc procs[] = {
105            fill_rect, draw_donut, draw_donut_skewed
106        };
107
108        this->drawProcs(canvas, r, paint, false, procs, SK_ARRAY_COUNT(procs));
109        canvas->translate(r.width() * 4/3, 0);
110        this->drawProcs(canvas, r, paint, true, procs, SK_ARRAY_COUNT(procs));
111    }
112
113    virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
114
115private:
116    void drawProcs(SkCanvas* canvas, const SkRect& r, const SkPaint& paint,
117                   bool doClip, const Proc procs[], size_t procsCount) {
118        SkAutoCanvasRestore acr(canvas, true);
119        for (size_t i = 0; i < procsCount; ++i) {
120            if (doClip) {
121                SkRect clipRect(r);
122                clipRect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
123                canvas->save();
124                canvas->clipRect(r);
125            }
126            procs[i](canvas, r, paint);
127            if (doClip) {
128                canvas->restore();
129            }
130            canvas->translate(0, r.height() * 4/3);
131        }
132    }
133private:
134    typedef GM INHERITED;
135};
136
137class BlurRectCompareGM : public skiagm::GM {
138    SkString  fName;
139    unsigned int fRectWidth, fRectHeight;
140    SkScalar fRadius;
141    SkBlurMask::Style fStyle;
142public:
143    BlurRectCompareGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, float radius, SkBlurMask::Style style)
144        : fName(name)
145        , fRectWidth(rectWidth)
146        , fRectHeight(rectHeight)
147        , fRadius(radius)
148        , fStyle(style)
149        {}
150
151    int width() const {
152        return fRectWidth;
153    }
154    int height() const {
155        return fRectHeight;
156    }
157    SkScalar radius() const {
158        return fRadius;
159    }
160    SkBlurMask::Style style() const {
161        return fStyle;
162    }
163
164protected:
165    virtual SkString onShortName() {
166        return fName;
167    }
168
169    virtual SkISize onISize() {
170        return SkISize::Make(640, 480);
171    }
172
173    virtual bool makeMask(SkMask *m, const SkRect&) = 0;
174
175    virtual void onDraw(SkCanvas* canvas) {
176        SkRect r;
177        r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight));
178
179        SkISize canvas_size = canvas->getDeviceSize();
180        int center_x = (canvas_size.fWidth - (int)(r.width()))/2;
181        int center_y = (canvas_size.fHeight - (int)(r.height()))/2;
182
183        SkMask mask;
184
185        if (!this->makeMask(&mask, r)) {
186            SkPaint paint;
187            r.offset( SkIntToScalar(center_x), SkIntToScalar(center_y) );
188            canvas->drawRect(r,paint);
189            return;
190        }
191        SkAutoMaskFreeImage amfi(mask.fImage);
192
193        SkBitmap bm;
194        bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height());
195        bm.setPixels(mask.fImage);
196
197        center_x = (canvas_size.fWidth - mask.fBounds.width())/2;
198        center_y = (canvas_size.fHeight - mask.fBounds.height())/2;
199
200        canvas->drawBitmap(bm, SkIntToScalar(center_x), SkIntToScalar(center_y), NULL);
201    }
202
203    virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
204
205private:
206    typedef GM INHERITED;
207};
208
209class BlurRectFastGM: public BlurRectCompareGM {
210public:
211    BlurRectFastGM(const char name[], unsigned int rect_width,
212                   unsigned int rect_height, float blur_radius,
213                   SkBlurMask::Style style) :
214        INHERITED(name, rect_width, rect_height, blur_radius, style)
215        {
216
217        }
218protected:
219    virtual bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
220        return SkBlurMask::BlurRect(m, r, this->radius(), this->style());
221    }
222private:
223    typedef BlurRectCompareGM INHERITED;
224};
225
226class BlurRectSlowGM: public BlurRectCompareGM {
227public:
228    BlurRectSlowGM(const char name[], unsigned int rect_width, unsigned int rect_height,
229                   float blur_radius, SkBlurMask::Style style) :
230        INHERITED(name, rect_width, rect_height, blur_radius, style)
231        {
232
233        }
234protected:
235    virtual bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
236        SkMask src;
237        r.roundOut(&src.fBounds);
238        src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop);  // move to origin
239        src.fFormat = SkMask::kA8_Format;
240        src.fRowBytes = src.fBounds.width();
241        src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
242        SkAutoMaskFreeImage amfi(src.fImage);
243
244        memset(src.fImage, 0xff, src.computeTotalImageSize());
245
246        return SkBlurMask::Blur(m, src, this->radius(), this->style(), this->getQuality());
247    }
248
249    virtual SkBlurMask::Quality getQuality() {
250        return SkBlurMask::kHigh_Quality;
251    }
252private:
253    typedef BlurRectCompareGM INHERITED;
254};
255
256class BlurRectSlowLowGM: public BlurRectSlowGM {
257public:
258    BlurRectSlowLowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
259                      float blurRadius, SkBlurMask::Style style) :
260        INHERITED(name, rectWidth, rectHeight, blurRadius, style)
261        {
262
263        }
264protected:
265    virtual SkBlurMask::Quality getQuality() SK_OVERRIDE {
266        return SkBlurMask::kLow_Quality;
267    }
268private:
269    typedef BlurRectSlowGM INHERITED;
270};
271
272class BlurRectGroundTruthGM: public BlurRectCompareGM {
273public:
274    BlurRectGroundTruthGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
275                          float blurRadius, SkBlurMask::Style style) :
276        INHERITED(name, rectWidth, rectHeight, blurRadius, style)
277        {
278
279        }
280protected:
281    virtual bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
282        SkMask src;
283        r.roundOut(&src.fBounds);
284        src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop);  // move to origin
285        src.fFormat = SkMask::kA8_Format;
286        src.fRowBytes = src.fBounds.width();
287        src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
288        SkAutoMaskFreeImage amfi(src.fImage);
289
290        memset(src.fImage, 0xff, src.computeTotalImageSize());
291
292        return SkBlurMask::BlurGroundTruth(m, src, this->radius(), this->style());
293    }
294
295    virtual SkBlurMask::Quality getQuality() {
296        return SkBlurMask::kHigh_Quality;
297    }
298private:
299    typedef BlurRectCompareGM INHERITED;
300};
301
302
303//////////////////////////////////////////////////////////////////////////////
304
305DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kNormal_BlurStyle);)
306DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kSolid_BlurStyle);)
307DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kOuter_BlurStyle);)
308DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kInner_BlurStyle);)
309
310// regular size rects, blurs should be small enough not to completely overlap.
311
312DEF_GM(return new BlurRectFastGM( "blurrect_25_100_2_normal_fast", 25, 100, 2,  SkBlurMask::kNormal_Style);)
313DEF_GM(return new BlurRectFastGM("blurrect_25_100_20_normal_fast", 25, 100, 20, SkBlurMask::kNormal_Style);)
314DEF_GM(return new BlurRectSlowGM( "blurrect_25_100_2_normal_slow", 25, 100, 2,  SkBlurMask::kNormal_Style);)
315DEF_GM(return new BlurRectSlowGM("blurrect_25_100_20_normal_slow", 25, 100, 20, SkBlurMask::kNormal_Style);)
316DEF_GM(return new BlurRectFastGM( "blurrect_25_100_2_inner_fast", 25, 100, 2,  SkBlurMask::kInner_Style);)
317DEF_GM(return new BlurRectFastGM("blurrect_25_100_20_inner_fast", 25, 100, 20, SkBlurMask::kInner_Style);)
318DEF_GM(return new BlurRectSlowGM( "blurrect_25_100_2_inner_slow", 25, 100, 2,  SkBlurMask::kInner_Style);)
319DEF_GM(return new BlurRectSlowGM("blurrect_25_100_20_inner_slow", 25, 100, 20, SkBlurMask::kInner_Style);)
320DEF_GM(return new BlurRectFastGM( "blurrect_25_100_2_outer_fast", 25, 100, 2,  SkBlurMask::kOuter_Style);)
321DEF_GM(return new BlurRectFastGM("blurrect_25_100_20_outer_fast", 25, 100, 20, SkBlurMask::kOuter_Style);)
322DEF_GM(return new BlurRectSlowGM( "blurrect_25_100_2_outer_slow", 25, 100, 2,  SkBlurMask::kOuter_Style);)
323DEF_GM(return new BlurRectSlowGM("blurrect_25_100_20_outer_slow", 25, 100, 20, SkBlurMask::kOuter_Style);)
324
325// skinny tall rects, blurs overlap in X but not y
326
327DEF_GM(return new BlurRectFastGM( "blurrect_5_100_2_normal_fast", 5, 100, 2 , SkBlurMask::kNormal_Style);)
328DEF_GM(return new BlurRectFastGM("blurrect_5_100_20_normal_fast", 5, 100, 20, SkBlurMask::kNormal_Style);)
329DEF_GM(return new BlurRectSlowGM( "blurrect_5_100_2_normal_slow", 5, 100, 2 , SkBlurMask::kNormal_Style);)
330DEF_GM(return new BlurRectSlowGM("blurrect_5_100_20_normal_slow", 5, 100, 20, SkBlurMask::kNormal_Style);)
331DEF_GM(return new BlurRectFastGM( "blurrect_5_100_2_inner_fast", 5, 100, 2 , SkBlurMask::kInner_Style);)
332DEF_GM(return new BlurRectFastGM("blurrect_5_100_20_inner_fast", 5, 100, 20, SkBlurMask::kInner_Style);)
333DEF_GM(return new BlurRectSlowGM( "blurrect_5_100_2_inner_slow", 5, 100, 2 , SkBlurMask::kInner_Style);)
334DEF_GM(return new BlurRectSlowGM("blurrect_5_100_20_inner_slow", 5, 100, 20, SkBlurMask::kInner_Style);)
335DEF_GM(return new BlurRectFastGM( "blurrect_5_100_2_outer_fast", 5, 100, 2 , SkBlurMask::kOuter_Style);)
336DEF_GM(return new BlurRectFastGM("blurrect_5_100_20_outer_fast", 5, 100, 20, SkBlurMask::kOuter_Style);)
337DEF_GM(return new BlurRectSlowGM( "blurrect_5_100_2_outer_slow", 5, 100, 2 , SkBlurMask::kOuter_Style);)
338DEF_GM(return new BlurRectSlowGM("blurrect_5_100_20_outer_slow", 5, 100, 20, SkBlurMask::kOuter_Style);)
339
340// tiny rects, blurs overlap in X and Y
341
342DEF_GM(return new BlurRectFastGM( "blurrect_5_5_2_normal_fast", 5, 5, 2 , SkBlurMask::kNormal_Style);)
343DEF_GM(return new BlurRectFastGM("blurrect_5_5_20_normal_fast", 5, 5, 20, SkBlurMask::kNormal_Style);)
344DEF_GM(return new BlurRectSlowGM( "blurrect_5_5_2_normal_slow", 5, 5, 2 , SkBlurMask::kNormal_Style);)
345DEF_GM(return new BlurRectSlowGM("blurrect_5_5_20_normal_slow", 5, 5, 20, SkBlurMask::kNormal_Style);)
346DEF_GM(return new BlurRectFastGM( "blurrect_5_5_2_inner_fast", 5, 5, 2 , SkBlurMask::kInner_Style);)
347DEF_GM(return new BlurRectFastGM("blurrect_5_5_20_inner_fast", 5, 5, 20, SkBlurMask::kInner_Style);)
348DEF_GM(return new BlurRectSlowGM( "blurrect_5_5_2_inner_slow", 5, 5, 2 , SkBlurMask::kInner_Style);)
349DEF_GM(return new BlurRectSlowGM("blurrect_5_5_20_inner_slow", 5, 5, 20, SkBlurMask::kInner_Style);)
350DEF_GM(return new BlurRectFastGM( "blurrect_5_5_2_outer_fast", 5, 5, 2 , SkBlurMask::kOuter_Style);)
351DEF_GM(return new BlurRectFastGM("blurrect_5_5_20_outer_fast", 5, 5, 20, SkBlurMask::kOuter_Style);)
352DEF_GM(return new BlurRectSlowGM( "blurrect_5_5_2_outer_slow", 5, 5, 2 , SkBlurMask::kOuter_Style);)
353DEF_GM(return new BlurRectSlowGM("blurrect_5_5_20_outer_slow", 5, 5, 20, SkBlurMask::kOuter_Style);)
354
355
356#if 0
357// dont' need to GM the gaussian convolution; it's slow and intended
358// as a ground truth comparison only.  Leaving these here in case we
359// ever want to turn these back on for debugging reasons.
360DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_1_simple", 25, 100, 1);)
361DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_2_simple", 25, 100, 2);)
362DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_3_simple", 25, 100, 3);)
363DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_4_simple", 25, 100, 4);)
364DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_5_simple", 25, 100, 5);)
365DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_6_simple", 25, 100, 6);)
366DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_7_simple", 25, 100, 7);)
367DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_8_simple", 25, 100, 8);)
368DEF_GM(return new BlurRectGroundTruthGM( "blurrect_25_100_9_simple", 25, 100, 9);)
369DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_10_simple", 25, 100, 10);)
370DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_11_simple", 25, 100, 11);)
371DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_12_simple", 25, 100, 12);)
372DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_13_simple", 25, 100, 13);)
373DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_14_simple", 25, 100, 14);)
374DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_15_simple", 25, 100, 15);)
375DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_16_simple", 25, 100, 16);)
376DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_17_simple", 25, 100, 17);)
377DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_18_simple", 25, 100, 18);)
378DEF_GM(return new BlurRectGroundTruthGM("blurrect_25_100_19_simple", 25, 100, 19);)
379#endif
380