xref: /third_party/skia/gm/circulararcs.cpp (revision cb93a386)
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/SkFloatBits.h"
19#include "include/private/SkTArray.h"
20
21#include <functional>
22
23#include "include/effects/SkStrokeAndFillPathEffect.h"
24static void set_strokeandfill(SkPaint* paint) {
25    SkASSERT(paint->getPathEffect() == nullptr);
26    paint->setPathEffect(SkStrokeAndFillPathEffect::Make());
27    paint->setStroke(true);
28}
29
30constexpr SkScalar kStarts[] = {0.f, 10.f, 30.f, 45.f, 90.f, 165.f, 180.f, 270.f};
31constexpr SkScalar kSweeps[] = {1.f, 45.f, 90.f, 130.f, 180.f, 184.f, 300.f, 355.f};
32constexpr SkScalar kDiameter = 40.f;
33constexpr SkRect kRect = {0.f, 0.f, kDiameter, kDiameter};
34constexpr int kW = 1000;
35constexpr int kH = 1000;
36constexpr SkScalar kPad = 20.f;
37
38void draw_arcs(SkCanvas* canvas, std::function<void(SkPaint*)> configureStyle) {
39    // Draws grid of arcs with different start/sweep angles in red and their complement arcs in
40    // blue.
41    auto drawGrid = [canvas, &configureStyle] (SkScalar x, SkScalar y, bool useCenter, bool aa) {
42        SkPaint p0;
43        p0.setColor(SK_ColorRED);
44        p0.setAntiAlias(aa);
45        // Set a reasonable stroke width that configureStyle can override.
46        p0.setStrokeWidth(15.f);
47        SkPaint p1 = p0;
48        p1.setColor(SK_ColorBLUE);
49        // Use alpha so we see magenta on overlap between arc and its complement.
50        p0.setAlpha(100);
51        p1.setAlpha(100);
52        configureStyle(&p0);
53        configureStyle(&p1);
54
55        canvas->save();
56        canvas->translate(kPad + x, kPad + y);
57        for (auto start : kStarts) {
58            canvas->save();
59            for (auto sweep : kSweeps) {
60                canvas->drawArc(kRect, start, sweep, useCenter, p0);
61                canvas->drawArc(kRect, start, -(360.f - sweep), useCenter, p1);
62                canvas->translate(kRect.width() + kPad, 0.f);
63            }
64            canvas->restore();
65            canvas->translate(0, kRect.height() + kPad);
66        }
67        canvas->restore();
68    };
69    // Draw a grids for combo of enabling/disabling aa and using center.
70    constexpr SkScalar kGridW = kW / 2.f;
71    constexpr SkScalar kGridH = kH / 2.f;
72    drawGrid(0.f   , 0.f   , false, false);
73    drawGrid(kGridW, 0.f   , true , false);
74    drawGrid(0.f   , kGridH, false, true );
75    drawGrid(kGridW, kGridH, true , true );
76    // Draw separators between the grids.
77    SkPaint linePaint;
78    linePaint.setAntiAlias(true);
79    linePaint.setColor(SK_ColorBLACK);
80    canvas->drawLine(kGridW, 0.f   , kGridW,            SkIntToScalar(kH), linePaint);
81    canvas->drawLine(0.f   , kGridH, SkIntToScalar(kW), kGridH,            linePaint);
82}
83
84#define DEF_ARC_GM(name) DEF_SIMPLE_GM(circular_arcs_##name, canvas, kW, kH)
85
86DEF_ARC_GM(fill) {
87    auto setFill = [] (SkPaint*p) { p->setStroke(false); };
88    draw_arcs(canvas, setFill);
89}
90
91DEF_ARC_GM(hairline) {
92    auto setHairline = [] (SkPaint* p) {
93        p->setStroke(true);
94        p->setStrokeWidth(0.f);
95    };
96    draw_arcs(canvas, setHairline);
97}
98
99DEF_ARC_GM(stroke_butt) {
100    auto setStroke = [](SkPaint* p) {
101        p->setStroke(true);
102        p->setStrokeCap(SkPaint::kButt_Cap);
103    };
104    draw_arcs(canvas, setStroke);
105}
106
107DEF_ARC_GM(stroke_square) {
108    auto setStroke = [] (SkPaint* p) {
109        p->setStroke(true);
110        p->setStrokeCap(SkPaint::kSquare_Cap);
111    };
112    draw_arcs(canvas, setStroke);
113}
114
115DEF_ARC_GM(stroke_round) {
116    auto setStroke = [] (SkPaint* p) {
117        p->setStroke(true);
118        p->setStrokeCap(SkPaint::kRound_Cap);
119    };
120    draw_arcs(canvas, setStroke);
121}
122
123DEF_ARC_GM(stroke_and_fill_butt) {
124    auto setStroke = [] (SkPaint* p) {
125        set_strokeandfill(p);
126        p->setStrokeCap(SkPaint::kButt_Cap);
127    };
128    draw_arcs(canvas, setStroke);
129}
130
131DEF_ARC_GM(stroke_and_fill_square) {
132    auto setStroke = [] (SkPaint* p) {
133        set_strokeandfill(p);
134        p->setStrokeCap(SkPaint::kSquare_Cap);
135    };
136    draw_arcs(canvas, setStroke);
137}
138
139DEF_ARC_GM(stroke_and_fill_round) {
140    auto setStroke = [] (SkPaint* p) {
141        set_strokeandfill(p);
142        p->setStrokeCap(SkPaint::kRound_Cap);
143    };
144    draw_arcs(canvas, setStroke);
145}
146
147DEF_SIMPLE_GM(circular_arcs_weird, canvas, 1000, 400) {
148    constexpr SkScalar kS = 50;
149    struct Arc {
150        SkRect   fOval;
151        SkScalar fStart;
152        SkScalar fSweep;
153    };
154    const Arc noDrawArcs[] = {
155        // no sweep
156        {SkRect::MakeWH(kS, kS),  0,  0},
157        // empty rect in x
158        {SkRect::MakeWH(-kS, kS), 0, 90},
159        // empty rect in y
160        {SkRect::MakeWH(kS, -kS), 0, 90},
161        // empty rect in x and y
162        {SkRect::MakeWH( 0,   0), 0, 90},
163    };
164    const Arc arcs[] = {
165        // large start
166        {SkRect::MakeWH(kS, kS),   810.f,   90.f},
167        // large negative start
168        {SkRect::MakeWH(kS, kS),  -810.f,   90.f},
169        // exactly 360 sweep
170        {SkRect::MakeWH(kS, kS),     0.f,  360.f},
171        // exactly -360 sweep
172        {SkRect::MakeWH(kS, kS),     0.f, -360.f},
173        // exactly 540 sweep
174        {SkRect::MakeWH(kS, kS),     0.f,  540.f},
175        // exactly -540 sweep
176        {SkRect::MakeWH(kS, kS),     0.f, -540.f},
177        // generic large sweep and large start
178        {SkRect::MakeWH(kS, kS),  1125.f,  990.f},
179    };
180    SkTArray<SkPaint> paints;
181    // fill
182    paints.push_back();
183    // stroke
184    paints.push_back().setStroke(true);
185    paints.back().setStrokeWidth(kS / 6.f);
186    // hairline
187    paints.push_back().setStroke(true);
188    paints.back().setStrokeWidth(0.f);
189    // stroke and fill
190    paints.push_back().setStyle(SkPaint::kStrokeAndFill_Style);
191    paints.back().setStrokeWidth(kS / 6.f);
192    // dash effect
193    paints.push_back().setStroke(true);
194    paints.back().setStrokeWidth(kS / 6.f);
195    constexpr SkScalar kDashIntervals[] = {kS / 15, 2 * kS / 15};
196    paints.back().setPathEffect(SkDashPathEffect::Make(kDashIntervals, 2, 0.f));
197
198    canvas->translate(kPad, kPad);
199    // This loop should draw nothing.
200    for (auto arc : noDrawArcs) {
201        for (auto paint : paints) {
202            paint.setAntiAlias(true);
203            canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint);
204            canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint);
205        }
206    }
207
208    SkPaint linePaint;
209    linePaint.setAntiAlias(true);
210    linePaint.setColor(SK_ColorRED);
211    SkScalar midX   = SK_ARRAY_COUNT(arcs) * (kS + kPad) - kPad/2.f;
212    SkScalar height = paints.count() * (kS + kPad);
213    canvas->drawLine(midX, -kPad, midX, height, linePaint);
214
215    for (auto paint : paints) {
216        paint.setAntiAlias(true);
217        canvas->save();
218        for (auto arc : arcs) {
219            canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint);
220            canvas->translate(kS + kPad, 0.f);
221        }
222        for (auto arc : arcs) {
223            canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint);
224            canvas->translate(kS + kPad, 0.f);
225        }
226        canvas->restore();
227        canvas->translate(0, kS + kPad);
228    }
229}
230
231DEF_SIMPLE_GM(onebadarc, canvas, 100, 100) {
232    SkPathBuilder path;
233    path.moveTo(SkBits2Float(0x41a00000), SkBits2Float(0x41a00000));  // 20, 20
234    path.lineTo(SkBits2Float(0x4208918c), SkBits2Float(0x4208918c));  // 34.1421f, 34.1421f
235    path.conicTo(SkBits2Float(0x41a00000), SkBits2Float(0x42412318),  // 20, 48.2843f
236            SkBits2Float(0x40bb73a0), SkBits2Float(0x4208918c),       // 5.85786f, 34.1421f
237            SkBits2Float(0x3f3504f3));                                // 0.707107f
238    path.quadTo(SkBits2Float(0x40bb73a0), SkBits2Float(0x4208918c),   // 5.85786f, 34.1421f
239            SkBits2Float(0x40bb73a2), SkBits2Float(0x4208918c));      // 5.85787f, 34.1421f
240    path.lineTo(SkBits2Float(0x41a00000), SkBits2Float(0x41a00000));  // 20, 20
241    path.close();
242    SkPaint p0;
243    p0.setColor(SK_ColorRED);
244    p0.setStrokeWidth(15.f);
245    p0.setStroke(true);
246    p0.setAlpha(100);
247    canvas->translate(20, 0);
248    canvas->drawPath(path.detach(), p0);
249
250    canvas->drawArc(SkRect{60, 0, 100, 40}, 45, 90, true, p0);
251}
252
253DEF_SIMPLE_GM(crbug_888453, canvas, 480, 150) {
254    // Two GPU path renderers were using a too-large tolerance when chopping connics to quads.
255    // This manifested as not-very-round circular arcs at certain radii. All the arcs being drawn
256    // here should look like circles.
257    SkPaint fill;
258    fill.setAntiAlias(true);
259    SkPaint hairline = fill;
260    hairline.setStroke(true);
261    SkPaint stroke = hairline;
262    stroke.setStrokeWidth(2.0f);
263    int x = 4;
264    int y0 = 25, y1 = 75, y2 = 125;
265    for (int r = 2; r <= 20; ++r) {
266        canvas->drawArc(SkRect::MakeXYWH(x - r, y0 - r, 2 * r, 2 * r), 0, 360, false, fill);
267        canvas->drawArc(SkRect::MakeXYWH(x - r, y1 - r, 2 * r, 2 * r), 0, 360, false, hairline);
268        canvas->drawArc(SkRect::MakeXYWH(x - r, y2 - r, 2 * r, 2 * r), 0, 360, false, stroke);
269        x += 2 * r + 4;
270    }
271}
272
273DEF_SIMPLE_GM(circular_arc_stroke_matrix, canvas, 820, 1090) {
274    static constexpr SkScalar kRadius = 40.f;
275    static constexpr SkScalar kStrokeWidth = 5.f;
276    static constexpr SkScalar kStart = 89.f;
277    static constexpr SkScalar kSweep = 180.f/SK_ScalarPI; // one radian
278
279    SkTArray<SkMatrix> matrices;
280    matrices.push_back().setRotate(kRadius, kRadius, 45.f);
281    matrices.push_back(SkMatrix::I());
282    matrices.push_back().setAll(-1,  0,  2*kRadius,
283                                 0,  1,  0,
284                                 0,  0,  1);
285    matrices.push_back().setAll( 1,  0,  0,
286                                 0, -1,  2*kRadius,
287                                 0,  0,  1);
288    matrices.push_back().setAll( 1,  0,  0,
289                                 0, -1,  2*kRadius,
290                                 0,  0,  1);
291    matrices.push_back().setAll( 0, -1,  2*kRadius,
292                                -1,  0,  2*kRadius,
293                                 0,  0,  1);
294    matrices.push_back().setAll( 0, -1,  2*kRadius,
295                                 1,  0,  0,
296                                 0,  0,  1);
297    matrices.push_back().setAll( 0,  1,  0,
298                                 1,  0,  0,
299                                 0,  0,  1);
300    matrices.push_back().setAll( 0,  1,  0,
301                                -1,  0,  2*kRadius,
302                                 0,  0,  1);
303    int baseMatrixCnt = matrices.count();
304
305
306    SkMatrix tinyCW;
307    tinyCW.setRotate(0.001f, kRadius, kRadius);
308    for (int i = 0; i < baseMatrixCnt; ++i) {
309        matrices.push_back().setConcat(matrices[i], tinyCW);
310    }
311    SkMatrix tinyCCW;
312    tinyCCW.setRotate(-0.001f, kRadius, kRadius);
313    for (int i = 0; i < baseMatrixCnt; ++i) {
314        matrices.push_back().setConcat(matrices[i], tinyCCW);
315    }
316    SkMatrix cw45;
317    cw45.setRotate(45.f, kRadius, kRadius);
318    for (int i = 0; i < baseMatrixCnt; ++i) {
319        matrices.push_back().setConcat(matrices[i], cw45);
320    }
321
322    int x = 0;
323    int y = 0;
324    static constexpr SkScalar kPad = 2*kStrokeWidth;
325    canvas->translate(kPad, kPad);
326    auto bounds = SkRect::MakeWH(2*kRadius, 2*kRadius);
327    for (auto cap : {SkPaint::kRound_Cap, SkPaint::kButt_Cap, SkPaint::kSquare_Cap}) {
328        for (const auto& m : matrices) {
329            SkPaint paint;
330            paint.setStrokeCap(cap);
331            paint.setAntiAlias(true);
332            paint.setStroke(true);
333            paint.setStrokeWidth(kStrokeWidth);
334            canvas->save();
335                canvas->translate(x * (2*kRadius + kPad), y * (2*kRadius + kPad));
336                canvas->concat(m);
337                paint.setColor(SK_ColorRED);
338                paint.setAlpha(0x80);
339                canvas->drawArc(bounds, kStart, kSweep, false, paint);
340                paint.setColor(SK_ColorBLUE);
341                paint.setAlpha(0x80);
342                canvas->drawArc(bounds, kStart, kSweep - 360.f, false, paint);
343            canvas->restore();
344            ++x;
345            if (x == baseMatrixCnt) {
346                x = 0;
347                ++y;
348            }
349        }
350    }
351}
352