xref: /third_party/skia/gm/addarc.cpp (revision cb93a386)
1/*
2 * Copyright 2015 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/SkPaint.h"
12#include "include/core/SkPathBuilder.h"
13#include "include/core/SkPathMeasure.h"
14#include "include/core/SkPoint.h"
15#include "include/core/SkRect.h"
16#include "include/core/SkScalar.h"
17#include "include/core/SkSize.h"
18#include "include/core/SkString.h"
19#include "include/core/SkTypes.h"
20#include "include/private/SkFloatingPoint.h"
21#include "include/utils/SkRandom.h"
22#include "tools/ToolUtils.h"
23#include "tools/timer/TimeUtils.h"
24
25class AddArcGM : public skiagm::GM {
26public:
27    AddArcGM() : fRotate(0) {}
28
29protected:
30    SkString onShortName() override { return SkString("addarc"); }
31
32    SkISize onISize() override { return SkISize::Make(1040, 1040); }
33
34    void onDraw(SkCanvas* canvas) override {
35        canvas->translate(20, 20);
36
37        SkRect r = SkRect::MakeWH(1000, 1000);
38
39        SkPaint paint;
40        paint.setAntiAlias(true);
41        paint.setStroke(true);
42        paint.setStrokeWidth(15);
43
44        const SkScalar inset = paint.getStrokeWidth() + 4;
45        const SkScalar sweepAngle = 345;
46        SkRandom rand;
47
48        SkScalar sign = 1;
49        while (r.width() > paint.getStrokeWidth() * 3) {
50            paint.setColor(ToolUtils::color_to_565(rand.nextU() | (0xFF << 24)));
51            SkScalar startAngle = rand.nextUScalar1() * 360;
52
53            SkScalar speed = SkScalarSqrt(16 / r.width()) * 0.5f;
54            startAngle += fRotate * 360 * speed * sign;
55
56            SkPathBuilder path;
57            path.addArc(r, startAngle, sweepAngle);
58            canvas->drawPath(path.detach().setIsVolatile(true), paint);
59
60            r.inset(inset, inset);
61            sign = -sign;
62        }
63    }
64
65    bool onAnimate(double nanos) override {
66        fRotate = TimeUtils::Scaled(1e-9 * nanos, 1, 360);
67        return true;
68    }
69
70private:
71    SkScalar fRotate;
72    using INHERITED = skiagm::GM;
73};
74DEF_GM( return new AddArcGM; )
75
76///////////////////////////////////////////////////
77
78#define R   400
79
80DEF_SIMPLE_GM(addarc_meas, canvas, 2*R + 40, 2*R + 40) {
81        canvas->translate(R + 20, R + 20);
82
83        SkPaint paint;
84        paint.setAntiAlias(true);
85        paint.setStroke(true);
86
87        SkPaint measPaint;
88        measPaint.setAntiAlias(true);
89        measPaint.setColor(SK_ColorRED);
90
91        const SkRect oval = SkRect::MakeLTRB(-R, -R, R, R);
92        canvas->drawOval(oval, paint);
93
94        for (SkScalar deg = 0; deg < 360; deg += 10) {
95            const SkScalar rad = SkDegreesToRadians(deg);
96            SkScalar rx = SkScalarCos(rad) * R;
97            SkScalar ry = SkScalarSin(rad) * R;
98
99            canvas->drawLine(0, 0, rx, ry, paint);
100
101            SkPathMeasure meas(SkPathBuilder().addArc(oval, 0, deg).detach(), false);
102            SkScalar arcLen = rad * R;
103            SkPoint pos;
104            if (meas.getPosTan(arcLen, &pos, nullptr)) {
105                canvas->drawLine({0, 0}, pos, measPaint);
106            }
107        }
108}
109
110///////////////////////////////////////////////////
111
112// Emphasize drawing a stroked oval (containing conics) and then scaling the results up,
113// to ensure that we compute the stroke taking the CTM into account
114//
115class StrokeCircleGM : public skiagm::GM {
116public:
117    StrokeCircleGM() : fRotate(0) {}
118
119protected:
120    SkString onShortName() override { return SkString("strokecircle"); }
121
122    SkISize onISize() override { return SkISize::Make(520, 520); }
123
124    void onDraw(SkCanvas* canvas) override {
125        canvas->scale(20, 20);
126        canvas->translate(13, 13);
127
128        SkPaint paint;
129        paint.setAntiAlias(true);
130        paint.setStroke(true);
131        paint.setStrokeWidth(SK_Scalar1 / 2);
132
133        const SkScalar delta = paint.getStrokeWidth() * 3 / 2;
134        SkRect r = SkRect::MakeXYWH(-12, -12, 24, 24);
135        SkRandom rand;
136
137        SkScalar sign = 1;
138        while (r.width() > paint.getStrokeWidth() * 2) {
139            SkAutoCanvasRestore acr(canvas, true);
140            canvas->rotate(fRotate * sign);
141
142            paint.setColor(ToolUtils::color_to_565(rand.nextU() | (0xFF << 24)));
143            canvas->drawOval(r, paint);
144            r.inset(delta, delta);
145            sign = -sign;
146        }
147    }
148
149    bool onAnimate(double nanos) override {
150        fRotate = TimeUtils::Scaled(1e-9 * nanos, 60, 360);
151        return true;
152    }
153
154private:
155    SkScalar fRotate;
156
157    using INHERITED = skiagm::GM;
158};
159DEF_GM( return new StrokeCircleGM; )
160
161//////////////////////
162
163// Fill circles and rotate them to test our Analytic Anti-Aliasing.
164// This test is based on StrokeCircleGM.
165class FillCircleGM : public skiagm::GM {
166public:
167    FillCircleGM() : fRotate(0) {}
168
169protected:
170    SkString onShortName() override { return SkString("fillcircle"); }
171
172    SkISize onISize() override { return SkISize::Make(520, 520); }
173
174    void onDraw(SkCanvas* canvas) override {
175        canvas->scale(20, 20);
176        canvas->translate(13, 13);
177
178        SkPaint paint;
179        paint.setAntiAlias(true);
180        paint.setStroke(true);
181        paint.setStrokeWidth(SK_Scalar1 / 2);
182
183        const SkScalar strokeWidth = paint.getStrokeWidth();
184        const SkScalar delta = strokeWidth * 3 / 2;
185        SkRect r = SkRect::MakeXYWH(-12, -12, 24, 24);
186        SkRandom rand;
187
188        // Reset style to fill. We only need stroke stype for producing delta and strokeWidth
189        paint.setStroke(false);
190
191        SkScalar sign = 1;
192        while (r.width() > strokeWidth * 2) {
193            SkAutoCanvasRestore acr(canvas, true);
194            canvas->rotate(fRotate * sign);
195            paint.setColor(ToolUtils::color_to_565(rand.nextU() | (0xFF << 24)));
196            canvas->drawOval(r, paint);
197            r.inset(delta, delta);
198            sign = -sign;
199        }
200    }
201
202    bool onAnimate(double nanos) override {
203        fRotate = TimeUtils::Scaled(1e-9 * nanos, 60, 360);
204        return true;
205    }
206
207private:
208    SkScalar fRotate;
209
210    using INHERITED = skiagm::GM;
211};
212DEF_GM( return new FillCircleGM; )
213
214//////////////////////
215
216static void html_canvas_arc(SkPathBuilder* path, SkScalar x, SkScalar y, SkScalar r, SkScalar start,
217                            SkScalar end, bool ccw, bool callArcTo) {
218    SkRect bounds = { x - r, y - r, x + r, y + r };
219    SkScalar sweep = ccw ? end - start : start - end;
220    if (callArcTo)
221        path->arcTo(bounds, start, sweep, false);
222    else
223        path->addArc(bounds, start, sweep);
224}
225
226// Lifted from canvas-arc-circumference-fill-diffs.html
227DEF_SIMPLE_GM(manyarcs, canvas, 620, 330) {
228        SkPaint paint;
229        paint.setAntiAlias(true);
230        paint.setStroke(true);
231
232        canvas->translate(10, 10);
233
234        // 20 angles.
235        SkScalar sweepAngles[] = {
236                           -123.7f, -2.3f, -2, -1, -0.3f, -0.000001f, 0, 0.000001f, 0.3f, 0.7f,
237                           1, 1.3f, 1.5f, 1.7f, 1.99999f, 2, 2.00001f, 2.3f, 4.3f, 3934723942837.3f
238        };
239        for (size_t i = 0; i < SK_ARRAY_COUNT(sweepAngles); ++i) {
240            sweepAngles[i] *= 180;
241        }
242
243        SkScalar startAngles[] = { -1, -0.5f, 0, 0.5f };
244        for (size_t i = 0; i < SK_ARRAY_COUNT(startAngles); ++i) {
245            startAngles[i] *= 180;
246        }
247
248        bool anticlockwise = false;
249        SkScalar sign = 1;
250        for (size_t i = 0; i < SK_ARRAY_COUNT(startAngles) * 2; ++i) {
251            if (i == SK_ARRAY_COUNT(startAngles)) {
252                anticlockwise = true;
253                sign = -1;
254            }
255            SkScalar startAngle = startAngles[i % SK_ARRAY_COUNT(startAngles)] * sign;
256            canvas->save();
257            for (size_t j = 0; j < SK_ARRAY_COUNT(sweepAngles); ++j) {
258                SkPathBuilder path;
259                path.moveTo(0, 2);
260                html_canvas_arc(&path, 18, 15, 10, startAngle, startAngle + (sweepAngles[j] * sign),
261                                anticlockwise, true);
262                path.lineTo(0, 28);
263                canvas->drawPath(path.detach().setIsVolatile(true), paint);
264                canvas->translate(30, 0);
265            }
266            canvas->restore();
267            canvas->translate(0, 40);
268        }
269}
270
271// Lifted from https://bugs.chromium.org/p/chromium/issues/detail?id=640031
272DEF_SIMPLE_GM(tinyanglearcs, canvas, 620, 330) {
273        SkPaint paint;
274        paint.setAntiAlias(true);
275        paint.setStroke(true);
276
277        canvas->translate(50, 50);
278
279        SkScalar outerRadius = 100000.0f;
280        SkScalar innerRadius = outerRadius - 20.0f;
281        SkScalar centerX = 50;
282        SkScalar centerY = outerRadius;
283        SkScalar startAngles[] = { 1.5f * SK_ScalarPI , 1.501f * SK_ScalarPI  };
284        SkScalar sweepAngle = 10.0f / outerRadius;
285
286        for (size_t i = 0; i < SK_ARRAY_COUNT(startAngles); ++i) {
287            SkPathBuilder path;
288            SkScalar endAngle = startAngles[i] + sweepAngle;
289            path.moveTo(centerX + innerRadius * sk_float_cos(startAngles[i]),
290                        centerY + innerRadius * sk_float_sin(startAngles[i]));
291            path.lineTo(centerX + outerRadius * sk_float_cos(startAngles[i]),
292                        centerY + outerRadius * sk_float_sin(startAngles[i]));
293            // A combination of tiny sweepAngle + large radius, we should draw a line.
294            html_canvas_arc(&path, centerX, outerRadius, outerRadius,
295                            startAngles[i] * 180 / SK_ScalarPI, endAngle * 180 / SK_ScalarPI,
296                            true, true);
297            path.lineTo(centerX + innerRadius * sk_float_cos(endAngle),
298                        centerY + innerRadius * sk_float_sin(endAngle));
299            html_canvas_arc(&path, centerX, outerRadius, innerRadius,
300                            endAngle * 180 / SK_ScalarPI, startAngles[i] * 180 / SK_ScalarPI,
301                            true, false);
302            canvas->drawPath(path.detach(), paint);
303            canvas->translate(20, 0);
304        }
305}
306