1 /*
2  * Copyright 2013 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/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkPaint.h"
13 #include "include/core/SkPoint.h"
14 #include "include/core/SkRRect.h"
15 #include "include/core/SkRect.h"
16 #include "include/core/SkScalar.h"
17 #include "include/core/SkShader.h"
18 #include "include/core/SkSize.h"
19 #include "include/core/SkString.h"
20 #include "include/core/SkTileMode.h"
21 #include "include/core/SkTypes.h"
22 #include "include/effects/SkGradientShader.h"
23 #include "include/private/SkTArray.h"
24 #include "include/utils/SkRandom.h"
25 #include "tools/ToolUtils.h"
26 
27 namespace skiagm {
28 
gen_color(SkRandom * rand)29 static SkColor gen_color(SkRandom* rand) {
30     SkScalar hsv[3];
31     hsv[0] = rand->nextRangeF(0.0f, 360.0f);
32     hsv[1] = rand->nextRangeF(0.75f, 1.0f);
33     hsv[2] = rand->nextRangeF(0.75f, 1.0f);
34 
35     return ToolUtils::color_to_565(SkHSVToColor(hsv));
36 }
37 
38 class RoundRectGM : public GM {
39 public:
RoundRectGM()40     RoundRectGM() {
41         this->setBGColor(0xFF000000);
42         this->makePaints();
43         this->makeMatrices();
44     }
45 
46 protected:
47 
onShortName()48     SkString onShortName() override {
49         return SkString("roundrects");
50     }
51 
onISize()52     SkISize onISize() override {
53         return SkISize::Make(1200, 900);
54     }
55 
makePaints()56     void makePaints() {
57         {
58             // no AA
59             SkPaint p;
60             fPaints.push_back(p);
61         }
62 
63         {
64             // AA
65             SkPaint p;
66             p.setAntiAlias(true);
67             fPaints.push_back(p);
68         }
69 
70         {
71             // AA with stroke style
72             SkPaint p;
73             p.setAntiAlias(true);
74             p.setStyle(SkPaint::kStroke_Style);
75             p.setStrokeWidth(SkIntToScalar(5));
76             fPaints.push_back(p);
77         }
78 
79         {
80             // AA with stroke style, width = 0
81             SkPaint p;
82             p.setAntiAlias(true);
83             p.setStyle(SkPaint::kStroke_Style);
84             fPaints.push_back(p);
85         }
86 
87         {
88             // AA with stroke and fill style
89             SkPaint p;
90             p.setAntiAlias(true);
91             p.setStyle(SkPaint::kStrokeAndFill_Style);
92             p.setStrokeWidth(SkIntToScalar(3));
93             fPaints.push_back(p);
94         }
95     }
96 
makeMatrices()97     void makeMatrices() {
98         {
99             SkMatrix m;
100             m.setIdentity();
101             fMatrices.push_back(m);
102         }
103 
104         {
105             SkMatrix m;
106             m.setScale(SkIntToScalar(3), SkIntToScalar(2));
107             fMatrices.push_back(m);
108         }
109 
110         {
111             SkMatrix m;
112             m.setScale(SkIntToScalar(2), SkIntToScalar(2));
113             fMatrices.push_back(m);
114         }
115 
116         {
117             SkMatrix m;
118             m.setScale(SkIntToScalar(1), SkIntToScalar(2));
119             fMatrices.push_back(m);
120         }
121 
122         {
123             SkMatrix m;
124             m.setScale(SkIntToScalar(4), SkIntToScalar(1));
125             fMatrices.push_back(m);
126         }
127 
128         {
129             SkMatrix m;
130             m.setRotate(SkIntToScalar(90));
131             fMatrices.push_back(m);
132         }
133 
134         {
135             SkMatrix m;
136             m.setSkew(SkIntToScalar(2), SkIntToScalar(3));
137             fMatrices.push_back(m);
138         }
139 
140         {
141             SkMatrix m;
142             m.setRotate(SkIntToScalar(60));
143             fMatrices.push_back(m);
144         }
145     }
146 
onDraw(SkCanvas * canvas)147     void onDraw(SkCanvas* canvas) override {
148         SkRandom rand(1);
149         canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1);
150         const SkRect rect = SkRect::MakeLTRB(-20, -30, 20, 30);
151         SkRRect circleRect;
152         circleRect.setRectXY(rect, 5, 5);
153 
154         const SkScalar kXStart = 60.0f;
155         const SkScalar kYStart = 80.0f;
156         const int kXStep = 150;
157         const int kYStep = 160;
158         int maxX = fMatrices.count();
159 
160         SkPaint rectPaint;
161         rectPaint.setAntiAlias(true);
162         rectPaint.setStyle(SkPaint::kStroke_Style);
163         rectPaint.setStrokeWidth(SkIntToScalar(0));
164         rectPaint.setColor(SK_ColorLTGRAY);
165 
166         int testCount = 0;
167         for (int i = 0; i < fPaints.count(); ++i) {
168             for (int j = 0; j < fMatrices.count(); ++j) {
169                 canvas->save();
170                 SkMatrix mat = fMatrices[j];
171                 // position the roundrect, and make it at off-integer coords.
172                 mat.postTranslate(kXStart + SK_Scalar1 * kXStep * (testCount % maxX) +
173                                   SK_Scalar1 / 4,
174                                   kYStart + SK_Scalar1 * kYStep * (testCount / maxX) +
175                                   3 * SK_Scalar1 / 4);
176                 canvas->concat(mat);
177 
178                 SkColor color = gen_color(&rand);
179                 fPaints[i].setColor(color);
180 
181                 canvas->drawRect(rect, rectPaint);
182                 canvas->drawRRect(circleRect, fPaints[i]);
183 
184                 canvas->restore();
185 
186                 ++testCount;
187             }
188         }
189 
190         // special cases
191 
192         // non-scaled tall and skinny roundrect
193         for (int i = 0; i < fPaints.count(); ++i) {
194             SkRect rect = SkRect::MakeLTRB(-20, -60, 20, 60);
195             SkRRect ellipseRect;
196             ellipseRect.setRectXY(rect, 5, 10);
197 
198             canvas->save();
199             // position the roundrect, and make it at off-integer coords.
200             canvas->translate(kXStart + SK_Scalar1 * kXStep * 2.55f + SK_Scalar1 / 4,
201                               kYStart + SK_Scalar1 * kYStep * i + 3 * SK_Scalar1 / 4);
202 
203             SkColor color = gen_color(&rand);
204             fPaints[i].setColor(color);
205 
206             canvas->drawRect(rect, rectPaint);
207             canvas->drawRRect(ellipseRect, fPaints[i]);
208             canvas->restore();
209         }
210 
211         // non-scaled wide and short roundrect
212         for (int i = 0; i < fPaints.count(); ++i) {
213             SkRect rect = SkRect::MakeLTRB(-80, -30, 80, 30);
214             SkRRect ellipseRect;
215             ellipseRect.setRectXY(rect, 20, 5);
216 
217             canvas->save();
218             // position the roundrect, and make it at off-integer coords.
219             canvas->translate(kXStart + SK_Scalar1 * kXStep * 4 + SK_Scalar1 / 4,
220                               kYStart + SK_Scalar1 * kYStep * i + 3 * SK_Scalar1 / 4 +
221                               SK_ScalarHalf * kYStep);
222 
223             SkColor color = gen_color(&rand);
224             fPaints[i].setColor(color);
225 
226             canvas->drawRect(rect, rectPaint);
227             canvas->drawRRect(ellipseRect, fPaints[i]);
228             canvas->restore();
229         }
230 
231         // super skinny roundrect
232         for (int i = 0; i < fPaints.count(); ++i) {
233             SkRect rect = SkRect::MakeLTRB(0, -60, 1, 60);
234             SkRRect circleRect;
235             circleRect.setRectXY(rect, 5, 5);
236 
237             canvas->save();
238             // position the roundrect, and make it at off-integer coords.
239             canvas->translate(kXStart + SK_Scalar1 * kXStep * 3.25f + SK_Scalar1 / 4,
240                               kYStart + SK_Scalar1 * kYStep * i + 3 * SK_Scalar1 / 4);
241 
242             SkColor color = gen_color(&rand);
243             fPaints[i].setColor(color);
244 
245             canvas->drawRRect(circleRect, fPaints[i]);
246             canvas->restore();
247         }
248 
249         // super short roundrect
250         for (int i = 0; i < fPaints.count(); ++i) {
251             SkRect rect = SkRect::MakeLTRB(-80, -1, 80, 0);
252             SkRRect circleRect;
253             circleRect.setRectXY(rect, 5, 5);
254 
255             canvas->save();
256             // position the roundrect, and make it at off-integer coords.
257             canvas->translate(kXStart + SK_Scalar1 * kXStep * 2.5f + SK_Scalar1 / 4,
258                               kYStart + SK_Scalar1 * kYStep * i + 3 * SK_Scalar1 / 4 +
259                               SK_ScalarHalf * kYStep);
260 
261             SkColor color = gen_color(&rand);
262             fPaints[i].setColor(color);
263 
264             canvas->drawRRect(circleRect, fPaints[i]);
265             canvas->restore();
266         }
267 
268         // radial gradient
269         SkPoint center = SkPoint::Make(SkIntToScalar(0), SkIntToScalar(0));
270         SkColor colors[] = { SK_ColorBLUE, SK_ColorRED, SK_ColorGREEN };
271         SkScalar pos[] = { 0, SK_ScalarHalf, SK_Scalar1 };
272         auto shader = SkGradientShader::MakeRadial(center, 20, colors, pos, SK_ARRAY_COUNT(colors),
273                                                    SkTileMode::kClamp);
274 
275         for (int i = 0; i < fPaints.count(); ++i) {
276             canvas->save();
277             // position the path, and make it at off-integer coords.
278             canvas->translate(kXStart + SK_Scalar1 * kXStep * 0 + SK_Scalar1 / 4,
279                               kYStart + SK_Scalar1 * kYStep * i + 3 * SK_Scalar1 / 4 +
280                               SK_ScalarHalf * kYStep);
281 
282             SkColor color = gen_color(&rand);
283             fPaints[i].setColor(color);
284             fPaints[i].setShader(shader);
285 
286             canvas->drawRect(rect, rectPaint);
287             canvas->drawRRect(circleRect, fPaints[i]);
288 
289             fPaints[i].setShader(nullptr);
290 
291             canvas->restore();
292         }
293 
294         // strokes and radii
295         {
296             SkScalar radii[][2] = {
297                 {10,10},
298                 {5,15},
299                 {5,15},
300                 {5,15}
301             };
302 
303             SkScalar strokeWidths[] = {
304                 20, 10, 20, 40
305             };
306 
307             for (int i = 0; i < 4; ++i) {
308                 SkRRect circleRect;
309                 circleRect.setRectXY(rect, radii[i][0], radii[i][1]);
310 
311                 canvas->save();
312                 // position the roundrect, and make it at off-integer coords.
313                 canvas->translate(kXStart + SK_Scalar1 * kXStep * 5 + SK_Scalar1 / 4,
314                                   kYStart + SK_Scalar1 * kYStep * i + 3 * SK_Scalar1 / 4 +
315                                   SK_ScalarHalf * kYStep);
316 
317                 SkColor color = gen_color(&rand);
318 
319                 SkPaint p;
320                 p.setAntiAlias(true);
321                 p.setStyle(SkPaint::kStroke_Style);
322                 p.setStrokeWidth(strokeWidths[i]);
323                 p.setColor(color);
324 
325                 canvas->drawRRect(circleRect, p);
326                 canvas->restore();
327             }
328         }
329 
330         // test old entry point ( https://bug.skia.org/3786 )
331         {
332             canvas->save();
333 
334             canvas->translate(kXStart + SK_Scalar1 * kXStep * 5 + SK_Scalar1 / 4,
335                               kYStart + SK_Scalar1 * kYStep * 4 + SK_Scalar1 / 4 +
336                               SK_ScalarHalf * kYStep);
337 
338             const SkColor color = gen_color(&rand);
339 
340             SkPaint p;
341             p.setColor(color);
342 
343             const SkRect oooRect = { 20, 30, -20, -30 };     // intentionally out of order
344             canvas->drawRoundRect(oooRect, 10, 10, p);
345 
346             canvas->restore();
347         }
348 
349         // rrect with stroke > radius/2
350         {
351             SkRect smallRect = { -30, -20, 30, 20 };
352             SkRRect circleRect;
353             circleRect.setRectXY(smallRect, 5, 5);
354 
355             canvas->save();
356             // position the roundrect, and make it at off-integer coords.
357             canvas->translate(kXStart + SK_Scalar1 * kXStep * 5 + SK_Scalar1 / 4,
358                               kYStart - SK_Scalar1 * kYStep + 73 * SK_Scalar1 / 4 +
359                               SK_ScalarHalf * kYStep);
360 
361             SkColor color = gen_color(&rand);
362 
363             SkPaint p;
364             p.setAntiAlias(true);
365             p.setStyle(SkPaint::kStroke_Style);
366             p.setStrokeWidth(25);
367             p.setColor(color);
368 
369             canvas->drawRRect(circleRect, p);
370             canvas->restore();
371         }
372     }
373 
374 private:
375     SkTArray<SkPaint> fPaints;
376     SkTArray<SkMatrix> fMatrices;
377 
378     using INHERITED = GM;
379 };
380 
381 //////////////////////////////////////////////////////////////////////////////
382 
383 DEF_GM( return new RoundRectGM; )
384 
385 }  // namespace skiagm
386