blob: f91482e0cc9a75e63325d9683722731066504a9d [file] [log] [blame]
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +00001/*
2 * Copyright 2013 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/SkPaint.h"
Mike Reede9d783c2020-08-17 14:14:13 -040012#include "include/core/SkPathBuilder.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040013#include "include/core/SkPoint.h"
14#include "include/core/SkRect.h"
15#include "include/core/SkScalar.h"
16#include "include/core/SkSize.h"
17#include "include/core/SkString.h"
18#include "include/core/SkTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "include/private/SkTArray.h"
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000020
21namespace skiagm {
22
23class HairlinesGM : public GM {
24protected:
commit-bot@chromium.orga90c6802014-04-30 13:20:45 +000025
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000026
mtklein36352bf2015-03-25 18:17:31 -070027 SkString onShortName() override {
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000028 return SkString("hairlines");
29 }
30
mtklein36352bf2015-03-25 18:17:31 -070031 SkISize onISize() override { return SkISize::Make(1250, 1250); }
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000032
mtklein36352bf2015-03-25 18:17:31 -070033 void onOnceBeforeDraw() override {
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000034 {
Mike Reede9d783c2020-08-17 14:14:13 -040035 SkPathBuilder lineAngles;
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000036 enum {
37 kNumAngles = 15,
38 kRadius = 40,
39 };
40 for (int i = 0; i < kNumAngles; ++i) {
41 SkScalar angle = SK_ScalarPI * SkIntToScalar(i) / kNumAngles;
42 SkScalar x = kRadius * SkScalarCos(angle);
43 SkScalar y = kRadius * SkScalarSin(angle);
Mike Reede9d783c2020-08-17 14:14:13 -040044 lineAngles.moveTo(x, y).lineTo(-x, -y);
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000045 }
Mike Reede9d783c2020-08-17 14:14:13 -040046 fPaths.push_back(lineAngles.detach());
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000047 }
48
Mike Reede9d783c2020-08-17 14:14:13 -040049 fPaths.push_back(SkPathBuilder().moveTo(0, -10)
50 .quadTo(100, 100, -10, 0)
51 .detach());
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000052
Mike Reede9d783c2020-08-17 14:14:13 -040053 fPaths.push_back(SkPathBuilder().moveTo(0, -5)
54 .quadTo(100, 100, -5, 0)
55 .detach());
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000056
Mike Reede9d783c2020-08-17 14:14:13 -040057 fPaths.push_back(SkPathBuilder().moveTo(0, -2)
58 .quadTo(100, 100, -2, 0)
59 .detach());
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000060
Mike Reede9d783c2020-08-17 14:14:13 -040061 fPaths.push_back(SkPathBuilder().moveTo(0, -1)
62 .quadTo(100, 100, -2 + 306.0f / 4, 75)
63 .detach());
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000064
Mike Reede9d783c2020-08-17 14:14:13 -040065 fPaths.push_back(SkPathBuilder().moveTo(0, -1)
66 .quadTo(100, 100, -1, 0)
67 .detach());
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000068
Mike Reede9d783c2020-08-17 14:14:13 -040069 fPaths.push_back(SkPathBuilder().moveTo(0, -0)
70 .quadTo(100, 100, 0, 0)
71 .detach());
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +000072
Mike Reede9d783c2020-08-17 14:14:13 -040073 fPaths.push_back(SkPathBuilder().moveTo(0, -0)
74 .quadTo(100, 100, 75, 75)
75 .detach());
commit-bot@chromium.orgb8bd6cb2013-09-03 14:56:17 +000076
77 // Two problem cases for gpu hairline renderer found by shapeops testing. These used
78 // to assert that the computed bounding box didn't contain all the vertices.
commit-bot@chromium.orgb8bd6cb2013-09-03 14:56:17 +000079
Mike Reede9d783c2020-08-17 14:14:13 -040080 fPaths.push_back(SkPathBuilder().moveTo(4, 6)
81 .cubicTo(5, 6, 5, 4, 4, 0)
82 .close()
83 .detach());
84
85 fPaths.push_back(SkPathBuilder().moveTo(5, 1)
86 .lineTo( 4.32787323f, 1.67212653f)
87 .cubicTo(2.75223875f, 3.24776125f,
88 3.00581908f, 4.51236057f,
89 3.7580452f, 4.37367964f)
90 .cubicTo(4.66472578f, 3.888381f,
91 5.f, 2.875f,
92 5.f, 1.f)
93 .close()
94 .detach());
robertphillips@google.comada90da2013-09-18 22:14:49 +000095
96 // Three paths that show the same bug (missing end caps)
robertphillips@google.comada90da2013-09-18 22:14:49 +000097
Mike Reede9d783c2020-08-17 14:14:13 -040098 fPaths.push_back(SkPathBuilder().moveTo(6.5f,5.5f)
99 .lineTo(3.5f,0.5f)
100 .moveTo(0.5f,5.5f)
101 .lineTo(3.5f,0.5f)
102 .detach());
robertphillips@google.comada90da2013-09-18 22:14:49 +0000103
Mike Reede9d783c2020-08-17 14:14:13 -0400104 // An X (crbug.com/137317)
105 fPaths.push_back(SkPathBuilder().moveTo(1, 1)
106 .lineTo(6, 6)
107 .moveTo(1, 6)
108 .lineTo(6, 1)
109 .detach());
robertphillips@google.comada90da2013-09-18 22:14:49 +0000110
Mike Reede9d783c2020-08-17 14:14:13 -0400111 // A right angle (crbug.com/137465 and crbug.com/256776)
112 fPaths.push_back(SkPathBuilder().moveTo(5.5f, 5.5f)
113 .lineTo(5.5f, 0.5f)
114 .lineTo(0.5f, 0.5f)
115 .detach());
robertphillips@google.com6c000322013-09-26 12:05:32 +0000116
117 {
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000118 // Arc example to test imperfect truncation bug (crbug.com/295626)
mtkleindbfd7ab2016-09-01 11:24:54 -0700119 constexpr SkScalar kRad = SkIntToScalar(2000);
120 constexpr SkScalar kStartAngle = 262.59717f;
121 constexpr SkScalar kSweepAngle = SkScalarHalf(17.188717f);
robertphillips@google.com6c000322013-09-26 12:05:32 +0000122
Mike Reede9d783c2020-08-17 14:14:13 -0400123 SkPathBuilder bug;
robertphillips@google.com6c000322013-09-26 12:05:32 +0000124
125 // Add a circular arc
126 SkRect circle = SkRect::MakeLTRB(-kRad, -kRad, kRad, kRad);
Mike Reede9d783c2020-08-17 14:14:13 -0400127 bug.addArc(circle, kStartAngle, kSweepAngle);
robertphillips@google.com6c000322013-09-26 12:05:32 +0000128
129 // Now add the chord that should cap the circular arc
Brian Osman4428f2c2019-04-02 10:59:28 -0400130 SkPoint p0 = { kRad * SkScalarCos(SkDegreesToRadians(kStartAngle)),
131 kRad * SkScalarSin(SkDegreesToRadians(kStartAngle)) };
robertphillips@google.com6c000322013-09-26 12:05:32 +0000132
Brian Osman4428f2c2019-04-02 10:59:28 -0400133 SkPoint p1 = { kRad * SkScalarCos(SkDegreesToRadians(kStartAngle + kSweepAngle)),
134 kRad * SkScalarSin(SkDegreesToRadians(kStartAngle + kSweepAngle)) };
robertphillips@google.com6c000322013-09-26 12:05:32 +0000135
Mike Reede9d783c2020-08-17 14:14:13 -0400136 bug.moveTo(p0);
137 bug.lineTo(p1);
138 fPaths.push_back(bug.detach());
robertphillips@google.com6c000322013-09-26 12:05:32 +0000139 }
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000140 }
141
mtklein36352bf2015-03-25 18:17:31 -0700142 void onDraw(SkCanvas* canvas) override {
mtkleindbfd7ab2016-09-01 11:24:54 -0700143 constexpr SkAlpha kAlphaValue[] = { 0xFF, 0x40 };
144 constexpr SkScalar kWidths[] = { 0, 0.5f, 1.5f };
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000145
146 enum {
147 kMargin = 5,
148 };
reed9aeb2a12015-04-11 19:29:50 -0700149 int wrapX = 1250 - kMargin;
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000150
151 SkScalar maxH = 0;
152 canvas->translate(SkIntToScalar(kMargin), SkIntToScalar(kMargin));
153 canvas->save();
154
155 SkScalar x = SkIntToScalar(kMargin);
156 for (int p = 0; p < fPaths.count(); ++p) {
157 for (size_t a = 0; a < SK_ARRAY_COUNT(kAlphaValue); ++a) {
158 for (int aa = 0; aa < 2; ++aa) {
joshualittbd528cd2014-12-10 14:23:40 -0800159 for (size_t w = 0; w < SK_ARRAY_COUNT(kWidths); w++) {
160 const SkRect& bounds = fPaths[p].getBounds();
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000161
joshualittbd528cd2014-12-10 14:23:40 -0800162 if (x + bounds.width() > wrapX) {
163 canvas->restore();
164 canvas->translate(0, maxH + SkIntToScalar(kMargin));
165 canvas->save();
166 maxH = 0;
167 x = SkIntToScalar(kMargin);
168 }
169
170 SkPaint paint;
171 paint.setARGB(kAlphaValue[a], 0, 0, 0);
172 paint.setAntiAlias(SkToBool(aa));
173 paint.setStyle(SkPaint::kStroke_Style);
174 paint.setStrokeWidth(kWidths[w]);
175
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000176 canvas->save();
joshualittbd528cd2014-12-10 14:23:40 -0800177 canvas->translate(-bounds.fLeft, -bounds.fTop);
178 canvas->drawPath(fPaths[p], paint);
179 canvas->restore();
180
Brian Osman116b33e2020-02-05 13:34:09 -0500181 maxH = std::max(maxH, bounds.height());
joshualittbd528cd2014-12-10 14:23:40 -0800182
183 SkScalar dx = bounds.width() + SkIntToScalar(kMargin);
184 x += dx;
185 canvas->translate(dx, 0);
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000186 }
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000187 }
188 }
189 }
190 canvas->restore();
191 }
192
193private:
194 SkTArray<SkPath> fPaths;
John Stiles7571f9e2020-09-02 22:42:33 -0400195 using INHERITED = GM;
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000196};
197
caryclarkc3262572015-12-04 11:08:42 -0800198static void draw_squarehair_tests(SkCanvas* canvas, SkScalar width, SkPaint::Cap cap, bool aa) {
199 SkPaint paint;
200 paint.setStrokeCap(cap);
201 paint.setStrokeWidth(width);
202 paint.setAntiAlias(aa);
203 paint.setStyle(SkPaint::kStroke_Style);
204 canvas->drawLine(10, 10, 20, 10, paint);
205 canvas->drawLine(30, 10, 30, 20, paint);
206 canvas->drawLine(40, 10, 50, 20, paint);
Mike Reede9d783c2020-08-17 14:14:13 -0400207 SkPathBuilder path;
caryclarkc3262572015-12-04 11:08:42 -0800208 path.moveTo(60, 10);
209 path.quadTo(60, 20, 70, 20);
210 path.conicTo(70, 10, 80, 10, 0.707f);
Mike Reede9d783c2020-08-17 14:14:13 -0400211 canvas->drawPath(path.detach(), paint);
212
caryclarkc3262572015-12-04 11:08:42 -0800213 path.moveTo(90, 10);
214 path.cubicTo(90, 20, 100, 20, 100, 10);
215 path.lineTo(110, 10);
Mike Reede9d783c2020-08-17 14:14:13 -0400216 canvas->drawPath(path.detach(), paint);
caryclarkc3262572015-12-04 11:08:42 -0800217 canvas->translate(0, 30);
218}
219
220DEF_SIMPLE_GM(squarehair, canvas, 240, 360) {
221 const bool aliases[] = { false, true };
222 const SkScalar widths[] = { 0, 0.999f, 1, 1.001f };
223 const SkPaint::Cap caps[] = { SkPaint::kButt_Cap, SkPaint::kSquare_Cap, SkPaint::kRound_Cap };
224 for (auto alias : aliases) {
225 canvas->save();
226 for (auto width : widths) {
227 for (auto cap : caps) {
228 draw_squarehair_tests(canvas, width, cap, alias);
229 }
230 }
231 canvas->restore();
232 canvas->translate(120, 0);
233 }
234}
235
Jim Van Verthb4610552020-07-15 13:26:43 -0400236// GM to test subdivision of hairlines
237static void draw_subdivided_quad(SkCanvas* canvas, int x0, int y0, int x1, int y1, SkColor color) {
238 SkPaint paint;
239 paint.setStrokeWidth(1);
240 paint.setAntiAlias(true);
241 paint.setStyle(SkPaint::kStroke_Style);
242 paint.setColor(color);
243
Mike Reedd16d6542020-08-22 15:08:27 -0400244 canvas->drawPath(SkPathBuilder().moveTo(0,0)
245 .quadTo(SkIntToScalar(x0), SkIntToScalar(y0),
246 SkIntToScalar(x1), SkIntToScalar(y1))
247 .detach(),
248 paint);
Jim Van Verthb4610552020-07-15 13:26:43 -0400249}
250
251DEF_SIMPLE_GM(hairline_subdiv, canvas, 512, 256) {
252 // no subdivisions
253 canvas->translate(45, -25);
254 draw_subdivided_quad(canvas, 334, 334, 467, 267, SK_ColorBLACK);
255
256 // one subdivision
257 canvas->translate(-185, -150);
258 draw_subdivided_quad(canvas, 472, 472, 660, 378, SK_ColorRED);
259
260 // two subdivisions
261 canvas->translate(-275, -200);
262 draw_subdivided_quad(canvas, 668, 668, 934, 535, SK_ColorGREEN);
263
264 // three subdivisions
265 canvas->translate(-385, -260);
266 draw_subdivided_quad(canvas, 944, 944, 1320, 756, SK_ColorBLUE);
267}
268
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000269//////////////////////////////////////////////////////////////////////////////
270
Hal Canarye964c182019-01-23 10:22:01 -0500271DEF_GM( return new HairlinesGM; )
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000272
John Stilesa6841be2020-08-06 14:11:56 -0400273} // namespace skiagm