blurrect.cpp revision 72c9faab45124e08c85f70ca38536914862d947c
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 "SkBlurMask.h"
10#include "SkBlurMaskFilter.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
58/*
59 * Spits out a dummy gradient to test blur with shader on paint
60 */
61static SkShader* MakeRadial() {
62    SkPoint pts[2] = {
63        { 0, 0 },
64        { SkIntToScalar(100), SkIntToScalar(100) }
65    };
66    SkShader::TileMode tm = SkShader::kClamp_TileMode;
67    const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, };
68    const SkScalar pos[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
69    SkMatrix scale;
70    scale.setScale(0.5f, 0.5f);
71    scale.postTranslate(25.f, 25.f);
72    SkPoint center0, center1;
73    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
74                SkScalarAve(pts[0].fY, pts[1].fY));
75    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
76                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
77    return SkGradientShader::CreateTwoPointRadial(center1, (pts[1].fX - pts[0].fX) / 7,
78                                                  center0, (pts[1].fX - pts[0].fX) / 2,
79                                                  colors, pos, SK_ARRAY_COUNT(colors), tm,
80                                                  0, &scale);
81}
82
83typedef void (*PaintProc)(SkPaint*, SkScalar width);
84
85class BlurRectGM : public skiagm::GM {
86      SkAutoTUnref<SkMaskFilter> fMaskFilters[kLastEnum_SkBlurStyle + 1];
87      SkString  fName;
88      SkAlpha   fAlpha;
89public:
90    BlurRectGM(const char name[], U8CPU alpha)
91        : fName(name)
92        , fAlpha(SkToU8(alpha)) {
93    }
94
95protected:
96    void onOnceBeforeDraw() SK_OVERRIDE {
97        for (int i = 0; i <= kLastEnum_SkBlurStyle; ++i) {
98            fMaskFilters[i].reset(SkBlurMaskFilter::Create((SkBlurStyle)i,
99                                  SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(STROKE_WIDTH/2)),
100                                  SkBlurMaskFilter::kHighQuality_BlurFlag));
101        }
102    }
103
104    SkString onShortName() SK_OVERRIDE {
105        return fName;
106    }
107
108    SkISize onISize() SK_OVERRIDE {
109        return SkISize::Make(860, 820);
110    }
111
112    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
113        canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2);
114
115        SkRect  r = { 0, 0, 100, 50 };
116        SkScalar scales[] = { SK_Scalar1, 0.6f };
117
118        for (size_t s = 0; s < SK_ARRAY_COUNT(scales); ++s) {
119            canvas->save();
120            for (size_t f = 0; f < SK_ARRAY_COUNT(fMaskFilters); ++f) {
121                SkPaint paint;
122                paint.setMaskFilter(fMaskFilters[f]);
123                paint.setAlpha(fAlpha);
124
125                SkPaint paintWithRadial = paint;
126                paintWithRadial.setShader(MakeRadial())->unref();
127
128                static const Proc procs[] = {
129                    fill_rect, draw_donut, draw_donut_skewed
130                };
131
132                canvas->save();
133                canvas->scale(scales[s], scales[s]);
134                this->drawProcs(canvas, r, paint, false, procs, SK_ARRAY_COUNT(procs));
135                canvas->translate(r.width() * 4/3, 0);
136                this->drawProcs(canvas, r, paintWithRadial, false, procs, SK_ARRAY_COUNT(procs));
137                canvas->translate(r.width() * 4/3, 0);
138                this->drawProcs(canvas, r, paint, true, procs, SK_ARRAY_COUNT(procs));
139                canvas->translate(r.width() * 4/3, 0);
140                this->drawProcs(canvas, r, paintWithRadial, true, procs, SK_ARRAY_COUNT(procs));
141                canvas->restore();
142
143                canvas->translate(0, SK_ARRAY_COUNT(procs) * r.height() * 4/3 * scales[s]);
144            }
145            canvas->restore();
146            canvas->translate(4 * r.width() * 4/3 * scales[s], 0);
147        }
148    }
149
150    uint32_t onGetFlags() const SK_OVERRIDE { return kSkipPipe_Flag | kSkipTiled_Flag; }
151
152private:
153    void drawProcs(SkCanvas* canvas, const SkRect& r, const SkPaint& paint,
154                   bool doClip, const Proc procs[], size_t procsCount) {
155        SkAutoCanvasRestore acr(canvas, true);
156        for (size_t i = 0; i < procsCount; ++i) {
157            if (doClip) {
158                SkRect clipRect(r);
159                clipRect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
160                canvas->save();
161                canvas->clipRect(r);
162            }
163            procs[i](canvas, r, paint);
164            if (doClip) {
165                canvas->restore();
166            }
167            canvas->translate(0, r.height() * 4/3);
168        }
169    }
170private:
171    typedef GM INHERITED;
172};
173
174
175class BlurRectDirectGM : public skiagm::GM {
176    SkString  fName;
177    int fGMWidth, fGMHeight;
178    int fPadding, fMargin;
179public:
180    BlurRectDirectGM(const char name[])
181        : fName(name),
182          fGMWidth(1200),
183          fGMHeight(1024),
184          fPadding(10),
185          fMargin(100)
186    {
187    }
188
189protected:
190    virtual SkString onShortName() {
191        return fName;
192    }
193
194    virtual SkISize onISize() {
195        return SkISize::Make(fGMWidth, fGMHeight);
196    }
197
198    virtual void onDraw(SkCanvas* canvas) {
199        const int widths[] = {25, 5, 5, 100, 150, 25};
200        const int heights[] = {100, 100, 5, 25, 150, 25};
201        const SkBlurStyle styles[] = {kNormal_SkBlurStyle, kInner_SkBlurStyle, kOuter_SkBlurStyle};
202        const float radii[] = {20, 5, 10};
203
204        canvas->translate(50,20);
205
206        int cur_x = 0;
207        int cur_y = 0;
208
209        int max_height = 0;
210
211        for (size_t i = 0 ; i < SK_ARRAY_COUNT(widths) ; i++) {
212            int width = widths[i];
213            int height = heights[i];
214            SkRect r;
215            r.setWH(SkIntToScalar(width), SkIntToScalar(height));
216            SkAutoCanvasRestore autoRestore(canvas, true);
217
218            for (size_t j = 0 ; j < SK_ARRAY_COUNT(radii) ; j++) {
219                float radius = radii[j];
220                for (size_t k = 0 ; k < SK_ARRAY_COUNT(styles) ; k++) {
221                    SkBlurStyle style = styles[k];
222
223                    SkMask mask;
224                    SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(radius), &mask, r, style);
225
226                    SkAutoMaskFreeImage amfi(mask.fImage);
227
228                    SkBitmap bm;
229                    bm.installMaskPixels(mask);
230
231                    if (cur_x + bm.width() >= fGMWidth - fMargin) {
232                        cur_x = 0;
233                        cur_y += max_height + fPadding;
234                        max_height = 0;
235                    }
236
237                    canvas->save();
238                    canvas->translate((SkScalar)cur_x, (SkScalar)cur_y);
239                    canvas->translate(-(bm.width() - r.width())/2, -(bm.height()-r.height())/2);
240                    canvas->drawBitmap(bm, 0.f, 0.f, NULL);
241                    canvas->restore();
242
243                    cur_x += bm.width() + fPadding;
244                    if (bm.height() > max_height)
245                        max_height = bm.height();
246                }
247            }
248        }
249    }
250
251    virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
252
253private:
254    typedef GM INHERITED;
255};
256
257class BlurRectCompareGM : public skiagm::GM {
258    SkString  fName;
259    unsigned int fRectWidth, fRectHeight;
260    SkScalar fRadius;
261    SkBlurStyle fStyle;
262public:
263    BlurRectCompareGM(const char name[], unsigned int rectWidth,
264                      unsigned int rectHeight, float radius,
265                      SkBlurStyle style)
266        : fName(name)
267        , fRectWidth(rectWidth)
268        , fRectHeight(rectHeight)
269        , fRadius(radius)
270        , fStyle(style) {
271    }
272    int width() const {
273        return fRectWidth;
274    }
275    int height() const {
276        return fRectHeight;
277    }
278    SkScalar radius() const {
279        return fRadius;
280    }
281    SkBlurStyle style() const {
282        return fStyle;
283    }
284
285protected:
286    virtual SkString onShortName() {
287        return fName;
288    }
289
290    virtual SkISize onISize() {
291        return SkISize::Make(640, 480);
292    }
293
294    virtual bool makeMask(SkMask *m, const SkRect&) = 0;
295
296    virtual void onDraw(SkCanvas* canvas) {
297        SkRect r;
298        r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight));
299
300        SkISize canvas_size = canvas->getDeviceSize();
301        int center_x = (canvas_size.fWidth - (int)(r.width()))/2;
302        int center_y = (canvas_size.fHeight - (int)(r.height()))/2;
303
304        SkMask mask;
305
306        if (!this->makeMask(&mask, r)) {
307            SkPaint paint;
308            r.offset( SkIntToScalar(center_x), SkIntToScalar(center_y) );
309            canvas->drawRect(r,paint);
310            return;
311        }
312        SkAutoMaskFreeImage amfi(mask.fImage);
313
314        SkBitmap bm;
315        bm.installMaskPixels(mask);
316
317        center_x = (canvas_size.fWidth - mask.fBounds.width())/2;
318        center_y = (canvas_size.fHeight - mask.fBounds.height())/2;
319
320        canvas->drawBitmap(bm, SkIntToScalar(center_x), SkIntToScalar(center_y), NULL);
321    }
322
323    virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
324
325private:
326    typedef GM INHERITED;
327};
328
329class BlurRectFastGM: public BlurRectCompareGM {
330public:
331    BlurRectFastGM(const char name[], unsigned int rectWidth,
332                   unsigned int rectHeight, float blurRadius,
333                   SkBlurStyle style) :
334        INHERITED(name, rectWidth, rectHeight, blurRadius, style) {
335        }
336
337protected:
338    bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
339        return SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(this->radius()),
340                                    m, r, this->style());
341    }
342private:
343    typedef BlurRectCompareGM INHERITED;
344};
345
346class BlurRectSlowGM: public BlurRectCompareGM {
347public:
348    BlurRectSlowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
349                   float blurRadius, SkBlurStyle style)
350        : INHERITED(name, rectWidth, rectHeight, blurRadius, style) {
351        }
352
353protected:
354    bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
355        SkMask src;
356        r.roundOut(&src.fBounds);
357        src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop);  // move to origin
358        src.fFormat = SkMask::kA8_Format;
359        src.fRowBytes = src.fBounds.width();
360        src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
361        SkAutoMaskFreeImage amfi(src.fImage);
362
363        memset(src.fImage, 0xff, src.computeTotalImageSize());
364
365        return SkBlurMask::BoxBlur(m, src,
366                                   SkBlurMask::ConvertRadiusToSigma(this->radius()),
367                                   this->style(), this->getQuality());
368    }
369
370    virtual SkBlurQuality getQuality() {
371        return kHigh_SkBlurQuality;
372    }
373private:
374    typedef BlurRectCompareGM INHERITED;
375};
376
377class BlurRectSlowLowGM: public BlurRectSlowGM {
378public:
379    BlurRectSlowLowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
380                      float blurRadius, SkBlurStyle style)
381        : INHERITED(name, rectWidth, rectHeight, blurRadius, style) {
382        }
383
384protected:
385    SkBlurQuality getQuality() SK_OVERRIDE {
386        return kLow_SkBlurQuality;
387    }
388private:
389    typedef BlurRectSlowGM INHERITED;
390};
391
392class BlurRectGroundTruthGM: public BlurRectCompareGM {
393public:
394    BlurRectGroundTruthGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
395                          float blurRadius, SkBlurStyle style)
396        : INHERITED(name, rectWidth, rectHeight, blurRadius, style) {
397        }
398
399protected:
400    bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
401        SkMask src;
402        r.roundOut(&src.fBounds);
403        src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop);  // move to origin
404        src.fFormat = SkMask::kA8_Format;
405        src.fRowBytes = src.fBounds.width();
406        src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
407        SkAutoMaskFreeImage amfi(src.fImage);
408
409        memset(src.fImage, 0xff, src.computeTotalImageSize());
410
411        return SkBlurMask::BlurGroundTruth(SkBlurMask::ConvertRadiusToSigma(this->radius()),
412                                           m, src, this->style());
413    }
414
415    virtual SkBlurQuality getQuality() {
416        return kHigh_SkBlurQuality;
417    }
418private:
419    typedef BlurRectCompareGM INHERITED;
420};
421
422
423//////////////////////////////////////////////////////////////////////////////
424
425DEF_GM(return new BlurRectGM("blurrects", 0xFF);)
426DEF_GM(return new BlurRectDirectGM("blurrect_gallery");)
427