blob: f19822218a7163ccbc192a6ae84946d36bb89895 [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"
Mike Reedcfb130c2020-08-03 11:02:20 -040012#include "include/core/SkPathBuilder.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/core/SkPathMeasure.h"
Ben Wagnerd1701ba2019-04-30 13:44:26 -040014#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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "include/utils/SkRandom.h"
22#include "tools/ToolUtils.h"
Hal Canary41248072019-07-11 16:32:53 -040023#include "tools/timer/TimeUtils.h"
reed19d8f9f2015-01-29 10:48:16 -080024
25class AddArcGM : public skiagm::GM {
reedd9adfe62015-02-01 19:01:04 -080026public:
27 AddArcGM() : fRotate(0) {}
28
reed19d8f9f2015-01-29 10:48:16 -080029protected:
mtklein36352bf2015-03-25 18:17:31 -070030 SkString onShortName() override { return SkString("addarc"); }
reed19d8f9f2015-01-29 10:48:16 -080031
mtklein36352bf2015-03-25 18:17:31 -070032 SkISize onISize() override { return SkISize::Make(1040, 1040); }
reed19d8f9f2015-01-29 10:48:16 -080033
mtklein36352bf2015-03-25 18:17:31 -070034 void onDraw(SkCanvas* canvas) override {
reed19d8f9f2015-01-29 10:48:16 -080035 canvas->translate(20, 20);
36
37 SkRect r = SkRect::MakeWH(1000, 1000);
38
39 SkPaint paint;
40 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -040041 paint.setStroke(true);
reed19d8f9f2015-01-29 10:48:16 -080042 paint.setStrokeWidth(15);
43
44 const SkScalar inset = paint.getStrokeWidth() + 4;
45 const SkScalar sweepAngle = 345;
46 SkRandom rand;
47
reedd9adfe62015-02-01 19:01:04 -080048 SkScalar sign = 1;
reed19d8f9f2015-01-29 10:48:16 -080049 while (r.width() > paint.getStrokeWidth() * 3) {
Mike Kleinea3f0142019-03-20 11:12:10 -050050 paint.setColor(ToolUtils::color_to_565(rand.nextU() | (0xFF << 24)));
reed19d8f9f2015-01-29 10:48:16 -080051 SkScalar startAngle = rand.nextUScalar1() * 360;
52
reedd9adfe62015-02-01 19:01:04 -080053 SkScalar speed = SkScalarSqrt(16 / r.width()) * 0.5f;
54 startAngle += fRotate * 360 * speed * sign;
55
Mike Reed92f6eb12020-08-25 11:48:41 -040056 SkPathBuilder path;
reed19d8f9f2015-01-29 10:48:16 -080057 path.addArc(r, startAngle, sweepAngle);
Mike Reed92f6eb12020-08-25 11:48:41 -040058 canvas->drawPath(path.detach(), paint);
reed19d8f9f2015-01-29 10:48:16 -080059
60 r.inset(inset, inset);
reedd9adfe62015-02-01 19:01:04 -080061 sign = -sign;
reed19d8f9f2015-01-29 10:48:16 -080062 }
63 }
64
Hal Canary41248072019-07-11 16:32:53 -040065 bool onAnimate(double nanos) override {
66 fRotate = TimeUtils::Scaled(1e-9 * nanos, 1, 360);
reedd9adfe62015-02-01 19:01:04 -080067 return true;
68 }
69
reed19d8f9f2015-01-29 10:48:16 -080070private:
reedd9adfe62015-02-01 19:01:04 -080071 SkScalar fRotate;
John Stiles7571f9e2020-09-02 22:42:33 -040072 using INHERITED = skiagm::GM;
reed19d8f9f2015-01-29 10:48:16 -080073};
74DEF_GM( return new AddArcGM; )
reed61adb1b2015-02-09 13:01:05 -080075
76///////////////////////////////////////////////////
77
78#define R 400
79
Hal Canary607c44f2019-01-23 10:40:02 -050080DEF_SIMPLE_GM(addarc_meas, canvas, 2*R + 40, 2*R + 40) {
reed61adb1b2015-02-09 13:01:05 -080081 canvas->translate(R + 20, R + 20);
82
83 SkPaint paint;
84 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -040085 paint.setStroke(true);
reed61adb1b2015-02-09 13:01:05 -080086
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
Mike Reed92f6eb12020-08-25 11:48:41 -0400101 SkPathMeasure meas(SkPathBuilder().addArc(oval, 0, deg).detach(), false);
reed61adb1b2015-02-09 13:01:05 -0800102 SkScalar arcLen = rad * R;
103 SkPoint pos;
halcanary96fcdcc2015-08-27 07:41:13 -0700104 if (meas.getPosTan(arcLen, &pos, nullptr)) {
Hal Canary23e474c2017-05-15 13:35:35 -0400105 canvas->drawLine({0, 0}, pos, measPaint);
reed61adb1b2015-02-09 13:01:05 -0800106 }
107 }
Hal Canary607c44f2019-01-23 10:40:02 -0500108}
reed8ed666d2015-02-10 17:44:26 -0800109
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) {}
halcanary9d524f22016-03-29 09:03:52 -0700118
reed8ed666d2015-02-10 17:44:26 -0800119protected:
mtklein36352bf2015-03-25 18:17:31 -0700120 SkString onShortName() override { return SkString("strokecircle"); }
halcanary9d524f22016-03-29 09:03:52 -0700121
mtklein36352bf2015-03-25 18:17:31 -0700122 SkISize onISize() override { return SkISize::Make(520, 520); }
halcanary9d524f22016-03-29 09:03:52 -0700123
mtklein36352bf2015-03-25 18:17:31 -0700124 void onDraw(SkCanvas* canvas) override {
reed8ed666d2015-02-10 17:44:26 -0800125 canvas->scale(20, 20);
126 canvas->translate(13, 13);
127
128 SkPaint paint;
129 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400130 paint.setStroke(true);
reed8ed666d2015-02-10 17:44:26 -0800131 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
Mike Kleinea3f0142019-03-20 11:12:10 -0500142 paint.setColor(ToolUtils::color_to_565(rand.nextU() | (0xFF << 24)));
reed8ed666d2015-02-10 17:44:26 -0800143 canvas->drawOval(r, paint);
144 r.inset(delta, delta);
145 sign = -sign;
146 }
147 }
148
Hal Canary41248072019-07-11 16:32:53 -0400149 bool onAnimate(double nanos) override {
150 fRotate = TimeUtils::Scaled(1e-9 * nanos, 60, 360);
reed8ed666d2015-02-10 17:44:26 -0800151 return true;
152 }
153
154private:
155 SkScalar fRotate;
156
John Stiles7571f9e2020-09-02 22:42:33 -0400157 using INHERITED = skiagm::GM;
reed8ed666d2015-02-10 17:44:26 -0800158};
159DEF_GM( return new StrokeCircleGM; )
reed9e779d42015-02-17 11:43:14 -0800160
161//////////////////////
162
liyuqiane60d8552016-10-03 13:49:37 -0700163// 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);
Mike Reed19630092020-05-18 21:25:44 -0400180 paint.setStroke(true);
liyuqiane60d8552016-10-03 13:49:37 -0700181 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
Mike Reed19630092020-05-18 21:25:44 -0400189 paint.setStroke(false);
liyuqiane60d8552016-10-03 13:49:37 -0700190
191 SkScalar sign = 1;
192 while (r.width() > strokeWidth * 2) {
193 SkAutoCanvasRestore acr(canvas, true);
194 canvas->rotate(fRotate * sign);
Mike Kleinea3f0142019-03-20 11:12:10 -0500195 paint.setColor(ToolUtils::color_to_565(rand.nextU() | (0xFF << 24)));
liyuqiane60d8552016-10-03 13:49:37 -0700196 canvas->drawOval(r, paint);
197 r.inset(delta, delta);
198 sign = -sign;
199 }
200 }
201
Hal Canary41248072019-07-11 16:32:53 -0400202 bool onAnimate(double nanos) override {
203 fRotate = TimeUtils::Scaled(1e-9 * nanos, 60, 360);
liyuqiane60d8552016-10-03 13:49:37 -0700204 return true;
205 }
206
207private:
208 SkScalar fRotate;
209
John Stiles7571f9e2020-09-02 22:42:33 -0400210 using INHERITED = skiagm::GM;
liyuqiane60d8552016-10-03 13:49:37 -0700211};
212DEF_GM( return new FillCircleGM; )
213
214//////////////////////
215
Mike Reedcfb130c2020-08-03 11:02:20 -0400216static void html_canvas_arc(SkPathBuilder* path, SkScalar x, SkScalar y, SkScalar r, SkScalar start,
xidachen6069dda2016-10-06 05:42:23 -0700217 SkScalar end, bool ccw, bool callArcTo) {
reed9e779d42015-02-17 11:43:14 -0800218 SkRect bounds = { x - r, y - r, x + r, y + r };
219 SkScalar sweep = ccw ? end - start : start - end;
xidachen6069dda2016-10-06 05:42:23 -0700220 if (callArcTo)
221 path->arcTo(bounds, start, sweep, false);
222 else
223 path->addArc(bounds, start, sweep);
reed9e779d42015-02-17 11:43:14 -0800224}
225
226// Lifted from canvas-arc-circumference-fill-diffs.html
Hal Canary607c44f2019-01-23 10:40:02 -0500227DEF_SIMPLE_GM(manyarcs, canvas, 620, 330) {
reed9e779d42015-02-17 11:43:14 -0800228 SkPaint paint;
229 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400230 paint.setStroke(true);
reed9e779d42015-02-17 11:43:14 -0800231
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 }
halcanary9d524f22016-03-29 09:03:52 -0700242
reed9e779d42015-02-17 11:43:14 -0800243 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 }
halcanary9d524f22016-03-29 09:03:52 -0700247
reed9e779d42015-02-17 11:43:14 -0800248 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) {
Mike Reedcfb130c2020-08-03 11:02:20 -0400258 SkPathBuilder path;
reed9e779d42015-02-17 11:43:14 -0800259 path.moveTo(0, 2);
260 html_canvas_arc(&path, 18, 15, 10, startAngle, startAngle + (sweepAngles[j] * sign),
xidachen6069dda2016-10-06 05:42:23 -0700261 anticlockwise, true);
reed9e779d42015-02-17 11:43:14 -0800262 path.lineTo(0, 28);
Mike Reedcfb130c2020-08-03 11:02:20 -0400263 canvas->drawPath(path.detach(), paint);
reed9e779d42015-02-17 11:43:14 -0800264 canvas->translate(30, 0);
265 }
266 canvas->restore();
267 canvas->translate(0, 40);
268 }
Hal Canary607c44f2019-01-23 10:40:02 -0500269}
xidachen6069dda2016-10-06 05:42:23 -0700270
271// Lifted from https://bugs.chromium.org/p/chromium/issues/detail?id=640031
Hal Canary607c44f2019-01-23 10:40:02 -0500272DEF_SIMPLE_GM(tinyanglearcs, canvas, 620, 330) {
xidachen6069dda2016-10-06 05:42:23 -0700273 SkPaint paint;
274 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400275 paint.setStroke(true);
xidachen6069dda2016-10-06 05:42:23 -0700276
277 canvas->translate(50, 50);
278
xidachen95e34a32016-10-19 10:24:28 -0700279 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) {
Mike Reedcfb130c2020-08-03 11:02:20 -0400287 SkPathBuilder path;
xidachen95e34a32016-10-19 10:24:28 -0700288 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);
Mike Reedcfb130c2020-08-03 11:02:20 -0400302 canvas->drawPath(path.detach(), paint);
xidachen95e34a32016-10-19 10:24:28 -0700303 canvas->translate(20, 0);
304 }
Hal Canary607c44f2019-01-23 10:40:02 -0500305}