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