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