xref: /aosp_15_r20/external/skia/gm/circulararcs.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
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 
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/SkPathBuilder.h"
13 #include "include/core/SkPathEffect.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkTypes.h"
17 #include "include/effects/SkDashPathEffect.h"
18 #include "include/private/base/SkTArray.h"
19 #include "src/base/SkFloatBits.h"
20 
21 #include <functional>
22 
23 using namespace skia_private;
24 
25 constexpr SkScalar kStarts[] = {0.f, 10.f, 30.f, 45.f, 90.f, 165.f, 180.f, 270.f};
26 constexpr SkScalar kSweeps[] = {1.f, 45.f, 90.f, 130.f, 180.f, 184.f, 300.f, 355.f};
27 constexpr SkScalar kDiameter = 40.f;
28 constexpr SkRect kRect = {0.f, 0.f, kDiameter, kDiameter};
29 constexpr int kW = 1000;
30 constexpr int kH = 1000;
31 
draw_arcs(SkCanvas * canvas,std::function<void (SkPaint *)> configureStyle)32 void 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) {
36         constexpr SkScalar kPad = 20.f;
37         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);
42         SkPaint p1 = p0;
43         p1.setColor(SK_ColorBLUE);
44         // Use alpha so we see magenta on overlap between arc and its complement.
45         p0.setAlpha(100);
46         p1.setAlpha(100);
47         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.
65     constexpr SkScalar kGridW = kW / 2.f;
66     constexpr SkScalar kGridH = kH / 2.f;
67     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 
DEF_ARC_GM(fill)81 DEF_ARC_GM(fill) {
82     auto setFill = [] (SkPaint*p) { p->setStroke(false); };
83     draw_arcs(canvas, setFill);
84 }
85 
DEF_ARC_GM(hairline)86 DEF_ARC_GM(hairline) {
87     auto setHairline = [] (SkPaint* p) {
88         p->setStroke(true);
89         p->setStrokeWidth(0.f);
90     };
91     draw_arcs(canvas, setHairline);
92 }
93 
DEF_ARC_GM(stroke_butt)94 DEF_ARC_GM(stroke_butt) {
95     auto setStroke = [](SkPaint* p) {
96         p->setStroke(true);
97         p->setStrokeCap(SkPaint::kButt_Cap);
98     };
99     draw_arcs(canvas, setStroke);
100 }
101 
DEF_ARC_GM(stroke_square)102 DEF_ARC_GM(stroke_square) {
103     auto setStroke = [] (SkPaint* p) {
104         p->setStroke(true);
105         p->setStrokeCap(SkPaint::kSquare_Cap);
106     };
107     draw_arcs(canvas, setStroke);
108 }
109 
DEF_ARC_GM(stroke_round)110 DEF_ARC_GM(stroke_round) {
111     auto setStroke = [] (SkPaint* p) {
112         p->setStroke(true);
113         p->setStrokeCap(SkPaint::kRound_Cap);
114     };
115     draw_arcs(canvas, setStroke);
116 }
117 
118 DEF_SIMPLE_GM(circular_arcs_weird, canvas, 1000, 400) {
119     constexpr SkScalar kS = 50;
120     struct Arc {
121         SkRect   fOval;
122         SkScalar fStart;
123         SkScalar fSweep;
124     };
125     const Arc noDrawArcs[] = {
126         // 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     };
135     const Arc arcs[] = {
136         // 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     };
151     TArray<SkPaint> paints;
152     // fill
153     paints.push_back();
154     // stroke
155     paints.push_back().setStroke(true);
156     paints.back().setStrokeWidth(kS / 6.f);
157     // hairline
158     paints.push_back().setStroke(true);
159     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
164     paints.push_back().setStroke(true);
165     paints.back().setStrokeWidth(kS / 6.f);
166     constexpr SkScalar kDashIntervals[] = {kS / 15, 2 * kS / 15};
167     paints.back().setPathEffect(SkDashPathEffect::Make(kDashIntervals, 2, 0.f));
168 
169     constexpr SkScalar kPad = 20.f;
170     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);
183     SkScalar midX   = std::size(arcs) * (kS + kPad) - kPad/2.f;
184     SkScalar height = paints.size() * (kS + kPad);
185     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 }
202 
203 DEF_SIMPLE_GM(onebadarc, canvas, 100, 100) {
204     SkPathBuilder path;
205     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);
217     p0.setStroke(true);
218     p0.setAlpha(100);
219     canvas->translate(20, 0);
220     canvas->drawPath(path.detach(), p0);
221 
222     canvas->drawArc(SkRect{60, 0, 100, 40}, 45, 90, true, p0);
223 }
224 
225 DEF_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;
232     hairline.setStroke(true);
233     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 }
244 
245 DEF_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 
251     TArray<SkMatrix> matrices;
252     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);
275     int baseMatrixCnt = matrices.size();
276 
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);
304             paint.setStroke(true);
305             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 }
324 
325 DEF_SIMPLE_GM(crbug_1472747, canvas, 400, 400) {
__anon692beef00702(float cx, float cy, float radius, SkPath* path) 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 }
346