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