• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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