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 <functional>
9#include "SkCanvas.h"
10#include "SkDashPathEffect.h"
11#include "gm.h"
12
13constexpr SkScalar kStarts[] = {0.f, 10.f, 30.f, 45.f, 90.f, 165.f, 180.f, 270.f};
14constexpr SkScalar kSweeps[] = {1.f, 45.f, 90.f, 130.f, 180.f, 184.f, 300.f, 355.f};
15constexpr SkScalar kDiameter = 40.f;
16constexpr SkRect kRect = {0.f, 0.f, kDiameter, kDiameter};
17constexpr int kW = 1000;
18constexpr int kH = 1000;
19constexpr SkScalar kPad = 20.f;
20
21void draw_arcs(SkCanvas* canvas, std::function<void(SkPaint*)> configureStyle) {
22    // Draws grid of arcs with different start/sweep angles in red and their complement arcs in
23    // blue.
24    auto drawGrid = [canvas, &configureStyle] (SkScalar x, SkScalar y, bool useCenter, bool aa) {
25        SkPaint p0;
26        p0.setColor(SK_ColorRED);
27        p0.setAntiAlias(aa);
28        // Set a reasonable stroke width that configureStyle can override.
29        p0.setStrokeWidth(15.f);
30        SkPaint p1 = p0;
31        p1.setColor(SK_ColorBLUE);
32        // Use alpha so we see magenta on overlap between arc and its complement.
33        p0.setAlpha(100);
34        p1.setAlpha(100);
35        configureStyle(&p0);
36        configureStyle(&p1);
37
38        canvas->save();
39        canvas->translate(kPad + x, kPad + y);
40        for (auto start : kStarts) {
41            canvas->save();
42            for (auto sweep : kSweeps) {
43                canvas->drawArc(kRect, start, sweep, useCenter, p0);
44                canvas->drawArc(kRect, start, -(360.f - sweep), useCenter, p1);
45                canvas->translate(kRect.width() + kPad, 0.f);
46            }
47            canvas->restore();
48            canvas->translate(0, kRect.height() + kPad);
49        }
50        canvas->restore();
51    };
52    // Draw a grids for combo of enabling/disabling aa and using center.
53    constexpr SkScalar kGridW = kW / 2.f;
54    constexpr SkScalar kGridH = kH / 2.f;
55    drawGrid(0.f   , 0.f   , false, false);
56    drawGrid(kGridW, 0.f   , true , false);
57    drawGrid(0.f   , kGridH, false, true );
58    drawGrid(kGridW, kGridH, true , true );
59    // Draw separators between the grids.
60    SkPaint linePaint;
61    linePaint.setAntiAlias(true);
62    linePaint.setColor(SK_ColorBLACK);
63    canvas->drawLine(kGridW, 0.f   , kGridW,            SkIntToScalar(kH), linePaint);
64    canvas->drawLine(0.f   , kGridH, SkIntToScalar(kW), kGridH,            linePaint);
65}
66
67#define DEF_ARC_GM(name) DEF_SIMPLE_GM(circular_arcs_##name, canvas, kW, kH)
68
69DEF_ARC_GM(fill) {
70    auto setFill = [] (SkPaint*p) { p->setStyle(SkPaint::kFill_Style); };
71    draw_arcs(canvas, setFill);
72}
73
74DEF_ARC_GM(hairline) {
75    auto setHairline = [] (SkPaint* p) {
76        p->setStyle(SkPaint::kStroke_Style);
77        p->setStrokeWidth(0.f);
78    };
79    draw_arcs(canvas, setHairline);
80}
81
82DEF_ARC_GM(stroke_butt) {
83    auto setStroke = [](SkPaint* p) {
84        p->setStyle(SkPaint::kStroke_Style);
85        p->setStrokeCap(SkPaint::kButt_Cap);
86    };
87    draw_arcs(canvas, setStroke);
88}
89
90DEF_ARC_GM(stroke_square) {
91    auto setStroke = [] (SkPaint* p) {
92        p->setStyle(SkPaint::kStroke_Style);
93        p->setStrokeCap(SkPaint::kSquare_Cap);
94    };
95    draw_arcs(canvas, setStroke);
96}
97
98DEF_ARC_GM(stroke_round) {
99    auto setStroke = [] (SkPaint* p) {
100        p->setStyle(SkPaint::kStroke_Style);
101        p->setStrokeCap(SkPaint::kRound_Cap);
102    };
103    draw_arcs(canvas, setStroke);
104}
105
106DEF_ARC_GM(stroke_and_fill_butt) {
107    auto setStroke = [] (SkPaint* p) {
108        p->setStyle(SkPaint::kStrokeAndFill_Style);
109        p->setStrokeCap(SkPaint::kButt_Cap);
110    };
111    draw_arcs(canvas, setStroke);
112}
113
114DEF_ARC_GM(stroke_and_fill_square) {
115    auto setStroke = [] (SkPaint* p) {
116        p->setStyle(SkPaint::kStrokeAndFill_Style);
117        p->setStrokeCap(SkPaint::kSquare_Cap);
118    };
119    draw_arcs(canvas, setStroke);
120}
121
122DEF_ARC_GM(stroke_and_fill_round) {
123    auto setStroke = [] (SkPaint* p) {
124        p->setStyle(SkPaint::kStrokeAndFill_Style);
125        p->setStrokeCap(SkPaint::kRound_Cap);
126    };
127    draw_arcs(canvas, setStroke);
128}
129
130DEF_SIMPLE_GM(circular_arcs_weird, canvas, 1000, 400) {
131    constexpr SkScalar kS = 50;
132    struct Arc {
133        SkRect   fOval;
134        SkScalar fStart;
135        SkScalar fSweep;
136    };
137    const Arc noDrawArcs[] = {
138        // no sweep
139        {SkRect::MakeWH(kS, kS),  0,  0},
140        // empty rect in x
141        {SkRect::MakeWH(-kS, kS), 0, 90},
142        // empty rect in y
143        {SkRect::MakeWH(kS, -kS), 0, 90},
144        // empty rect in x and y
145        {SkRect::MakeWH( 0,   0), 0, 90},
146    };
147    const Arc arcs[] = {
148        // large start
149        {SkRect::MakeWH(kS, kS),   810.f,   90.f},
150        // large negative start
151        {SkRect::MakeWH(kS, kS),  -810.f,   90.f},
152        // exactly 360 sweep
153        {SkRect::MakeWH(kS, kS),     0.f,  360.f},
154        // exactly -360 sweep
155        {SkRect::MakeWH(kS, kS),     0.f, -360.f},
156        // exactly 540 sweep
157        {SkRect::MakeWH(kS, kS),     0.f,  540.f},
158        // exactly -540 sweep
159        {SkRect::MakeWH(kS, kS),     0.f, -540.f},
160        // generic large sweep and large start
161        {SkRect::MakeWH(kS, kS),  1125.f,  990.f},
162    };
163    SkTArray<SkPaint> paints;
164    // fill
165    paints.push_back();
166    // stroke
167    paints.push_back().setStyle(SkPaint::kStroke_Style);
168    paints.back().setStrokeWidth(kS / 6.f);
169    // hairline
170    paints.push_back().setStyle(SkPaint::kStroke_Style);
171    paints.back().setStrokeWidth(0.f);
172    // stroke and fill
173    paints.push_back().setStyle(SkPaint::kStrokeAndFill_Style);
174    paints.back().setStrokeWidth(kS / 6.f);
175    // dash effect
176    paints.push_back().setStyle(SkPaint::kStroke_Style);
177    paints.back().setStrokeWidth(kS / 6.f);
178    constexpr SkScalar kDashIntervals[] = {kS / 15, 2 * kS / 15};
179    paints.back().setPathEffect(SkDashPathEffect::Make(kDashIntervals, 2, 0.f));
180
181    canvas->translate(kPad, kPad);
182    // This loop should draw nothing.
183    for (auto arc : noDrawArcs) {
184        for (auto paint : paints) {
185            paint.setAntiAlias(true);
186            canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint);
187            canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint);
188        }
189    }
190
191    SkPaint linePaint;
192    linePaint.setAntiAlias(true);
193    linePaint.setColor(SK_ColorRED);
194    SkScalar midX   = SK_ARRAY_COUNT(arcs) * (kS + kPad) - kPad/2.f;
195    SkScalar height = paints.count() * (kS + kPad);
196    canvas->drawLine(midX, -kPad, midX, height, linePaint);
197
198    for (auto paint : paints) {
199        paint.setAntiAlias(true);
200        canvas->save();
201        for (auto arc : arcs) {
202            canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint);
203            canvas->translate(kS + kPad, 0.f);
204        }
205        for (auto arc : arcs) {
206            canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint);
207            canvas->translate(kS + kPad, 0.f);
208        }
209        canvas->restore();
210        canvas->translate(0, kS + kPad);
211    }
212}
213
214DEF_SIMPLE_GM(onebadarc, canvas, 100, 100) {
215    SkPath path;
216    path.moveTo(SkBits2Float(0x41a00000), SkBits2Float(0x41a00000));  // 20, 20
217    path.lineTo(SkBits2Float(0x4208918c), SkBits2Float(0x4208918c));  // 34.1421f, 34.1421f
218    path.conicTo(SkBits2Float(0x41a00000), SkBits2Float(0x42412318),  // 20, 48.2843f
219            SkBits2Float(0x40bb73a0), SkBits2Float(0x4208918c),       // 5.85786f, 34.1421f
220            SkBits2Float(0x3f3504f3));                                // 0.707107f
221    path.quadTo(SkBits2Float(0x40bb73a0), SkBits2Float(0x4208918c),   // 5.85786f, 34.1421f
222            SkBits2Float(0x40bb73a2), SkBits2Float(0x4208918c));      // 5.85787f, 34.1421f
223    path.lineTo(SkBits2Float(0x41a00000), SkBits2Float(0x41a00000));  // 20, 20
224    path.close();
225    SkPaint p0;
226    p0.setColor(SK_ColorRED);
227    p0.setStrokeWidth(15.f);
228    p0.setStyle(SkPaint::kStroke_Style);
229    p0.setAlpha(100);
230    canvas->translate(20, 0);
231    canvas->drawPath(path, p0);
232
233    SkRect kRect = { 60, 0, 100, 40};
234    canvas->drawArc(kRect, 45, 90, true, p0);
235}
236