blob: 8702263e6e037f42f91f0ed86b2c6062ccd6b771 [file] [log] [blame]
bsalomon71c5eee2016-08-19 10:53:13 -07001/*
2 * Copyright 2016 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 Wagner7fde8e12019-05-01 17:28:53 -040010#include "include/core/SkColor.h"
11#include "include/core/SkPaint.h"
Mike Reed06d7c9d2020-08-26 12:56:51 -040012#include "include/core/SkPathBuilder.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040013#include "include/core/SkPathEffect.h"
14#include "include/core/SkRect.h"
15#include "include/core/SkScalar.h"
16#include "include/core/SkTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "include/effects/SkDashPathEffect.h"
Kevin Lubickdc6cc022023-01-13 11:24:27 -050018#include "include/private/base/SkFloatBits.h"
19#include "include/private/base/SkTArray.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040020
21#include <functional>
bsalomon71c5eee2016-08-19 10:53:13 -070022
Herb Derbyec96c212023-03-06 10:31:22 -050023using namespace skia_private;
24
mtkleindbfd7ab2016-09-01 11:24:54 -070025constexpr SkScalar kStarts[] = {0.f, 10.f, 30.f, 45.f, 90.f, 165.f, 180.f, 270.f};
26constexpr SkScalar kSweeps[] = {1.f, 45.f, 90.f, 130.f, 180.f, 184.f, 300.f, 355.f};
27constexpr SkScalar kDiameter = 40.f;
28constexpr SkRect kRect = {0.f, 0.f, kDiameter, kDiameter};
29constexpr int kW = 1000;
30constexpr int kH = 1000;
bsalomon71c5eee2016-08-19 10:53:13 -070031
32void draw_arcs(SkCanvas* canvas, std::function<void(SkPaint*)> configureStyle) {
33 // Draws grid of arcs with different start/sweep angles in red and their complement arcs in
34 // blue.
35 auto drawGrid = [canvas, &configureStyle] (SkScalar x, SkScalar y, bool useCenter, bool aa) {
John Stiles6009cc62023-08-02 09:13:14 -040036 constexpr SkScalar kPad = 20.f;
bsalomon71c5eee2016-08-19 10:53:13 -070037 SkPaint p0;
38 p0.setColor(SK_ColorRED);
39 p0.setAntiAlias(aa);
40 // Set a reasonable stroke width that configureStyle can override.
41 p0.setStrokeWidth(15.f);
bsalomon71c5eee2016-08-19 10:53:13 -070042 SkPaint p1 = p0;
43 p1.setColor(SK_ColorBLUE);
bsalomon4c547882016-08-19 13:41:28 -070044 // Use alpha so we see magenta on overlap between arc and its complement.
45 p0.setAlpha(100);
46 p1.setAlpha(100);
bsalomon71c5eee2016-08-19 10:53:13 -070047 configureStyle(&p0);
48 configureStyle(&p1);
49
50 canvas->save();
51 canvas->translate(kPad + x, kPad + y);
52 for (auto start : kStarts) {
53 canvas->save();
54 for (auto sweep : kSweeps) {
55 canvas->drawArc(kRect, start, sweep, useCenter, p0);
56 canvas->drawArc(kRect, start, -(360.f - sweep), useCenter, p1);
57 canvas->translate(kRect.width() + kPad, 0.f);
58 }
59 canvas->restore();
60 canvas->translate(0, kRect.height() + kPad);
61 }
62 canvas->restore();
63 };
64 // Draw a grids for combo of enabling/disabling aa and using center.
mtkleindbfd7ab2016-09-01 11:24:54 -070065 constexpr SkScalar kGridW = kW / 2.f;
66 constexpr SkScalar kGridH = kH / 2.f;
bsalomon71c5eee2016-08-19 10:53:13 -070067 drawGrid(0.f , 0.f , false, false);
68 drawGrid(kGridW, 0.f , true , false);
69 drawGrid(0.f , kGridH, false, true );
70 drawGrid(kGridW, kGridH, true , true );
71 // Draw separators between the grids.
72 SkPaint linePaint;
73 linePaint.setAntiAlias(true);
74 linePaint.setColor(SK_ColorBLACK);
75 canvas->drawLine(kGridW, 0.f , kGridW, SkIntToScalar(kH), linePaint);
76 canvas->drawLine(0.f , kGridH, SkIntToScalar(kW), kGridH, linePaint);
77}
78
79#define DEF_ARC_GM(name) DEF_SIMPLE_GM(circular_arcs_##name, canvas, kW, kH)
80
bsalomon4c547882016-08-19 13:41:28 -070081DEF_ARC_GM(fill) {
Mike Reed19630092020-05-18 21:25:44 -040082 auto setFill = [] (SkPaint*p) { p->setStroke(false); };
bsalomon71c5eee2016-08-19 10:53:13 -070083 draw_arcs(canvas, setFill);
84}
85
86DEF_ARC_GM(hairline) {
87 auto setHairline = [] (SkPaint* p) {
Mike Reed19630092020-05-18 21:25:44 -040088 p->setStroke(true);
bsalomon71c5eee2016-08-19 10:53:13 -070089 p->setStrokeWidth(0.f);
90 };
91 draw_arcs(canvas, setHairline);
92}
93
94DEF_ARC_GM(stroke_butt) {
95 auto setStroke = [](SkPaint* p) {
Mike Reed19630092020-05-18 21:25:44 -040096 p->setStroke(true);
bsalomon71c5eee2016-08-19 10:53:13 -070097 p->setStrokeCap(SkPaint::kButt_Cap);
98 };
99 draw_arcs(canvas, setStroke);
100}
101
102DEF_ARC_GM(stroke_square) {
103 auto setStroke = [] (SkPaint* p) {
Mike Reed19630092020-05-18 21:25:44 -0400104 p->setStroke(true);
bsalomon71c5eee2016-08-19 10:53:13 -0700105 p->setStrokeCap(SkPaint::kSquare_Cap);
106 };
107 draw_arcs(canvas, setStroke);
108}
109
110DEF_ARC_GM(stroke_round) {
111 auto setStroke = [] (SkPaint* p) {
Mike Reed19630092020-05-18 21:25:44 -0400112 p->setStroke(true);
bsalomon71c5eee2016-08-19 10:53:13 -0700113 p->setStrokeCap(SkPaint::kRound_Cap);
114 };
115 draw_arcs(canvas, setStroke);
116}
bsalomonac1d0ab2016-08-22 10:00:14 -0700117
bsalomon21af9ca2016-08-25 12:29:23 -0700118DEF_SIMPLE_GM(circular_arcs_weird, canvas, 1000, 400) {
mtkleindbfd7ab2016-09-01 11:24:54 -0700119 constexpr SkScalar kS = 50;
bsalomon21af9ca2016-08-25 12:29:23 -0700120 struct Arc {
121 SkRect fOval;
122 SkScalar fStart;
123 SkScalar fSweep;
124 };
mtkleindbfd7ab2016-09-01 11:24:54 -0700125 const Arc noDrawArcs[] = {
bsalomon21af9ca2016-08-25 12:29:23 -0700126 // no sweep
127 {SkRect::MakeWH(kS, kS), 0, 0},
128 // empty rect in x
129 {SkRect::MakeWH(-kS, kS), 0, 90},
130 // empty rect in y
131 {SkRect::MakeWH(kS, -kS), 0, 90},
132 // empty rect in x and y
133 {SkRect::MakeWH( 0, 0), 0, 90},
134 };
mtkleindbfd7ab2016-09-01 11:24:54 -0700135 const Arc arcs[] = {
bsalomon21af9ca2016-08-25 12:29:23 -0700136 // large start
137 {SkRect::MakeWH(kS, kS), 810.f, 90.f},
138 // large negative start
139 {SkRect::MakeWH(kS, kS), -810.f, 90.f},
140 // exactly 360 sweep
141 {SkRect::MakeWH(kS, kS), 0.f, 360.f},
142 // exactly -360 sweep
143 {SkRect::MakeWH(kS, kS), 0.f, -360.f},
144 // exactly 540 sweep
145 {SkRect::MakeWH(kS, kS), 0.f, 540.f},
146 // exactly -540 sweep
147 {SkRect::MakeWH(kS, kS), 0.f, -540.f},
148 // generic large sweep and large start
149 {SkRect::MakeWH(kS, kS), 1125.f, 990.f},
150 };
Herb Derbyec96c212023-03-06 10:31:22 -0500151 TArray<SkPaint> paints;
bsalomon21af9ca2016-08-25 12:29:23 -0700152 // fill
153 paints.push_back();
154 // stroke
Mike Reed19630092020-05-18 21:25:44 -0400155 paints.push_back().setStroke(true);
bsalomon21af9ca2016-08-25 12:29:23 -0700156 paints.back().setStrokeWidth(kS / 6.f);
157 // hairline
Mike Reed19630092020-05-18 21:25:44 -0400158 paints.push_back().setStroke(true);
bsalomon21af9ca2016-08-25 12:29:23 -0700159 paints.back().setStrokeWidth(0.f);
160 // stroke and fill
161 paints.push_back().setStyle(SkPaint::kStrokeAndFill_Style);
162 paints.back().setStrokeWidth(kS / 6.f);
163 // dash effect
Mike Reed19630092020-05-18 21:25:44 -0400164 paints.push_back().setStroke(true);
bsalomon21af9ca2016-08-25 12:29:23 -0700165 paints.back().setStrokeWidth(kS / 6.f);
mtkleindbfd7ab2016-09-01 11:24:54 -0700166 constexpr SkScalar kDashIntervals[] = {kS / 15, 2 * kS / 15};
bsalomon21af9ca2016-08-25 12:29:23 -0700167 paints.back().setPathEffect(SkDashPathEffect::Make(kDashIntervals, 2, 0.f));
168
John Stiles6009cc62023-08-02 09:13:14 -0400169 constexpr SkScalar kPad = 20.f;
bsalomon21af9ca2016-08-25 12:29:23 -0700170 canvas->translate(kPad, kPad);
171 // This loop should draw nothing.
172 for (auto arc : noDrawArcs) {
173 for (auto paint : paints) {
174 paint.setAntiAlias(true);
175 canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint);
176 canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint);
177 }
178 }
179
180 SkPaint linePaint;
181 linePaint.setAntiAlias(true);
182 linePaint.setColor(SK_ColorRED);
Herb Derbyc37b3862022-06-21 09:49:17 -0400183 SkScalar midX = std::size(arcs) * (kS + kPad) - kPad/2.f;
Herb Derbyffacce52022-11-09 10:51:34 -0500184 SkScalar height = paints.size() * (kS + kPad);
bsalomon21af9ca2016-08-25 12:29:23 -0700185 canvas->drawLine(midX, -kPad, midX, height, linePaint);
186
187 for (auto paint : paints) {
188 paint.setAntiAlias(true);
189 canvas->save();
190 for (auto arc : arcs) {
191 canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint);
192 canvas->translate(kS + kPad, 0.f);
193 }
194 for (auto arc : arcs) {
195 canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint);
196 canvas->translate(kS + kPad, 0.f);
197 }
198 canvas->restore();
199 canvas->translate(0, kS + kPad);
200 }
201}
caryclarkf71ab8f2016-08-26 09:54:25 -0700202
203DEF_SIMPLE_GM(onebadarc, canvas, 100, 100) {
Mike Reed06d7c9d2020-08-26 12:56:51 -0400204 SkPathBuilder path;
caryclarkf71ab8f2016-08-26 09:54:25 -0700205 path.moveTo(SkBits2Float(0x41a00000), SkBits2Float(0x41a00000)); // 20, 20
206 path.lineTo(SkBits2Float(0x4208918c), SkBits2Float(0x4208918c)); // 34.1421f, 34.1421f
207 path.conicTo(SkBits2Float(0x41a00000), SkBits2Float(0x42412318), // 20, 48.2843f
208 SkBits2Float(0x40bb73a0), SkBits2Float(0x4208918c), // 5.85786f, 34.1421f
209 SkBits2Float(0x3f3504f3)); // 0.707107f
210 path.quadTo(SkBits2Float(0x40bb73a0), SkBits2Float(0x4208918c), // 5.85786f, 34.1421f
211 SkBits2Float(0x40bb73a2), SkBits2Float(0x4208918c)); // 5.85787f, 34.1421f
212 path.lineTo(SkBits2Float(0x41a00000), SkBits2Float(0x41a00000)); // 20, 20
213 path.close();
214 SkPaint p0;
215 p0.setColor(SK_ColorRED);
216 p0.setStrokeWidth(15.f);
Mike Reed19630092020-05-18 21:25:44 -0400217 p0.setStroke(true);
caryclarkf71ab8f2016-08-26 09:54:25 -0700218 p0.setAlpha(100);
219 canvas->translate(20, 0);
Mike Reed06d7c9d2020-08-26 12:56:51 -0400220 canvas->drawPath(path.detach(), p0);
caryclarkf71ab8f2016-08-26 09:54:25 -0700221
John Stilesa212b952021-08-02 13:26:38 -0400222 canvas->drawArc(SkRect{60, 0, 100, 40}, 45, 90, true, p0);
caryclarkf71ab8f2016-08-26 09:54:25 -0700223}
Brian Osmane3deee12018-11-20 11:10:15 -0500224
225DEF_SIMPLE_GM(crbug_888453, canvas, 480, 150) {
226 // Two GPU path renderers were using a too-large tolerance when chopping connics to quads.
227 // This manifested as not-very-round circular arcs at certain radii. All the arcs being drawn
228 // here should look like circles.
229 SkPaint fill;
230 fill.setAntiAlias(true);
231 SkPaint hairline = fill;
Mike Reed19630092020-05-18 21:25:44 -0400232 hairline.setStroke(true);
Brian Osmane3deee12018-11-20 11:10:15 -0500233 SkPaint stroke = hairline;
234 stroke.setStrokeWidth(2.0f);
235 int x = 4;
236 int y0 = 25, y1 = 75, y2 = 125;
237 for (int r = 2; r <= 20; ++r) {
238 canvas->drawArc(SkRect::MakeXYWH(x - r, y0 - r, 2 * r, 2 * r), 0, 360, false, fill);
239 canvas->drawArc(SkRect::MakeXYWH(x - r, y1 - r, 2 * r, 2 * r), 0, 360, false, hairline);
240 canvas->drawArc(SkRect::MakeXYWH(x - r, y2 - r, 2 * r, 2 * r), 0, 360, false, stroke);
241 x += 2 * r + 4;
242 }
243}
Brian Salomon3517aa72019-12-11 08:16:22 -0500244
245DEF_SIMPLE_GM(circular_arc_stroke_matrix, canvas, 820, 1090) {
246 static constexpr SkScalar kRadius = 40.f;
247 static constexpr SkScalar kStrokeWidth = 5.f;
248 static constexpr SkScalar kStart = 89.f;
249 static constexpr SkScalar kSweep = 180.f/SK_ScalarPI; // one radian
250
Herb Derbyec96c212023-03-06 10:31:22 -0500251 TArray<SkMatrix> matrices;
Brian Salomon3517aa72019-12-11 08:16:22 -0500252 matrices.push_back().setRotate(kRadius, kRadius, 45.f);
253 matrices.push_back(SkMatrix::I());
254 matrices.push_back().setAll(-1, 0, 2*kRadius,
255 0, 1, 0,
256 0, 0, 1);
257 matrices.push_back().setAll( 1, 0, 0,
258 0, -1, 2*kRadius,
259 0, 0, 1);
260 matrices.push_back().setAll( 1, 0, 0,
261 0, -1, 2*kRadius,
262 0, 0, 1);
263 matrices.push_back().setAll( 0, -1, 2*kRadius,
264 -1, 0, 2*kRadius,
265 0, 0, 1);
266 matrices.push_back().setAll( 0, -1, 2*kRadius,
267 1, 0, 0,
268 0, 0, 1);
269 matrices.push_back().setAll( 0, 1, 0,
270 1, 0, 0,
271 0, 0, 1);
272 matrices.push_back().setAll( 0, 1, 0,
273 -1, 0, 2*kRadius,
274 0, 0, 1);
Herb Derbyffacce52022-11-09 10:51:34 -0500275 int baseMatrixCnt = matrices.size();
Brian Salomon3517aa72019-12-11 08:16:22 -0500276
277
278 SkMatrix tinyCW;
279 tinyCW.setRotate(0.001f, kRadius, kRadius);
280 for (int i = 0; i < baseMatrixCnt; ++i) {
281 matrices.push_back().setConcat(matrices[i], tinyCW);
282 }
283 SkMatrix tinyCCW;
284 tinyCCW.setRotate(-0.001f, kRadius, kRadius);
285 for (int i = 0; i < baseMatrixCnt; ++i) {
286 matrices.push_back().setConcat(matrices[i], tinyCCW);
287 }
288 SkMatrix cw45;
289 cw45.setRotate(45.f, kRadius, kRadius);
290 for (int i = 0; i < baseMatrixCnt; ++i) {
291 matrices.push_back().setConcat(matrices[i], cw45);
292 }
293
294 int x = 0;
295 int y = 0;
296 static constexpr SkScalar kPad = 2*kStrokeWidth;
297 canvas->translate(kPad, kPad);
298 auto bounds = SkRect::MakeWH(2*kRadius, 2*kRadius);
299 for (auto cap : {SkPaint::kRound_Cap, SkPaint::kButt_Cap, SkPaint::kSquare_Cap}) {
300 for (const auto& m : matrices) {
301 SkPaint paint;
302 paint.setStrokeCap(cap);
303 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400304 paint.setStroke(true);
Brian Salomon3517aa72019-12-11 08:16:22 -0500305 paint.setStrokeWidth(kStrokeWidth);
306 canvas->save();
307 canvas->translate(x * (2*kRadius + kPad), y * (2*kRadius + kPad));
308 canvas->concat(m);
309 paint.setColor(SK_ColorRED);
310 paint.setAlpha(0x80);
311 canvas->drawArc(bounds, kStart, kSweep, false, paint);
312 paint.setColor(SK_ColorBLUE);
313 paint.setAlpha(0x80);
314 canvas->drawArc(bounds, kStart, kSweep - 360.f, false, paint);
315 canvas->restore();
316 ++x;
317 if (x == baseMatrixCnt) {
318 x = 0;
319 ++y;
320 }
321 }
322 }
323}
Michael Ludwigd41d6af2023-11-27 20:45:34 -0500324
325DEF_SIMPLE_GM(crbug_1472747, canvas, 400, 400) {
326 auto addCanvas2dCircleArcTo = [](float cx, float cy, float radius, SkPath* path) {
327 SkRect oval = SkRect::MakeLTRB(cx - radius, cy - radius, cx + radius, cy + radius);
328 // arcTo(oval, 0, 2pi, anticlockwise) gets split to 0->-180,-180->-360
329 path->arcTo(oval, 0.f, -180.f, false);
330 path->arcTo(oval, -180.f, -180.f, false);
331 };
332
333 // This manually stroked circle is large enough to trigger pre-chopping in the
334 // tessellation path renderers, but uses a non-default winding mode, which
335 // originally was not preserved in the chopped path.
336 static constexpr float kRadius = 31000.f;
337 SkPath strokedCircle;
338 addCanvas2dCircleArcTo(0.f, kRadius + 10.f, kRadius, &strokedCircle); // inner
339 addCanvas2dCircleArcTo(0.f, kRadius + 10.f, kRadius + 5.f, &strokedCircle); // outer
340 strokedCircle.setFillType(SkPathFillType::kEvenOdd);
341
342 SkPaint fill;
343 fill.setAntiAlias(true);
344 canvas->drawPath(strokedCircle, fill);
345}