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