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 "SkBlurMask.h"
9 #include "SkCanvas.h"
10 #include "SkMaskFilter.h"
11 #include "SkPath.h"
12 #include "SkTo.h"
13 #include "gm.h"
14 
15 #define STROKE_WIDTH    SkIntToScalar(10)
16 
17 typedef void (*Proc)(SkCanvas*, const SkRect&, const SkPaint&);
18 
fill_rect(SkCanvas * canvas,const SkRect & r,const SkPaint & p)19 static void fill_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
20     canvas->drawRect(r, p);
21 }
22 
draw_donut(SkCanvas * canvas,const SkRect & r,const SkPaint & p)23 static void draw_donut(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
24     SkRect  rect;
25     SkPath  path;
26 
27     rect = r;
28     rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2);
29     path.addRect(rect);
30     rect = r;
31     rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
32 
33     path.addRect(rect);
34     path.setFillType(SkPath::kEvenOdd_FillType);
35 
36     canvas->drawPath(path, p);
37 }
38 
draw_donut_skewed(SkCanvas * canvas,const SkRect & r,const SkPaint & p)39 static void draw_donut_skewed(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
40     SkRect  rect;
41     SkPath  path;
42 
43     rect = r;
44     rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2);
45     path.addRect(rect);
46     rect = r;
47     rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
48 
49     rect.offset(7, -7);
50 
51     path.addRect(rect);
52     path.setFillType(SkPath::kEvenOdd_FillType);
53 
54     canvas->drawPath(path, p);
55 }
56 
57 #include "SkGradientShader.h"
58 
59 /*
60  * Spits out a dummy gradient to test blur with shader on paint
61  */
make_radial()62 static sk_sp<SkShader> make_radial() {
63     SkPoint pts[2] = {
64         { 0, 0 },
65         { SkIntToScalar(100), SkIntToScalar(100) }
66     };
67     SkShader::TileMode tm = SkShader::kClamp_TileMode;
68     const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, };
69     const SkScalar pos[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
70     SkMatrix scale;
71     scale.setScale(0.5f, 0.5f);
72     scale.postTranslate(25.f, 25.f);
73     SkPoint center0, center1;
74     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
75                 SkScalarAve(pts[0].fY, pts[1].fY));
76     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
77                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
78     return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
79                                                  center0, (pts[1].fX - pts[0].fX) / 2,
80                                                  colors, pos, SK_ARRAY_COUNT(colors), tm,
81                                                  0, &scale);
82 }
83 
84 typedef void (*PaintProc)(SkPaint*, SkScalar width);
85 
86 class BlurRectGM : public skiagm::GM {
87       sk_sp<SkMaskFilter> fMaskFilters[kLastEnum_SkBlurStyle + 1];
88       SkString  fName;
89       SkAlpha   fAlpha;
90 public:
BlurRectGM(const char name[],U8CPU alpha)91     BlurRectGM(const char name[], U8CPU alpha)
92         : fName(name)
93         , fAlpha(SkToU8(alpha)) {
94     }
95 
96 protected:
onOnceBeforeDraw()97     void onOnceBeforeDraw() override {
98         for (int i = 0; i <= kLastEnum_SkBlurStyle; ++i) {
99             fMaskFilters[i] = SkMaskFilter::MakeBlur((SkBlurStyle)i,
100                                   SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(STROKE_WIDTH/2)));
101         }
102     }
103 
onShortName()104     SkString onShortName() override {
105         return fName;
106     }
107 
onISize()108     SkISize onISize() override {
109         return SkISize::Make(860, 820);
110     }
111 
onDraw(SkCanvas * canvas)112     void onDraw(SkCanvas* canvas) 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(make_radial());
127 
128                 constexpr 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 private:
drawProcs(SkCanvas * canvas,const SkRect & r,const SkPaint & paint,bool doClip,const Proc procs[],size_t procsCount)151     void drawProcs(SkCanvas* canvas, const SkRect& r, const SkPaint& paint,
152                    bool doClip, const Proc procs[], size_t procsCount) {
153         SkAutoCanvasRestore acr(canvas, true);
154         for (size_t i = 0; i < procsCount; ++i) {
155             if (doClip) {
156                 SkRect clipRect(r);
157                 clipRect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
158                 canvas->save();
159                 canvas->clipRect(r);
160             }
161             procs[i](canvas, r, paint);
162             if (doClip) {
163                 canvas->restore();
164             }
165             canvas->translate(0, r.height() * 4/3);
166         }
167     }
168 private:
169     typedef GM INHERITED;
170 };
171 
172 DEF_SIMPLE_GM(blurrect_gallery, canvas, 1200, 1024) {
173         const int fGMWidth = 1200;
174         const int fPadding = 10;
175         const int fMargin = 100;
176 
177         const int widths[] = {25, 5, 5, 100, 150, 25};
178         const int heights[] = {100, 100, 5, 25, 150, 25};
179         const SkBlurStyle styles[] = {kNormal_SkBlurStyle, kInner_SkBlurStyle, kOuter_SkBlurStyle};
180         const float radii[] = {20, 5, 10};
181 
182         canvas->translate(50,20);
183 
184         int cur_x = 0;
185         int cur_y = 0;
186 
187         int max_height = 0;
188 
189         for (size_t i = 0 ; i < SK_ARRAY_COUNT(widths) ; i++) {
190             int width = widths[i];
191             int height = heights[i];
192             SkRect r;
193             r.setWH(SkIntToScalar(width), SkIntToScalar(height));
194             SkAutoCanvasRestore autoRestore(canvas, true);
195 
196             for (size_t j = 0 ; j < SK_ARRAY_COUNT(radii) ; j++) {
197                 float radius = radii[j];
198                 for (size_t k = 0 ; k < SK_ARRAY_COUNT(styles) ; k++) {
199                     SkBlurStyle style = styles[k];
200 
201                     SkMask mask;
202                     if (!SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(radius),
203                                               &mask, r, style)) {
204                         continue;
205                     }
206 
207                     SkAutoMaskFreeImage amfi(mask.fImage);
208 
209                     SkBitmap bm;
210                     bm.installMaskPixels(mask);
211 
212                     if (cur_x + bm.width() >= fGMWidth - fMargin) {
213                         cur_x = 0;
214                         cur_y += max_height + fPadding;
215                         max_height = 0;
216                     }
217 
218                     canvas->save();
219                     canvas->translate((SkScalar)cur_x, (SkScalar)cur_y);
220                     canvas->translate(-(bm.width() - r.width())/2, -(bm.height()-r.height())/2);
221                     canvas->drawBitmap(bm, 0.f, 0.f, nullptr);
222                     canvas->restore();
223 
224                     cur_x += bm.width() + fPadding;
225                     if (bm.height() > max_height)
226                         max_height = bm.height();
227                 }
228             }
229         }
230 }
231 
232 //////////////////////////////////////////////////////////////////////////////
233 
234 DEF_GM(return new BlurRectGM("blurrects", 0xFF);)
235