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