blob: 3f4d5780d4c3b10d2db8d354ab1abab35acadbf7 [file] [log] [blame]
reed19d8f9f2015-01-29 10:48:16 -08001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
9#include "include/core/SkCanvas.h"
Ben Wagnerd1701ba2019-04-30 13:44:26 -040010#include "include/core/SkColor.h"
11#include "include/core/SkPaint.h"
12#include "include/core/SkPath.h"
Mike Reedcfb130c2020-08-03 11:02:20 -040013#include "include/core/SkPathBuilder.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "include/core/SkPathMeasure.h"
Ben Wagnerd1701ba2019-04-30 13:44:26 -040015#include "include/core/SkPoint.h"
16#include "include/core/SkRect.h"
17#include "include/core/SkScalar.h"
18#include "include/core/SkSize.h"
19#include "include/core/SkString.h"
20#include "include/core/SkTypes.h"
21#include "include/private/SkFloatingPoint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050022#include "include/utils/SkRandom.h"
23#include "tools/ToolUtils.h"
Hal Canary41248072019-07-11 16:32:53 -040024#include "tools/timer/TimeUtils.h"
reed19d8f9f2015-01-29 10:48:16 -080025
26class AddArcGM : public skiagm::GM {
reedd9adfe62015-02-01 19:01:04 -080027public:
28 AddArcGM() : fRotate(0) {}
29
reed19d8f9f2015-01-29 10:48:16 -080030protected:
mtklein36352bf2015-03-25 18:17:31 -070031 SkString onShortName() override { return SkString("addarc"); }
reed19d8f9f2015-01-29 10:48:16 -080032
mtklein36352bf2015-03-25 18:17:31 -070033 SkISize onISize() override { return SkISize::Make(1040, 1040); }
reed19d8f9f2015-01-29 10:48:16 -080034
mtklein36352bf2015-03-25 18:17:31 -070035 void onDraw(SkCanvas* canvas) override {
reed19d8f9f2015-01-29 10:48:16 -080036 canvas->translate(20, 20);
37
38 SkRect r = SkRect::MakeWH(1000, 1000);
39
40 SkPaint paint;
41 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -040042 paint.setStroke(true);
reed19d8f9f2015-01-29 10:48:16 -080043 paint.setStrokeWidth(15);
44
45 const SkScalar inset = paint.getStrokeWidth() + 4;
46 const SkScalar sweepAngle = 345;
47 SkRandom rand;
48
reedd9adfe62015-02-01 19:01:04 -080049 SkScalar sign = 1;
reed19d8f9f2015-01-29 10:48:16 -080050 while (r.width() > paint.getStrokeWidth() * 3) {
Mike Kleinea3f0142019-03-20 11:12:10 -050051 paint.setColor(ToolUtils::color_to_565(rand.nextU() | (0xFF << 24)));
reed19d8f9f2015-01-29 10:48:16 -080052 SkScalar startAngle = rand.nextUScalar1() * 360;
53
reedd9adfe62015-02-01 19:01:04 -080054 SkScalar speed = SkScalarSqrt(16 / r.width()) * 0.5f;
55 startAngle += fRotate * 360 * speed * sign;
56
reed19d8f9f2015-01-29 10:48:16 -080057 SkPath path;
58 path.addArc(r, startAngle, sweepAngle);
59 canvas->drawPath(path, paint);
60
61 r.inset(inset, inset);
reedd9adfe62015-02-01 19:01:04 -080062 sign = -sign;
reed19d8f9f2015-01-29 10:48:16 -080063 }
64 }
65
Hal Canary41248072019-07-11 16:32:53 -040066 bool onAnimate(double nanos) override {
67 fRotate = TimeUtils::Scaled(1e-9 * nanos, 1, 360);
reedd9adfe62015-02-01 19:01:04 -080068 return true;
69 }
70
reed19d8f9f2015-01-29 10:48:16 -080071private:
reedd9adfe62015-02-01 19:01:04 -080072 SkScalar fRotate;
reed19d8f9f2015-01-29 10:48:16 -080073 typedef skiagm::GM INHERITED;
74};
75DEF_GM( return new AddArcGM; )
reed61adb1b2015-02-09 13:01:05 -080076
77///////////////////////////////////////////////////
78
79#define R 400
80
Hal Canary607c44f2019-01-23 10:40:02 -050081DEF_SIMPLE_GM(addarc_meas, canvas, 2*R + 40, 2*R + 40) {
reed61adb1b2015-02-09 13:01:05 -080082 canvas->translate(R + 20, R + 20);
83
84 SkPaint paint;
85 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -040086 paint.setStroke(true);
reed61adb1b2015-02-09 13:01:05 -080087
88 SkPaint measPaint;
89 measPaint.setAntiAlias(true);
90 measPaint.setColor(SK_ColorRED);
91
92 const SkRect oval = SkRect::MakeLTRB(-R, -R, R, R);
93 canvas->drawOval(oval, paint);
94
95 for (SkScalar deg = 0; deg < 360; deg += 10) {
96 const SkScalar rad = SkDegreesToRadians(deg);
97 SkScalar rx = SkScalarCos(rad) * R;
98 SkScalar ry = SkScalarSin(rad) * R;
99
100 canvas->drawLine(0, 0, rx, ry, paint);
101
102 SkPath path;
103 path.addArc(oval, 0, deg);
104 SkPathMeasure meas(path, false);
105 SkScalar arcLen = rad * R;
106 SkPoint pos;
halcanary96fcdcc2015-08-27 07:41:13 -0700107 if (meas.getPosTan(arcLen, &pos, nullptr)) {
Hal Canary23e474c2017-05-15 13:35:35 -0400108 canvas->drawLine({0, 0}, pos, measPaint);
reed61adb1b2015-02-09 13:01:05 -0800109 }
110 }
Hal Canary607c44f2019-01-23 10:40:02 -0500111}
reed8ed666d2015-02-10 17:44:26 -0800112
113///////////////////////////////////////////////////
114
115// Emphasize drawing a stroked oval (containing conics) and then scaling the results up,
116// to ensure that we compute the stroke taking the CTM into account
117//
118class StrokeCircleGM : public skiagm::GM {
119public:
120 StrokeCircleGM() : fRotate(0) {}
halcanary9d524f22016-03-29 09:03:52 -0700121
reed8ed666d2015-02-10 17:44:26 -0800122protected:
mtklein36352bf2015-03-25 18:17:31 -0700123 SkString onShortName() override { return SkString("strokecircle"); }
halcanary9d524f22016-03-29 09:03:52 -0700124
mtklein36352bf2015-03-25 18:17:31 -0700125 SkISize onISize() override { return SkISize::Make(520, 520); }
halcanary9d524f22016-03-29 09:03:52 -0700126
mtklein36352bf2015-03-25 18:17:31 -0700127 void onDraw(SkCanvas* canvas) override {
reed8ed666d2015-02-10 17:44:26 -0800128 canvas->scale(20, 20);
129 canvas->translate(13, 13);
130
131 SkPaint paint;
132 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400133 paint.setStroke(true);
reed8ed666d2015-02-10 17:44:26 -0800134 paint.setStrokeWidth(SK_Scalar1 / 2);
135
136 const SkScalar delta = paint.getStrokeWidth() * 3 / 2;
137 SkRect r = SkRect::MakeXYWH(-12, -12, 24, 24);
138 SkRandom rand;
139
140 SkScalar sign = 1;
141 while (r.width() > paint.getStrokeWidth() * 2) {
142 SkAutoCanvasRestore acr(canvas, true);
143 canvas->rotate(fRotate * sign);
144
Mike Kleinea3f0142019-03-20 11:12:10 -0500145 paint.setColor(ToolUtils::color_to_565(rand.nextU() | (0xFF << 24)));
reed8ed666d2015-02-10 17:44:26 -0800146 canvas->drawOval(r, paint);
147 r.inset(delta, delta);
148 sign = -sign;
149 }
150 }
151
Hal Canary41248072019-07-11 16:32:53 -0400152 bool onAnimate(double nanos) override {
153 fRotate = TimeUtils::Scaled(1e-9 * nanos, 60, 360);
reed8ed666d2015-02-10 17:44:26 -0800154 return true;
155 }
156
157private:
158 SkScalar fRotate;
159
160 typedef skiagm::GM INHERITED;
161};
162DEF_GM( return new StrokeCircleGM; )
reed9e779d42015-02-17 11:43:14 -0800163
164//////////////////////
165
liyuqiane60d8552016-10-03 13:49:37 -0700166// Fill circles and rotate them to test our Analytic Anti-Aliasing.
167// This test is based on StrokeCircleGM.
168class FillCircleGM : public skiagm::GM {
169public:
170 FillCircleGM() : fRotate(0) {}
171
172protected:
173 SkString onShortName() override { return SkString("fillcircle"); }
174
175 SkISize onISize() override { return SkISize::Make(520, 520); }
176
177 void onDraw(SkCanvas* canvas) override {
178 canvas->scale(20, 20);
179 canvas->translate(13, 13);
180
181 SkPaint paint;
182 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400183 paint.setStroke(true);
liyuqiane60d8552016-10-03 13:49:37 -0700184 paint.setStrokeWidth(SK_Scalar1 / 2);
185
186 const SkScalar strokeWidth = paint.getStrokeWidth();
187 const SkScalar delta = strokeWidth * 3 / 2;
188 SkRect r = SkRect::MakeXYWH(-12, -12, 24, 24);
189 SkRandom rand;
190
191 // Reset style to fill. We only need stroke stype for producing delta and strokeWidth
Mike Reed19630092020-05-18 21:25:44 -0400192 paint.setStroke(false);
liyuqiane60d8552016-10-03 13:49:37 -0700193
194 SkScalar sign = 1;
195 while (r.width() > strokeWidth * 2) {
196 SkAutoCanvasRestore acr(canvas, true);
197 canvas->rotate(fRotate * sign);
Mike Kleinea3f0142019-03-20 11:12:10 -0500198 paint.setColor(ToolUtils::color_to_565(rand.nextU() | (0xFF << 24)));
liyuqiane60d8552016-10-03 13:49:37 -0700199 canvas->drawOval(r, paint);
200 r.inset(delta, delta);
201 sign = -sign;
202 }
203 }
204
Hal Canary41248072019-07-11 16:32:53 -0400205 bool onAnimate(double nanos) override {
206 fRotate = TimeUtils::Scaled(1e-9 * nanos, 60, 360);
liyuqiane60d8552016-10-03 13:49:37 -0700207 return true;
208 }
209
210private:
211 SkScalar fRotate;
212
213 typedef skiagm::GM INHERITED;
214};
215DEF_GM( return new FillCircleGM; )
216
217//////////////////////
218
Mike Reedcfb130c2020-08-03 11:02:20 -0400219static void html_canvas_arc(SkPathBuilder* path, SkScalar x, SkScalar y, SkScalar r, SkScalar start,
xidachen6069dda2016-10-06 05:42:23 -0700220 SkScalar end, bool ccw, bool callArcTo) {
reed9e779d42015-02-17 11:43:14 -0800221 SkRect bounds = { x - r, y - r, x + r, y + r };
222 SkScalar sweep = ccw ? end - start : start - end;
xidachen6069dda2016-10-06 05:42:23 -0700223 if (callArcTo)
224 path->arcTo(bounds, start, sweep, false);
225 else
226 path->addArc(bounds, start, sweep);
reed9e779d42015-02-17 11:43:14 -0800227}
228
229// Lifted from canvas-arc-circumference-fill-diffs.html
Hal Canary607c44f2019-01-23 10:40:02 -0500230DEF_SIMPLE_GM(manyarcs, canvas, 620, 330) {
reed9e779d42015-02-17 11:43:14 -0800231 SkPaint paint;
232 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400233 paint.setStroke(true);
reed9e779d42015-02-17 11:43:14 -0800234
235 canvas->translate(10, 10);
236
237 // 20 angles.
238 SkScalar sweepAngles[] = {
239 -123.7f, -2.3f, -2, -1, -0.3f, -0.000001f, 0, 0.000001f, 0.3f, 0.7f,
240 1, 1.3f, 1.5f, 1.7f, 1.99999f, 2, 2.00001f, 2.3f, 4.3f, 3934723942837.3f
241 };
242 for (size_t i = 0; i < SK_ARRAY_COUNT(sweepAngles); ++i) {
243 sweepAngles[i] *= 180;
244 }
halcanary9d524f22016-03-29 09:03:52 -0700245
reed9e779d42015-02-17 11:43:14 -0800246 SkScalar startAngles[] = { -1, -0.5f, 0, 0.5f };
247 for (size_t i = 0; i < SK_ARRAY_COUNT(startAngles); ++i) {
248 startAngles[i] *= 180;
249 }
halcanary9d524f22016-03-29 09:03:52 -0700250
reed9e779d42015-02-17 11:43:14 -0800251 bool anticlockwise = false;
252 SkScalar sign = 1;
253 for (size_t i = 0; i < SK_ARRAY_COUNT(startAngles) * 2; ++i) {
254 if (i == SK_ARRAY_COUNT(startAngles)) {
255 anticlockwise = true;
256 sign = -1;
257 }
258 SkScalar startAngle = startAngles[i % SK_ARRAY_COUNT(startAngles)] * sign;
259 canvas->save();
260 for (size_t j = 0; j < SK_ARRAY_COUNT(sweepAngles); ++j) {
Mike Reedcfb130c2020-08-03 11:02:20 -0400261 SkPathBuilder path;
reed9e779d42015-02-17 11:43:14 -0800262 path.moveTo(0, 2);
263 html_canvas_arc(&path, 18, 15, 10, startAngle, startAngle + (sweepAngles[j] * sign),
xidachen6069dda2016-10-06 05:42:23 -0700264 anticlockwise, true);
reed9e779d42015-02-17 11:43:14 -0800265 path.lineTo(0, 28);
Mike Reedcfb130c2020-08-03 11:02:20 -0400266 canvas->drawPath(path.detach(), paint);
reed9e779d42015-02-17 11:43:14 -0800267 canvas->translate(30, 0);
268 }
269 canvas->restore();
270 canvas->translate(0, 40);
271 }
Hal Canary607c44f2019-01-23 10:40:02 -0500272}
xidachen6069dda2016-10-06 05:42:23 -0700273
274// Lifted from https://bugs.chromium.org/p/chromium/issues/detail?id=640031
Hal Canary607c44f2019-01-23 10:40:02 -0500275DEF_SIMPLE_GM(tinyanglearcs, canvas, 620, 330) {
xidachen6069dda2016-10-06 05:42:23 -0700276 SkPaint paint;
277 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400278 paint.setStroke(true);
xidachen6069dda2016-10-06 05:42:23 -0700279
280 canvas->translate(50, 50);
281
xidachen95e34a32016-10-19 10:24:28 -0700282 SkScalar outerRadius = 100000.0f;
283 SkScalar innerRadius = outerRadius - 20.0f;
284 SkScalar centerX = 50;
285 SkScalar centerY = outerRadius;
286 SkScalar startAngles[] = { 1.5f * SK_ScalarPI , 1.501f * SK_ScalarPI };
287 SkScalar sweepAngle = 10.0f / outerRadius;
288
289 for (size_t i = 0; i < SK_ARRAY_COUNT(startAngles); ++i) {
Mike Reedcfb130c2020-08-03 11:02:20 -0400290 SkPathBuilder path;
xidachen95e34a32016-10-19 10:24:28 -0700291 SkScalar endAngle = startAngles[i] + sweepAngle;
292 path.moveTo(centerX + innerRadius * sk_float_cos(startAngles[i]),
293 centerY + innerRadius * sk_float_sin(startAngles[i]));
294 path.lineTo(centerX + outerRadius * sk_float_cos(startAngles[i]),
295 centerY + outerRadius * sk_float_sin(startAngles[i]));
296 // A combination of tiny sweepAngle + large radius, we should draw a line.
297 html_canvas_arc(&path, centerX, outerRadius, outerRadius,
298 startAngles[i] * 180 / SK_ScalarPI, endAngle * 180 / SK_ScalarPI,
299 true, true);
300 path.lineTo(centerX + innerRadius * sk_float_cos(endAngle),
301 centerY + innerRadius * sk_float_sin(endAngle));
302 html_canvas_arc(&path, centerX, outerRadius, innerRadius,
303 endAngle * 180 / SK_ScalarPI, startAngles[i] * 180 / SK_ScalarPI,
304 true, false);
Mike Reedcfb130c2020-08-03 11:02:20 -0400305 canvas->drawPath(path.detach(), paint);
xidachen95e34a32016-10-19 10:24:28 -0700306 canvas->translate(20, 0);
307 }
Hal Canary607c44f2019-01-23 10:40:02 -0500308}