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
16 typedef void (*Proc)(SkCanvas*, const SkRect&, const SkPaint&);
17
fill_rect(SkCanvas * canvas,const SkRect & r,const SkPaint & p)18 static void fill_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
19 canvas->drawRect(r, p);
20 }
21
draw_donut(SkCanvas * canvas,const SkRect & r,const SkPaint & p)22 static 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
draw_donut_skewed(SkCanvas * canvas,const SkRect & r,const SkPaint & p)38 static 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 */
MakeRadial()61 static 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::CreateTwoPointConical(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
83 typedef void (*PaintProc)(SkPaint*, SkScalar width);
84
85 class BlurRectGM : public skiagm::GM {
86 SkAutoTUnref<SkMaskFilter> fMaskFilters[kLastEnum_SkBlurStyle + 1];
87 SkString fName;
88 SkAlpha fAlpha;
89 public:
BlurRectGM(const char name[],U8CPU alpha)90 BlurRectGM(const char name[], U8CPU alpha)
91 : fName(name)
92 , fAlpha(SkToU8(alpha)) {
93 }
94
95 protected:
onOnceBeforeDraw()96 void onOnceBeforeDraw() 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
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(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 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
173 class BlurRectDirectGM : public skiagm::GM {
174 SkString fName;
175 int fGMWidth, fGMHeight;
176 int fPadding, fMargin;
177 public:
BlurRectDirectGM(const char name[])178 BlurRectDirectGM(const char name[])
179 : fName(name),
180 fGMWidth(1200),
181 fGMHeight(1024),
182 fPadding(10),
183 fMargin(100)
184 {
185 }
186
187 protected:
onShortName()188 virtual SkString onShortName() {
189 return fName;
190 }
191
onISize()192 virtual SkISize onISize() {
193 return SkISize::Make(fGMWidth, fGMHeight);
194 }
195
onDraw(SkCanvas * canvas)196 virtual void onDraw(SkCanvas* canvas) {
197 const int widths[] = {25, 5, 5, 100, 150, 25};
198 const int heights[] = {100, 100, 5, 25, 150, 25};
199 const SkBlurStyle styles[] = {kNormal_SkBlurStyle, kInner_SkBlurStyle, kOuter_SkBlurStyle};
200 const float radii[] = {20, 5, 10};
201
202 canvas->translate(50,20);
203
204 int cur_x = 0;
205 int cur_y = 0;
206
207 int max_height = 0;
208
209 for (size_t i = 0 ; i < SK_ARRAY_COUNT(widths) ; i++) {
210 int width = widths[i];
211 int height = heights[i];
212 SkRect r;
213 r.setWH(SkIntToScalar(width), SkIntToScalar(height));
214 SkAutoCanvasRestore autoRestore(canvas, true);
215
216 for (size_t j = 0 ; j < SK_ARRAY_COUNT(radii) ; j++) {
217 float radius = radii[j];
218 for (size_t k = 0 ; k < SK_ARRAY_COUNT(styles) ; k++) {
219 SkBlurStyle style = styles[k];
220
221 SkMask mask;
222 SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(radius), &mask, r, style);
223
224 SkAutoMaskFreeImage amfi(mask.fImage);
225
226 SkBitmap bm;
227 bm.installMaskPixels(mask);
228
229 if (cur_x + bm.width() >= fGMWidth - fMargin) {
230 cur_x = 0;
231 cur_y += max_height + fPadding;
232 max_height = 0;
233 }
234
235 canvas->save();
236 canvas->translate((SkScalar)cur_x, (SkScalar)cur_y);
237 canvas->translate(-(bm.width() - r.width())/2, -(bm.height()-r.height())/2);
238 canvas->drawBitmap(bm, 0.f, 0.f, NULL);
239 canvas->restore();
240
241 cur_x += bm.width() + fPadding;
242 if (bm.height() > max_height)
243 max_height = bm.height();
244 }
245 }
246 }
247 }
248
249 private:
250 typedef GM INHERITED;
251 };
252
253 class BlurRectCompareGM : public skiagm::GM {
254 SkString fName;
255 unsigned int fRectWidth, fRectHeight;
256 SkScalar fRadius;
257 SkBlurStyle fStyle;
258 public:
BlurRectCompareGM(const char name[],unsigned int rectWidth,unsigned int rectHeight,float radius,SkBlurStyle style)259 BlurRectCompareGM(const char name[], unsigned int rectWidth,
260 unsigned int rectHeight, float radius,
261 SkBlurStyle style)
262 : fName(name)
263 , fRectWidth(rectWidth)
264 , fRectHeight(rectHeight)
265 , fRadius(radius)
266 , fStyle(style) {
267 }
width() const268 int width() const {
269 return fRectWidth;
270 }
height() const271 int height() const {
272 return fRectHeight;
273 }
radius() const274 SkScalar radius() const {
275 return fRadius;
276 }
style() const277 SkBlurStyle style() const {
278 return fStyle;
279 }
280
281 protected:
onShortName()282 virtual SkString onShortName() {
283 return fName;
284 }
285
onISize()286 virtual SkISize onISize() {
287 return SkISize::Make(640, 480);
288 }
289
290 virtual bool makeMask(SkMask *m, const SkRect&) = 0;
291
onDraw(SkCanvas * canvas)292 virtual void onDraw(SkCanvas* canvas) {
293 SkRect r;
294 r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight));
295
296 SkISize canvas_size = canvas->getDeviceSize();
297 int center_x = (canvas_size.fWidth - (int)(r.width()))/2;
298 int center_y = (canvas_size.fHeight - (int)(r.height()))/2;
299
300 SkMask mask;
301
302 if (!this->makeMask(&mask, r)) {
303 SkPaint paint;
304 r.offset( SkIntToScalar(center_x), SkIntToScalar(center_y) );
305 canvas->drawRect(r,paint);
306 return;
307 }
308 SkAutoMaskFreeImage amfi(mask.fImage);
309
310 SkBitmap bm;
311 bm.installMaskPixels(mask);
312
313 center_x = (canvas_size.fWidth - mask.fBounds.width())/2;
314 center_y = (canvas_size.fHeight - mask.fBounds.height())/2;
315
316 canvas->drawBitmap(bm, SkIntToScalar(center_x), SkIntToScalar(center_y), NULL);
317 }
318
319 private:
320 typedef GM INHERITED;
321 };
322
323 class BlurRectFastGM: public BlurRectCompareGM {
324 public:
BlurRectFastGM(const char name[],unsigned int rectWidth,unsigned int rectHeight,float blurRadius,SkBlurStyle style)325 BlurRectFastGM(const char name[], unsigned int rectWidth,
326 unsigned int rectHeight, float blurRadius,
327 SkBlurStyle style) :
328 INHERITED(name, rectWidth, rectHeight, blurRadius, style) {
329 }
330
331 protected:
makeMask(SkMask * m,const SkRect & r)332 bool makeMask(SkMask *m, const SkRect& r) override {
333 return SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(this->radius()),
334 m, r, this->style());
335 }
336 private:
337 typedef BlurRectCompareGM INHERITED;
338 };
339
340 class BlurRectSlowGM: public BlurRectCompareGM {
341 public:
BlurRectSlowGM(const char name[],unsigned int rectWidth,unsigned int rectHeight,float blurRadius,SkBlurStyle style)342 BlurRectSlowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
343 float blurRadius, SkBlurStyle style)
344 : INHERITED(name, rectWidth, rectHeight, blurRadius, style) {
345 }
346
347 protected:
makeMask(SkMask * m,const SkRect & r)348 bool makeMask(SkMask *m, const SkRect& r) override {
349 SkMask src;
350 r.roundOut(&src.fBounds);
351 src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop); // move to origin
352 src.fFormat = SkMask::kA8_Format;
353 src.fRowBytes = src.fBounds.width();
354 src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
355 SkAutoMaskFreeImage amfi(src.fImage);
356
357 memset(src.fImage, 0xff, src.computeTotalImageSize());
358
359 return SkBlurMask::BoxBlur(m, src,
360 SkBlurMask::ConvertRadiusToSigma(this->radius()),
361 this->style(), this->getQuality());
362 }
363
getQuality()364 virtual SkBlurQuality getQuality() {
365 return kHigh_SkBlurQuality;
366 }
367 private:
368 typedef BlurRectCompareGM INHERITED;
369 };
370
371 class BlurRectSlowLowGM: public BlurRectSlowGM {
372 public:
BlurRectSlowLowGM(const char name[],unsigned int rectWidth,unsigned int rectHeight,float blurRadius,SkBlurStyle style)373 BlurRectSlowLowGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
374 float blurRadius, SkBlurStyle style)
375 : INHERITED(name, rectWidth, rectHeight, blurRadius, style) {
376 }
377
378 protected:
getQuality()379 SkBlurQuality getQuality() override {
380 return kLow_SkBlurQuality;
381 }
382 private:
383 typedef BlurRectSlowGM INHERITED;
384 };
385
386 class BlurRectGroundTruthGM: public BlurRectCompareGM {
387 public:
BlurRectGroundTruthGM(const char name[],unsigned int rectWidth,unsigned int rectHeight,float blurRadius,SkBlurStyle style)388 BlurRectGroundTruthGM(const char name[], unsigned int rectWidth, unsigned int rectHeight,
389 float blurRadius, SkBlurStyle style)
390 : INHERITED(name, rectWidth, rectHeight, blurRadius, style) {
391 }
392
393 protected:
makeMask(SkMask * m,const SkRect & r)394 bool makeMask(SkMask *m, const SkRect& r) override {
395 SkMask src;
396 r.roundOut(&src.fBounds);
397 src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop); // move to origin
398 src.fFormat = SkMask::kA8_Format;
399 src.fRowBytes = src.fBounds.width();
400 src.fImage = SkMask::AllocImage(src.computeTotalImageSize());
401 SkAutoMaskFreeImage amfi(src.fImage);
402
403 memset(src.fImage, 0xff, src.computeTotalImageSize());
404
405 return SkBlurMask::BlurGroundTruth(SkBlurMask::ConvertRadiusToSigma(this->radius()),
406 m, src, this->style());
407 }
408
getQuality()409 virtual SkBlurQuality getQuality() {
410 return kHigh_SkBlurQuality;
411 }
412 private:
413 typedef BlurRectCompareGM INHERITED;
414 };
415
416
417 //////////////////////////////////////////////////////////////////////////////
418
419 DEF_GM(return new BlurRectGM("blurrects", 0xFF);)
420 DEF_GM(return new BlurRectDirectGM("blurrect_gallery");)
421