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