• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"
set_strokeandfill(SkPaint * paint)24 static void set_strokeandfill(SkPaint* paint) {
25     SkASSERT(paint->getPathEffect() == nullptr);
26     paint->setPathEffect(SkStrokeAndFillPathEffect::Make());
27     paint->setStroke(true);
28 }
29 
30 constexpr SkScalar kStarts[] = {0.f, 10.f, 30.f, 45.f, 90.f, 165.f, 180.f, 270.f};
31 constexpr SkScalar kSweeps[] = {1.f, 45.f, 90.f, 130.f, 180.f, 184.f, 300.f, 355.f};
32 constexpr SkScalar kDiameter = 40.f;
33 constexpr SkRect kRect = {0.f, 0.f, kDiameter, kDiameter};
34 constexpr int kW = 1000;
35 constexpr int kH = 1000;
36 constexpr SkScalar kPad = 20.f;
37 
draw_arcs(SkCanvas * canvas,std::function<void (SkPaint *)> configureStyle)38 void 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 
DEF_ARC_GM(fill)86 DEF_ARC_GM(fill) {
87     auto setFill = [] (SkPaint*p) { p->setStroke(false); };
88     draw_arcs(canvas, setFill);
89 }
90 
DEF_ARC_GM(hairline)91 DEF_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 
DEF_ARC_GM(stroke_butt)99 DEF_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 
DEF_ARC_GM(stroke_square)107 DEF_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 
DEF_ARC_GM(stroke_round)115 DEF_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 
DEF_ARC_GM(stroke_and_fill_butt)123 DEF_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 
DEF_ARC_GM(stroke_and_fill_square)131 DEF_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 
DEF_ARC_GM(stroke_and_fill_round)139 DEF_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 
147 DEF_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 
231 DEF_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 
253 DEF_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 
273 DEF_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