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