blob: 56b9cfcfe0f0ea3b3537f8d2ae7b921729b643be [file] [log] [blame]
reed@google.com35a81df2012-05-04 21:49:27 +00001/*
2 * Copyright 2012 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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
9#include "include/core/SkCanvas.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040010#include "include/core/SkColor.h"
11#include "include/core/SkFont.h"
12#include "include/core/SkMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/core/SkPaint.h"
Mike Reed06d7c9d2020-08-26 12:56:51 -040014#include "include/core/SkPathBuilder.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040015#include "include/core/SkPathEffect.h"
16#include "include/core/SkPoint.h"
17#include "include/core/SkRect.h"
18#include "include/core/SkScalar.h"
19#include "include/core/SkSize.h"
20#include "include/core/SkString.h"
21#include "include/core/SkTypeface.h"
22#include "include/core/SkTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050023#include "include/effects/SkDashPathEffect.h"
24#include "tools/ToolUtils.h"
Kevin Lubicke836c3a2023-10-20 06:55:35 -040025#include "tools/fonts/FontToolUtils.h"
reed@google.com35a81df2012-05-04 21:49:27 +000026
Ben Wagner7fde8e12019-05-01 17:28:53 -040027#include <math.h>
28#include <initializer_list>
29
reed@google.comde1837b2012-05-21 16:47:43 +000030static void drawline(SkCanvas* canvas, int on, int off, const SkPaint& paint,
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000031 SkScalar finalX = SkIntToScalar(600), SkScalar finalY = SkIntToScalar(0),
halcanary9d524f22016-03-29 09:03:52 -070032 SkScalar phase = SkIntToScalar(0),
robertphillips9f2251c2014-11-04 13:33:50 -080033 SkScalar startX = SkIntToScalar(0), SkScalar startY = SkIntToScalar(0)) {
reed@google.com35a81df2012-05-04 21:49:27 +000034 SkPaint p(paint);
35
36 const SkScalar intervals[] = {
37 SkIntToScalar(on),
38 SkIntToScalar(off),
39 };
40
reeda4393342016-03-18 11:22:57 -070041 p.setPathEffect(SkDashPathEffect::Make(intervals, 2, phase));
robertphillips9f2251c2014-11-04 13:33:50 -080042 canvas->drawLine(startX, startY, finalX, finalY, p);
reed@google.comde1837b2012-05-21 16:47:43 +000043}
44
45// earlier bug stopped us from drawing very long single-segment dashes, because
46// SkPathMeasure was skipping very small delta-T values (nearlyzero). This is
47// now fixes, so this giant dash should appear.
48static void show_giant_dash(SkCanvas* canvas) {
49 SkPaint paint;
50
51 drawline(canvas, 1, 1, paint, SkIntToScalar(20 * 1000));
reed@google.com35a81df2012-05-04 21:49:27 +000052}
53
egdaniel21402e32014-11-05 05:02:27 -080054static void show_zero_len_dash(SkCanvas* canvas) {
55 SkPaint paint;
56
57 drawline(canvas, 2, 2, paint, SkIntToScalar(0));
Mike Reed19630092020-05-18 21:25:44 -040058 paint.setStroke(true);
egdaniel21402e32014-11-05 05:02:27 -080059 paint.setStrokeWidth(SkIntToScalar(2));
60 canvas->translate(0, SkIntToScalar(20));
61 drawline(canvas, 4, 4, paint, SkIntToScalar(0));
62}
63
reed@google.com21384df2012-05-18 17:59:08 +000064class DashingGM : public skiagm::GM {
Leandro Lovisolo24fa2112023-08-15 19:05:17 +000065 SkString getName() const override { return SkString("dashing"); }
reed@google.com35a81df2012-05-04 21:49:27 +000066
Leandro Lovisolo8f023882023-08-15 21:13:52 +000067 SkISize getISize() override { return {640, 340}; }
commit-bot@chromium.orga90c6802014-04-30 13:20:45 +000068
Hal Canaryfa3305a2019-07-18 12:36:54 -040069 void onDraw(SkCanvas* canvas) override {
John Stilesdbcf6802020-05-18 10:58:51 -040070 struct Intervals {
reed@google.com35a81df2012-05-04 21:49:27 +000071 int fOnInterval;
72 int fOffInterval;
reed@google.com35a81df2012-05-04 21:49:27 +000073 };
rmistry@google.comae933ce2012-08-23 18:19:56 +000074
reed@google.com35a81df2012-05-04 21:49:27 +000075 SkPaint paint;
Mike Reed19630092020-05-18 21:25:44 -040076 paint.setStroke(true);
reed@google.com35a81df2012-05-04 21:49:27 +000077
78 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
79 canvas->translate(0, SK_ScalarHalf);
reed@google.com35a81df2012-05-04 21:49:27 +000080 for (int width = 0; width <= 2; ++width) {
John Stilesdbcf6802020-05-18 10:58:51 -040081 for (const Intervals& data : {Intervals{1, 1},
82 Intervals{4, 1}}) {
83 for (bool aa : {false, true}) {
reed@google.com35a81df2012-05-04 21:49:27 +000084 int w = width * width * width;
John Stilesdbcf6802020-05-18 10:58:51 -040085 paint.setAntiAlias(aa);
reed@google.com35a81df2012-05-04 21:49:27 +000086 paint.setStrokeWidth(SkIntToScalar(w));
rmistry@google.comae933ce2012-08-23 18:19:56 +000087
reed@google.com35a81df2012-05-04 21:49:27 +000088 int scale = w ? w : 1;
89
John Stilesdbcf6802020-05-18 10:58:51 -040090 drawline(canvas, data.fOnInterval * scale, data.fOffInterval * scale,
reed@google.com35a81df2012-05-04 21:49:27 +000091 paint);
92 canvas->translate(0, SkIntToScalar(20));
93 }
94 }
95 }
rmistry@google.comae933ce2012-08-23 18:19:56 +000096
reed@google.comde1837b2012-05-21 16:47:43 +000097 show_giant_dash(canvas);
egdaniel21402e32014-11-05 05:02:27 -080098 canvas->translate(0, SkIntToScalar(20));
99 show_zero_len_dash(canvas);
egdanielc00389e2015-10-05 08:11:49 -0700100 canvas->translate(0, SkIntToScalar(20));
101 // Draw 0 on, 0 off dashed line
102 paint.setStrokeWidth(SkIntToScalar(8));
103 drawline(canvas, 0, 0, paint);
reed@google.com35a81df2012-05-04 21:49:27 +0000104 }
reed@google.com21384df2012-05-18 17:59:08 +0000105};
reed@google.com35a81df2012-05-04 21:49:27 +0000106
reed@google.com21384df2012-05-18 17:59:08 +0000107///////////////////////////////////////////////////////////////////////////////
108
Mike Reed06d7c9d2020-08-26 12:56:51 -0400109static SkPath make_unit_star(int n) {
reed@google.com21384df2012-05-18 17:59:08 +0000110 SkScalar rad = -SK_ScalarPI / 2;
111 const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n;
rmistry@google.comae933ce2012-08-23 18:19:56 +0000112
Mike Reed06d7c9d2020-08-26 12:56:51 -0400113 SkPathBuilder b;
114 b.moveTo(0, -SK_Scalar1);
reed@google.com21384df2012-05-18 17:59:08 +0000115 for (int i = 1; i < n; i++) {
116 rad += drad;
Mike Reed06d7c9d2020-08-26 12:56:51 -0400117 b.lineTo(SkScalarCos(rad), SkScalarSin(rad));
reed@google.com21384df2012-05-18 17:59:08 +0000118 }
Mike Reed06d7c9d2020-08-26 12:56:51 -0400119 return b.close().detach();
reed@google.com21384df2012-05-18 17:59:08 +0000120}
121
Mike Reed06d7c9d2020-08-26 12:56:51 -0400122static SkPath make_path_line(const SkRect& bounds) {
123 return SkPathBuilder().moveTo(bounds.left(), bounds.top())
124 .lineTo(bounds.right(), bounds.bottom())
125 .detach();
reed@google.com21384df2012-05-18 17:59:08 +0000126}
127
Mike Reed06d7c9d2020-08-26 12:56:51 -0400128static SkPath make_path_rect(const SkRect& bounds) {
129 return SkPath::Rect(bounds);
reed@google.com21384df2012-05-18 17:59:08 +0000130}
131
Mike Reed06d7c9d2020-08-26 12:56:51 -0400132static SkPath make_path_oval(const SkRect& bounds) {
133 return SkPath::Oval(bounds);
reed@google.com21384df2012-05-18 17:59:08 +0000134}
135
Mike Reed06d7c9d2020-08-26 12:56:51 -0400136static SkPath make_path_star(const SkRect& bounds) {
137 SkPath path = make_unit_star(5);
Mike Reed2ac6ce82021-01-15 12:26:22 -0500138 SkMatrix matrix = SkMatrix::RectToRect(path.getBounds(), bounds, SkMatrix::kCenter_ScaleToFit);
Mike Reed06d7c9d2020-08-26 12:56:51 -0400139 return path.makeTransform(matrix);
reed@google.com21384df2012-05-18 17:59:08 +0000140}
141
142class Dashing2GM : public skiagm::GM {
Leandro Lovisolo24fa2112023-08-15 19:05:17 +0000143 SkString getName() const override { return SkString("dashing2"); }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000144
Leandro Lovisolo8f023882023-08-15 21:13:52 +0000145 SkISize getISize() override { return {640, 480}; }
commit-bot@chromium.orga90c6802014-04-30 13:20:45 +0000146
Hal Canaryfa3305a2019-07-18 12:36:54 -0400147 void onDraw(SkCanvas* canvas) override {
mtkleindbfd7ab2016-09-01 11:24:54 -0700148 constexpr int gIntervals[] = {
reed@google.com21384df2012-05-18 17:59:08 +0000149 3, // 3 dashes: each count [0] followed by intervals [1..count]
150 2, 10, 10,
151 4, 20, 5, 5, 5,
152 2, 2, 2
153 };
154
Mike Reed06d7c9d2020-08-26 12:56:51 -0400155 SkPath (*gProc[])(const SkRect&) = {
reed@google.com21384df2012-05-18 17:59:08 +0000156 make_path_line, make_path_rect, make_path_oval, make_path_star,
157 };
rmistry@google.comae933ce2012-08-23 18:19:56 +0000158
reed@google.com21384df2012-05-18 17:59:08 +0000159 SkPaint paint;
160 paint.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400161 paint.setStroke(true);
reed@google.com21384df2012-05-18 17:59:08 +0000162 paint.setStrokeWidth(SkIntToScalar(6));
rmistry@google.comae933ce2012-08-23 18:19:56 +0000163
reed@google.com21384df2012-05-18 17:59:08 +0000164 SkRect bounds = SkRect::MakeWH(SkIntToScalar(120), SkIntToScalar(120));
165 bounds.offset(SkIntToScalar(20), SkIntToScalar(20));
166 SkScalar dx = bounds.width() * 4 / 3;
167 SkScalar dy = bounds.height() * 4 / 3;
168
169 const int* intervals = &gIntervals[1];
170 for (int y = 0; y < gIntervals[0]; ++y) {
Herb Derbyc37b3862022-06-21 09:49:17 -0400171 SkScalar vals[std::size(gIntervals)]; // more than enough
reed@google.com21384df2012-05-18 17:59:08 +0000172 int count = *intervals++;
173 for (int i = 0; i < count; ++i) {
174 vals[i] = SkIntToScalar(*intervals++);
175 }
176 SkScalar phase = vals[0] / 2;
reeda4393342016-03-18 11:22:57 -0700177 paint.setPathEffect(SkDashPathEffect::Make(vals, count, phase));
rmistry@google.comae933ce2012-08-23 18:19:56 +0000178
Herb Derbyc37b3862022-06-21 09:49:17 -0400179 for (size_t x = 0; x < std::size(gProc); ++x) {
reed@google.com21384df2012-05-18 17:59:08 +0000180 SkPath path;
181 SkRect r = bounds;
182 r.offset(x * dx, y * dy);
Mike Reed06d7c9d2020-08-26 12:56:51 -0400183 canvas->drawPath(gProc[x](r), paint);
reed@google.com21384df2012-05-18 17:59:08 +0000184 }
185 }
186 }
reed@google.com35a81df2012-05-04 21:49:27 +0000187};
188
189//////////////////////////////////////////////////////////////////////////////
190
robertphillips@google.com629ab542012-11-28 17:18:11 +0000191// Test out the on/off line dashing Chrome if fond of
192class Dashing3GM : public skiagm::GM {
Leandro Lovisolo24fa2112023-08-15 19:05:17 +0000193 SkString getName() const override { return SkString("dashing3"); }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000194
Leandro Lovisolo8f023882023-08-15 21:13:52 +0000195 SkISize getISize() override { return {640, 480}; }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000196
197 // Draw a 100x100 block of dashed lines. The horizontal ones are BW
198 // while the vertical ones are AA.
skia.committer@gmail.com73b140a2012-12-05 02:01:21 +0000199 void drawDashedLines(SkCanvas* canvas,
200 SkScalar lineLength,
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000201 SkScalar phase,
202 SkScalar dashLength,
203 int strokeWidth,
204 bool circles) {
robertphillips@google.com629ab542012-11-28 17:18:11 +0000205 SkPaint p;
206 p.setColor(SK_ColorBLACK);
Mike Reed19630092020-05-18 21:25:44 -0400207 p.setStroke(true);
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000208 p.setStrokeWidth(SkIntToScalar(strokeWidth));
robertphillips@google.com629ab542012-11-28 17:18:11 +0000209
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000210 if (circles) {
211 p.setStrokeCap(SkPaint::kRound_Cap);
212 }
213
214 SkScalar intervals[2] = { dashLength, dashLength };
robertphillips@google.com629ab542012-11-28 17:18:11 +0000215
reeda4393342016-03-18 11:22:57 -0700216 p.setPathEffect(SkDashPathEffect::Make(intervals, 2, phase));
robertphillips@google.com629ab542012-11-28 17:18:11 +0000217
218 SkPoint pts[2];
219
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000220 for (int y = 0; y < 100; y += 10*strokeWidth) {
robertphillips@google.com629ab542012-11-28 17:18:11 +0000221 pts[0].set(0, SkIntToScalar(y));
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000222 pts[1].set(lineLength, SkIntToScalar(y));
robertphillips@google.com629ab542012-11-28 17:18:11 +0000223
224 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p);
225 }
226
227 p.setAntiAlias(true);
228
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000229 for (int x = 0; x < 100; x += 14*strokeWidth) {
robertphillips@google.com629ab542012-11-28 17:18:11 +0000230 pts[0].set(SkIntToScalar(x), 0);
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000231 pts[1].set(SkIntToScalar(x), lineLength);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000232
233 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p);
234 }
235 }
236
Hal Canaryfa3305a2019-07-18 12:36:54 -0400237 void onDraw(SkCanvas* canvas) override {
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000238 // 1on/1off 1x1 squares with phase of 0 - points fastpath
robertphillips@google.com629ab542012-11-28 17:18:11 +0000239 canvas->save();
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000240 canvas->translate(2, 0);
241 this->drawDashedLines(canvas, 100, 0, SK_Scalar1, 1, false);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000242 canvas->restore();
243
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000244 // 1on/1off 1x1 squares with phase of .5 - rects fastpath (due to partial squares)
robertphillips@google.com629ab542012-11-28 17:18:11 +0000245 canvas->save();
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000246 canvas->translate(112, 0);
247 this->drawDashedLines(canvas, 100, SK_ScalarHalf, SK_Scalar1, 1, false);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000248 canvas->restore();
249
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000250 // 1on/1off 1x1 squares with phase of 1 - points fastpath
robertphillips@google.com629ab542012-11-28 17:18:11 +0000251 canvas->save();
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000252 canvas->translate(222, 0);
253 this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, false);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000254 canvas->restore();
255
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000256 // 1on/1off 1x1 squares with phase of 1 and non-integer length - rects fastpath
robertphillips@google.com629ab542012-11-28 17:18:11 +0000257 canvas->save();
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000258 canvas->translate(332, 0);
reed@google.com140d7282013-01-07 20:25:04 +0000259 this->drawDashedLines(canvas, 99.5f, SK_ScalarHalf, SK_Scalar1, 1, false);
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000260 canvas->restore();
261
robertphillips@google.com5c4d5582013-01-15 12:53:31 +0000262 // 255on/255off 1x1 squares with phase of 0 - rects fast path
263 canvas->save();
264 canvas->translate(446, 0);
265 this->drawDashedLines(canvas, 100, 0, SkIntToScalar(255), 1, false);
266 canvas->restore();
267
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000268 // 1on/1off 3x3 squares with phase of 0 - points fast path
269 canvas->save();
270 canvas->translate(2, 110);
271 this->drawDashedLines(canvas, 100, 0, SkIntToScalar(3), 3, false);
272 canvas->restore();
273
274 // 1on/1off 3x3 squares with phase of 1.5 - rects fast path
275 canvas->save();
276 canvas->translate(112, 110);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000277 this->drawDashedLines(canvas, 100, 1.5f, SkIntToScalar(3), 3, false);
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000278 canvas->restore();
279
280 // 1on/1off 1x1 circles with phase of 1 - no fast path yet
281 canvas->save();
282 canvas->translate(2, 220);
283 this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, true);
284 canvas->restore();
285
286 // 1on/1off 3x3 circles with phase of 1 - no fast path yet
287 canvas->save();
288 canvas->translate(112, 220);
289 this->drawDashedLines(canvas, 100, 0, SkIntToScalar(3), 3, true);
290 canvas->restore();
291
292 // 1on/1off 1x1 squares with rotation - should break fast path
293 canvas->save();
294 canvas->translate(332+SK_ScalarRoot2Over2*100, 110+SK_ScalarRoot2Over2*100);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000295 canvas->rotate(45);
296 canvas->translate(-50, -50);
297
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000298 this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, false);
robertphillips@google.com629ab542012-11-28 17:18:11 +0000299 canvas->restore();
300
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000301 // 3on/3off 3x1 rects - should use rect fast path regardless of phase
302 for (int phase = 0; phase <= 3; ++phase) {
303 canvas->save();
skia.committer@gmail.com73b140a2012-12-05 02:01:21 +0000304 canvas->translate(SkIntToScalar(phase*110+2),
robertphillips@google.com8c685c52012-12-04 20:34:11 +0000305 SkIntToScalar(330));
306 this->drawDashedLines(canvas, 100, SkIntToScalar(phase), SkIntToScalar(3), 1, false);
307 canvas->restore();
308 }
robertphillips@google.com629ab542012-11-28 17:18:11 +0000309 }
310
311};
312
313//////////////////////////////////////////////////////////////////////////////
314
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000315class Dashing4GM : public skiagm::GM {
Leandro Lovisolo24fa2112023-08-15 19:05:17 +0000316 SkString getName() const override { return SkString("dashing4"); }
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000317
Leandro Lovisolo8f023882023-08-15 21:13:52 +0000318 SkISize getISize() override { return {640, 1100}; }
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000319
Hal Canaryfa3305a2019-07-18 12:36:54 -0400320 void onDraw(SkCanvas* canvas) override {
John Stilesdbcf6802020-05-18 10:58:51 -0400321 struct Intervals {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000322 int fOnInterval;
323 int fOffInterval;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000324 };
325
326 SkPaint paint;
Mike Reed19630092020-05-18 21:25:44 -0400327 paint.setStroke(true);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000328
329 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
Greg Daniel95581bb2017-06-30 10:36:38 -0400330 canvas->translate(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000331
332 for (int width = 0; width <= 2; ++width) {
John Stilesdbcf6802020-05-18 10:58:51 -0400333 for (const Intervals& data : {Intervals{1, 1},
334 Intervals{4, 2},
Chris Dalton06b52ad2020-12-15 10:01:35 -0700335 Intervals{0, 4}}) { // test for zero length on interval.
336 // zero length intervals should draw
337 // a line of squares or circles
John Stilesdbcf6802020-05-18 10:58:51 -0400338 for (bool aa : {false, true}) {
339 for (auto cap : {SkPaint::kRound_Cap, SkPaint::kSquare_Cap}) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000340 int w = width * width * width;
John Stilesdbcf6802020-05-18 10:58:51 -0400341 paint.setAntiAlias(aa);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000342 paint.setStrokeWidth(SkIntToScalar(w));
John Stilesdbcf6802020-05-18 10:58:51 -0400343 paint.setStrokeCap(cap);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000344
345 int scale = w ? w : 1;
346
John Stilesdbcf6802020-05-18 10:58:51 -0400347 drawline(canvas, data.fOnInterval * scale, data.fOffInterval * scale,
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000348 paint);
349 canvas->translate(0, SkIntToScalar(20));
350 }
351 }
352 }
353 }
354
355 for (int aa = 0; aa <= 1; ++aa) {
356 paint.setAntiAlias(SkToBool(aa));
357 paint.setStrokeWidth(8.f);
358 paint.setStrokeCap(SkPaint::kSquare_Cap);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000359 // Single dash element that is cut off at start and end
commit-bot@chromium.org71db8822014-05-19 14:59:04 +0000360 drawline(canvas, 32, 16, paint, 20.f, 0, 5.f);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000361 canvas->translate(0, SkIntToScalar(20));
362
363 // Two dash elements where each one is cut off at beginning and end respectively
commit-bot@chromium.org71db8822014-05-19 14:59:04 +0000364 drawline(canvas, 32, 16, paint, 56.f, 0, 5.f);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000365 canvas->translate(0, SkIntToScalar(20));
366
367 // Many dash elements where first and last are cut off at beginning and end respectively
commit-bot@chromium.org71db8822014-05-19 14:59:04 +0000368 drawline(canvas, 32, 16, paint, 584.f, 0, 5.f);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000369 canvas->translate(0, SkIntToScalar(20));
370
371 // Diagonal dash line where src pnts are not axis aligned (as apposed to being diagonal from
372 // a canvas rotation)
commit-bot@chromium.org71db8822014-05-19 14:59:04 +0000373 drawline(canvas, 32, 16, paint, 600.f, 30.f);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000374 canvas->translate(0, SkIntToScalar(20));
375
376 // Case where only the off interval exists on the line. Thus nothing should be drawn
commit-bot@chromium.org71db8822014-05-19 14:59:04 +0000377 drawline(canvas, 32, 16, paint, 8.f, 0.f, 40.f);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000378 canvas->translate(0, SkIntToScalar(20));
379 }
Greg Daniel5fb30562017-06-29 12:27:48 -0400380
381 // Test overlapping circles.
382 canvas->translate(SkIntToScalar(5), SkIntToScalar(20));
383 paint.setAntiAlias(true);
384 paint.setStrokeCap(SkPaint::kRound_Cap);
385 paint.setColor(0x44000000);
386 paint.setStrokeWidth(40);
387 drawline(canvas, 0, 30, paint);
388
389 canvas->translate(0, SkIntToScalar(50));
390 paint.setStrokeCap(SkPaint::kSquare_Cap);
391 drawline(canvas, 0, 30, paint);
Greg Danielc96f9b52017-12-07 15:00:06 -0500392
393 // Test we draw the cap when the line length is zero.
394 canvas->translate(0, SkIntToScalar(50));
395 paint.setStrokeCap(SkPaint::kRound_Cap);
396 paint.setColor(0xFF000000);
397 paint.setStrokeWidth(11);
398 drawline(canvas, 0, 30, paint, 0);
399
400 canvas->translate(SkIntToScalar(100), 0);
401 drawline(canvas, 1, 30, paint, 0);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000402 }
403};
404
405//////////////////////////////////////////////////////////////////////////////
406
robertphillips9f2251c2014-11-04 13:33:50 -0800407class Dashing5GM : public skiagm::GM {
408public:
409 Dashing5GM(bool doAA) : fDoAA(doAA) {}
reed@google.com35a81df2012-05-04 21:49:27 +0000410
Hal Canaryfa3305a2019-07-18 12:36:54 -0400411private:
mtklein36352bf2015-03-25 18:17:31 -0700412 bool runAsBench() const override { return true; }
mtkleincf5d9c92015-01-23 10:31:45 -0800413
Leandro Lovisolo24fa2112023-08-15 19:05:17 +0000414 SkString getName() const override { return SkString(fDoAA ? "dashing5_aa" : "dashing5_bw"); }
robertphillips9f2251c2014-11-04 13:33:50 -0800415
Leandro Lovisolo8f023882023-08-15 21:13:52 +0000416 SkISize getISize() override { return {400, 200}; }
robertphillips9f2251c2014-11-04 13:33:50 -0800417
mtklein36352bf2015-03-25 18:17:31 -0700418 void onDraw(SkCanvas* canvas) override {
mtkleindbfd7ab2016-09-01 11:24:54 -0700419 constexpr int kOn = 4;
420 constexpr int kOff = 4;
421 constexpr int kIntervalLength = kOn + kOff;
robertphillips9f2251c2014-11-04 13:33:50 -0800422
mtkleindbfd7ab2016-09-01 11:24:54 -0700423 constexpr SkColor gColors[kIntervalLength] = {
robertphillips9f2251c2014-11-04 13:33:50 -0800424 SK_ColorRED,
425 SK_ColorGREEN,
426 SK_ColorBLUE,
427 SK_ColorCYAN,
428 SK_ColorMAGENTA,
429 SK_ColorYELLOW,
430 SK_ColorGRAY,
431 SK_ColorDKGRAY
432 };
433
434 SkPaint paint;
Mike Reed19630092020-05-18 21:25:44 -0400435 paint.setStroke(true);
robertphillips9f2251c2014-11-04 13:33:50 -0800436
437 paint.setAntiAlias(fDoAA);
438
439 SkMatrix rot;
440 rot.setRotate(90);
441 SkASSERT(rot.rectStaysRect());
442
443 canvas->concat(rot);
444
445 int sign; // used to toggle the direction of the lines
446 int phase = 0;
447
448 for (int x = 0; x < 200; x += 10) {
449 paint.setStrokeWidth(SkIntToScalar(phase+1));
450 paint.setColor(gColors[phase]);
451 sign = (x % 20) ? 1 : -1;
halcanary9d524f22016-03-29 09:03:52 -0700452 drawline(canvas, kOn, kOff, paint,
453 SkIntToScalar(x), -sign * SkIntToScalar(10003),
robertphillips9f2251c2014-11-04 13:33:50 -0800454 SkIntToScalar(phase),
455 SkIntToScalar(x), sign * SkIntToScalar(10003));
456 phase = (phase + 1) % kIntervalLength;
457 }
458
459 for (int y = -400; y < 0; y += 10) {
460 paint.setStrokeWidth(SkIntToScalar(phase+1));
461 paint.setColor(gColors[phase]);
462 sign = (y % 20) ? 1 : -1;
halcanary9d524f22016-03-29 09:03:52 -0700463 drawline(canvas, kOn, kOff, paint,
464 -sign * SkIntToScalar(10003), SkIntToScalar(y),
robertphillips9f2251c2014-11-04 13:33:50 -0800465 SkIntToScalar(phase),
466 sign * SkIntToScalar(10003), SkIntToScalar(y));
467 phase = (phase + 1) % kIntervalLength;
468 }
469 }
470
471private:
472 bool fDoAA;
473};
474
reed6dc14aa2016-04-11 07:46:38 -0700475DEF_SIMPLE_GM(longpathdash, canvas, 612, 612) {
caryclarkf97aa742015-12-18 07:03:13 -0800476 SkPath lines;
477 for (int x = 32; x < 256; x += 16) {
478 for (SkScalar a = 0; a < 3.141592f * 2; a += 0.03141592f) {
479 SkPoint pts[2] = {
480 { 256 + (float) sin(a) * x,
481 256 + (float) cos(a) * x },
482 { 256 + (float) sin(a + 3.141592 / 3) * (x + 64),
483 256 + (float) cos(a + 3.141592 / 3) * (x + 64) }
484 };
485 lines.moveTo(pts[0]);
486 for (SkScalar i = 0; i < 1; i += 0.05f) {
487 lines.lineTo(pts[0].fX * (1 - i) + pts[1].fX * i,
488 pts[0].fY * (1 - i) + pts[1].fY * i);
489 }
490 }
491 }
492 SkPaint p;
493 p.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400494 p.setStroke(true);
caryclarkf97aa742015-12-18 07:03:13 -0800495 p.setStrokeWidth(1);
496 const SkScalar intervals[] = { 1, 1 };
Herb Derbyc37b3862022-06-21 09:49:17 -0400497 p.setPathEffect(SkDashPathEffect::Make(intervals, std::size(intervals), 0));
Ben Wagner63fd7602017-10-09 15:45:33 -0400498
reed6dc14aa2016-04-11 07:46:38 -0700499 canvas->translate(50, 50);
caryclarkf97aa742015-12-18 07:03:13 -0800500 canvas->drawPath(lines, p);
501}
502
caryclark70e6d602016-01-30 10:11:21 -0800503DEF_SIMPLE_GM(longlinedash, canvas, 512, 512) {
504 SkPaint p;
505 p.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400506 p.setStroke(true);
caryclark70e6d602016-01-30 10:11:21 -0800507 p.setStrokeWidth(80);
508
509 const SkScalar intervals[] = { 2, 2 };
Herb Derbyc37b3862022-06-21 09:49:17 -0400510 p.setPathEffect(SkDashPathEffect::Make(intervals, std::size(intervals), 0));
caryclark70e6d602016-01-30 10:11:21 -0800511 canvas->drawRect(SkRect::MakeXYWH(-10000, 100, 20000, 20), p);
512}
513
Robert Phillipsacc6b152021-02-10 11:54:35 -0500514DEF_SIMPLE_GM(dashbigrects, canvas, 256, 256) {
515 SkRandom rand;
516
517 constexpr int kHalfStrokeWidth = 8;
518 constexpr int kOnOffInterval = 2*kHalfStrokeWidth;
519
520 canvas->clear(SkColors::kBlack);
521
522 SkPaint p;
523 p.setAntiAlias(true);
524 p.setStroke(true);
525 p.setStrokeWidth(2*kHalfStrokeWidth);
526 p.setStrokeCap(SkPaint::kButt_Cap);
527
528 constexpr SkScalar intervals[] = { kOnOffInterval, kOnOffInterval };
Herb Derbyc37b3862022-06-21 09:49:17 -0400529 p.setPathEffect(SkDashPathEffect::Make(intervals, std::size(intervals), 0));
Robert Phillipsacc6b152021-02-10 11:54:35 -0500530
531 constexpr float gWidthHeights[] = {
532 1000000000.0f * kOnOffInterval + kOnOffInterval/2.0f,
533 1000000.0f * kOnOffInterval + kOnOffInterval/2.0f,
534 1000.0f * kOnOffInterval + kOnOffInterval/2.0f,
535 100.0f * kOnOffInterval + kOnOffInterval/2.0f,
536 10.0f * kOnOffInterval + kOnOffInterval/2.0f,
537 9.0f * kOnOffInterval + kOnOffInterval/2.0f,
538 8.0f * kOnOffInterval + kOnOffInterval/2.0f,
539 7.0f * kOnOffInterval + kOnOffInterval/2.0f,
540 6.0f * kOnOffInterval + kOnOffInterval/2.0f,
541 5.0f * kOnOffInterval + kOnOffInterval/2.0f,
542 4.0f * kOnOffInterval + kOnOffInterval/2.0f,
543 };
544
Herb Derbyc37b3862022-06-21 09:49:17 -0400545 for (size_t i = 0; i < std::size(gWidthHeights); ++i) {
Robert Phillipsacc6b152021-02-10 11:54:35 -0500546 p.setColor(ToolUtils::color_to_565(rand.nextU() | (0xFF << 24)));
547
548 int offset = 2 * i * kHalfStrokeWidth + kHalfStrokeWidth;
549 canvas->drawRect(SkRect::MakeXYWH(offset, offset, gWidthHeights[i], gWidthHeights[i]), p);
550 }
551}
552
caryclark70e6d602016-01-30 10:11:21 -0800553DEF_SIMPLE_GM(longwavyline, canvas, 512, 512) {
554 SkPaint p;
555 p.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400556 p.setStroke(true);
caryclark70e6d602016-01-30 10:11:21 -0800557 p.setStrokeWidth(2);
558
559 SkPath wavy;
560 wavy.moveTo(-10000, 100);
561 for (SkScalar i = -10000; i < 10000; i += 20) {
562 wavy.quadTo(i + 5, 95, i + 10, 100);
563 wavy.quadTo(i + 15, 105, i + 20, 100);
564 }
565 canvas->drawPath(wavy, p);
566}
567
caryclarkd7ea92f2016-03-16 07:34:02 -0700568DEF_SIMPLE_GM(dashtextcaps, canvas, 512, 512) {
569 SkPaint p;
570 p.setAntiAlias(true);
Mike Reed19630092020-05-18 21:25:44 -0400571 p.setStroke(true);
caryclarkd7ea92f2016-03-16 07:34:02 -0700572 p.setStrokeWidth(10);
573 p.setStrokeCap(SkPaint::kRound_Cap);
caryclark1aaadbd2016-03-17 07:01:49 -0700574 p.setStrokeJoin(SkPaint::kRound_Join);
caryclarkd7ea92f2016-03-16 07:34:02 -0700575 p.setARGB(0xff, 0xbb, 0x00, 0x00);
Mike Reed4de2f1f2019-01-05 16:35:13 -0500576
Kevin Lubicke836c3a2023-10-20 06:55:35 -0400577 SkFont font(ToolUtils::DefaultPortableTypeface(), 100);
Mike Reed4de2f1f2019-01-05 16:35:13 -0500578
caryclarkd7ea92f2016-03-16 07:34:02 -0700579 const SkScalar intervals[] = { 12, 12 };
Herb Derbyc37b3862022-06-21 09:49:17 -0400580 p.setPathEffect(SkDashPathEffect::Make(intervals, std::size(intervals), 0));
Mike Reed4de2f1f2019-01-05 16:35:13 -0500581 canvas->drawString("Sausages", 10, 90, font, p);
caryclarkd7ea92f2016-03-16 07:34:02 -0700582 canvas->drawLine(8, 120, 456, 120, p);
583}
584
Brian Salomon72f78c32017-12-21 11:56:42 -0500585DEF_SIMPLE_GM(dash_line_zero_off_interval, canvas, 160, 330) {
586 static constexpr SkScalar kIntervals[] = {5.f, 0.f, 2.f, 0.f};
587 SkPaint dashPaint;
Herb Derbyc37b3862022-06-21 09:49:17 -0400588 dashPaint.setPathEffect(SkDashPathEffect::Make(kIntervals, std::size(kIntervals), 0.f));
Brian Salomon72f78c32017-12-21 11:56:42 -0500589 SkASSERT(dashPaint.getPathEffect());
Mike Reed19630092020-05-18 21:25:44 -0400590 dashPaint.setStroke(true);
Brian Salomon72f78c32017-12-21 11:56:42 -0500591 dashPaint.setStrokeWidth(20.f);
592 static constexpr struct {
593 SkPoint fA, fB;
594 } kLines[] = {{{0.5f, 0.5f}, {30.5f, 0.5f}}, // horizontal
595 {{0.5f, 0.5f}, {0.5f, 30.5f}}, // vertical
596 {{0.5f, 0.5f}, {0.5f, 0.5f}}, // point
597 {{0.5f, 0.5f}, {25.5f, 25.5f}}}; // diagonal
598 SkScalar pad = 5.f + dashPaint.getStrokeWidth();
599 canvas->translate(pad / 2.f, pad / 2.f);
600 canvas->save();
601 SkScalar h = 0.f;
602 for (const auto& line : kLines) {
Brian Osman788b9162020-02-07 10:36:46 -0500603 h = std::max(h, SkScalarAbs(line.fA.fY - line.fB.fY));
Brian Salomon72f78c32017-12-21 11:56:42 -0500604 }
605 for (const auto& line : kLines) {
606 SkScalar w = SkScalarAbs(line.fA.fX - line.fB.fX);
607 for (auto cap : {SkPaint::kButt_Cap, SkPaint::kSquare_Cap, SkPaint::kRound_Cap}) {
608 dashPaint.setStrokeCap(cap);
609 for (auto aa : {false, true}) {
610 dashPaint.setAntiAlias(aa);
611 canvas->drawLine(line.fA, line.fB, dashPaint);
612 canvas->translate(0.f, pad + h);
613 }
614 }
615 canvas->restore();
616 canvas->translate(pad + w, 0.f);
617 canvas->save();
618 }
619}
620
Brian Salomon518fd4d2020-05-08 15:32:28 -0400621DEF_SIMPLE_GM(thin_aa_dash_lines, canvas, 330, 110) {
Brian Salomonc6839122020-05-07 12:43:19 -0400622 SkPaint paint;
Brian Salomon518fd4d2020-05-08 15:32:28 -0400623 static constexpr SkScalar kScale = 100.f;
624 static constexpr SkScalar kIntervals[] = {10/kScale, 5/kScale};
Herb Derbyc37b3862022-06-21 09:49:17 -0400625 paint.setPathEffect(SkDashPathEffect::Make(kIntervals, std::size(kIntervals), 0.f));
Brian Salomonc6839122020-05-07 12:43:19 -0400626 paint.setAntiAlias(true);
Brian Salomon518fd4d2020-05-08 15:32:28 -0400627 paint.setStrokeWidth(0.25f/kScale);
Brian Salomonc6839122020-05-07 12:43:19 -0400628 // substep moves the subpixel offset every iteration.
Brian Salomon518fd4d2020-05-08 15:32:28 -0400629 static constexpr SkScalar kSubstep = 0.05f/kScale;
Brian Salomonc6839122020-05-07 12:43:19 -0400630 // We will draw a grid of horiz/vertical lines that pass through each other's off intervals.
631 static constexpr SkScalar kStep = kIntervals[0] + kIntervals[1];
Brian Salomon518fd4d2020-05-08 15:32:28 -0400632 canvas->scale(kScale, kScale);
Brian Salomonc6839122020-05-07 12:43:19 -0400633 canvas->translate(kIntervals[1], kIntervals[1]);
Brian Salomon518fd4d2020-05-08 15:32:28 -0400634 for (auto c : {SkPaint::kButt_Cap, SkPaint::kSquare_Cap, SkPaint::kRound_Cap}) {
635 paint.setStrokeCap(c);
636 for (SkScalar x = -.5f*kIntervals[1]; x < 105/kScale; x += (kStep + kSubstep)) {
637 canvas->drawLine({x, 0}, {x, 100/kScale}, paint);
638 canvas->drawLine({0, x}, {100/kScale, x}, paint);
639 }
640 canvas->translate(110/kScale, 0);
Brian Salomonc6839122020-05-07 12:43:19 -0400641 }
642}
643
kcbanner4eb7c232020-11-06 10:05:24 -0500644DEF_SIMPLE_GM(path_effect_empty_result, canvas, 100, 100) {
645 SkPaint p;
646 p.setStroke(true);
647 p.setStrokeWidth(1);
648
649 SkPath path;
650 float r = 70;
651 float l = 70;
652 float t = 70;
653 float b = 70;
654 path.moveTo(l, t);
655 path.lineTo(r, t);
656 path.lineTo(r, b);
657 path.lineTo(l, b);
658 path.close();
659
660 float dashes[] = {2.f, 2.f};
661 p.setPathEffect(SkDashPathEffect::Make(dashes, 2, 0.f));
662
663 canvas->drawPath(path, p);
664}
665
robertphillips9f2251c2014-11-04 13:33:50 -0800666//////////////////////////////////////////////////////////////////////////////
667
halcanary385fe4d2015-08-26 13:07:48 -0700668DEF_GM(return new DashingGM;)
669DEF_GM(return new Dashing2GM;)
670DEF_GM(return new Dashing3GM;)
671DEF_GM(return new Dashing4GM;)
672DEF_GM(return new Dashing5GM(true);)
673DEF_GM(return new Dashing5GM(false);)