blob: ed4f918cf4c0d5558a99da9724be30dd49f41831 [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 Kleinc0bd9f92019-04-23 12:05:21 -050012#include "include/core/SkPath.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 {
35 SkPath* lineAnglesPath = &fPaths.push_back();
36 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);
44 lineAnglesPath->moveTo(x, y);
45 lineAnglesPath->lineTo(-x, -y);
46 }
47 }
48
49 {
50 SkPath* kindaTightQuad = &fPaths.push_back();
51 kindaTightQuad->moveTo(0, -10 * SK_Scalar1);
52 kindaTightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -10 * SK_Scalar1, 0);
53 }
54
55 {
56 SkPath* tightQuad = &fPaths.push_back();
57 tightQuad->moveTo(0, -5 * SK_Scalar1);
58 tightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -5 * SK_Scalar1, 0);
59 }
60
61 {
62 SkPath* tighterQuad = &fPaths.push_back();
63 tighterQuad->moveTo(0, -2 * SK_Scalar1);
64 tighterQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -2 * SK_Scalar1, 0);
65 }
66
67 {
68 SkPath* unevenTighterQuad = &fPaths.push_back();
69 unevenTighterQuad->moveTo(0, -1 * SK_Scalar1);
70 SkPoint p;
71 p.set(-2 * SK_Scalar1 + 3 * SkIntToScalar(102) / 4, SkIntToScalar(75));
72 unevenTighterQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), p.fX, p.fY);
73 }
74
75 {
76 SkPath* reallyTightQuad = &fPaths.push_back();
77 reallyTightQuad->moveTo(0, -1 * SK_Scalar1);
78 reallyTightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -1 * SK_Scalar1, 0);
79 }
80
81 {
82 SkPath* closedQuad = &fPaths.push_back();
83 closedQuad->moveTo(0, -0);
84 closedQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), 0, 0);
85 }
86
87 {
88 SkPath* unevenClosedQuad = &fPaths.push_back();
89 unevenClosedQuad->moveTo(0, -0);
90 unevenClosedQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100),
91 SkIntToScalar(75), SkIntToScalar(75));
92 }
commit-bot@chromium.orgb8bd6cb2013-09-03 14:56:17 +000093
94 // Two problem cases for gpu hairline renderer found by shapeops testing. These used
95 // to assert that the computed bounding box didn't contain all the vertices.
96 {
97 SkPath* problem1 = &fPaths.push_back();
98 problem1->moveTo(SkIntToScalar(4), SkIntToScalar(6));
99 problem1->cubicTo(SkIntToScalar(5), SkIntToScalar(6),
100 SkIntToScalar(5), SkIntToScalar(4),
101 SkIntToScalar(4), SkIntToScalar(0));
102 problem1->close();
103 }
104
105 {
106 SkPath* problem2 = &fPaths.push_back();
107 problem2->moveTo(SkIntToScalar(5), SkIntToScalar(1));
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000108 problem2->lineTo(4.32787323f, 1.67212653f);
109 problem2->cubicTo(2.75223875f, 3.24776125f,
110 3.00581908f, 4.51236057f,
111 3.7580452f, 4.37367964f);
112 problem2->cubicTo(4.66472578f, 3.888381f,
113 5.f, 2.875f,
114 5.f, 1.f);
commit-bot@chromium.orgb8bd6cb2013-09-03 14:56:17 +0000115 problem2->close();
116 }
robertphillips@google.comada90da2013-09-18 22:14:49 +0000117
118 // Three paths that show the same bug (missing end caps)
119 {
120 // A caret (crbug.com/131770)
121 SkPath* bug0 = &fPaths.push_back();
122 bug0->moveTo(6.5f,5.5f);
123 bug0->lineTo(3.5f,0.5f);
124 bug0->moveTo(0.5f,5.5f);
125 bug0->lineTo(3.5f,0.5f);
126 }
127
128 {
129 // An X (crbug.com/137317)
130 SkPath* bug1 = &fPaths.push_back();
131
132 bug1->moveTo(1, 1);
133 bug1->lineTo(6, 6);
134 bug1->moveTo(1, 6);
135 bug1->lineTo(6, 1);
136 }
137
138 {
139 // A right angle (crbug.com/137465 and crbug.com/256776)
140 SkPath* bug2 = &fPaths.push_back();
141
142 bug2->moveTo(5.5f, 5.5f);
143 bug2->lineTo(5.5f, 0.5f);
144 bug2->lineTo(0.5f, 0.5f);
145 }
robertphillips@google.com6c000322013-09-26 12:05:32 +0000146
147 {
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000148 // Arc example to test imperfect truncation bug (crbug.com/295626)
mtkleindbfd7ab2016-09-01 11:24:54 -0700149 constexpr SkScalar kRad = SkIntToScalar(2000);
150 constexpr SkScalar kStartAngle = 262.59717f;
151 constexpr SkScalar kSweepAngle = SkScalarHalf(17.188717f);
robertphillips@google.com6c000322013-09-26 12:05:32 +0000152
153 SkPath* bug = &fPaths.push_back();
154
155 // Add a circular arc
156 SkRect circle = SkRect::MakeLTRB(-kRad, -kRad, kRad, kRad);
157 bug->addArc(circle, kStartAngle, kSweepAngle);
158
159 // Now add the chord that should cap the circular arc
Brian Osman4428f2c2019-04-02 10:59:28 -0400160 SkPoint p0 = { kRad * SkScalarCos(SkDegreesToRadians(kStartAngle)),
161 kRad * SkScalarSin(SkDegreesToRadians(kStartAngle)) };
robertphillips@google.com6c000322013-09-26 12:05:32 +0000162
Brian Osman4428f2c2019-04-02 10:59:28 -0400163 SkPoint p1 = { kRad * SkScalarCos(SkDegreesToRadians(kStartAngle + kSweepAngle)),
164 kRad * SkScalarSin(SkDegreesToRadians(kStartAngle + kSweepAngle)) };
robertphillips@google.com6c000322013-09-26 12:05:32 +0000165
166 bug->moveTo(p0);
167 bug->lineTo(p1);
168 }
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000169 }
170
mtklein36352bf2015-03-25 18:17:31 -0700171 void onDraw(SkCanvas* canvas) override {
mtkleindbfd7ab2016-09-01 11:24:54 -0700172 constexpr SkAlpha kAlphaValue[] = { 0xFF, 0x40 };
173 constexpr SkScalar kWidths[] = { 0, 0.5f, 1.5f };
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000174
175 enum {
176 kMargin = 5,
177 };
reed9aeb2a12015-04-11 19:29:50 -0700178 int wrapX = 1250 - kMargin;
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000179
180 SkScalar maxH = 0;
181 canvas->translate(SkIntToScalar(kMargin), SkIntToScalar(kMargin));
182 canvas->save();
183
184 SkScalar x = SkIntToScalar(kMargin);
185 for (int p = 0; p < fPaths.count(); ++p) {
186 for (size_t a = 0; a < SK_ARRAY_COUNT(kAlphaValue); ++a) {
187 for (int aa = 0; aa < 2; ++aa) {
joshualittbd528cd2014-12-10 14:23:40 -0800188 for (size_t w = 0; w < SK_ARRAY_COUNT(kWidths); w++) {
189 const SkRect& bounds = fPaths[p].getBounds();
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000190
joshualittbd528cd2014-12-10 14:23:40 -0800191 if (x + bounds.width() > wrapX) {
192 canvas->restore();
193 canvas->translate(0, maxH + SkIntToScalar(kMargin));
194 canvas->save();
195 maxH = 0;
196 x = SkIntToScalar(kMargin);
197 }
198
199 SkPaint paint;
200 paint.setARGB(kAlphaValue[a], 0, 0, 0);
201 paint.setAntiAlias(SkToBool(aa));
202 paint.setStyle(SkPaint::kStroke_Style);
203 paint.setStrokeWidth(kWidths[w]);
204
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000205 canvas->save();
joshualittbd528cd2014-12-10 14:23:40 -0800206 canvas->translate(-bounds.fLeft, -bounds.fTop);
207 canvas->drawPath(fPaths[p], paint);
208 canvas->restore();
209
210 maxH = SkMaxScalar(maxH, bounds.height());
211
212 SkScalar dx = bounds.width() + SkIntToScalar(kMargin);
213 x += dx;
214 canvas->translate(dx, 0);
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000215 }
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000216 }
217 }
218 }
219 canvas->restore();
220 }
221
222private:
223 SkTArray<SkPath> fPaths;
224 typedef GM INHERITED;
225};
226
caryclarkc3262572015-12-04 11:08:42 -0800227static void draw_squarehair_tests(SkCanvas* canvas, SkScalar width, SkPaint::Cap cap, bool aa) {
228 SkPaint paint;
229 paint.setStrokeCap(cap);
230 paint.setStrokeWidth(width);
231 paint.setAntiAlias(aa);
232 paint.setStyle(SkPaint::kStroke_Style);
233 canvas->drawLine(10, 10, 20, 10, paint);
234 canvas->drawLine(30, 10, 30, 20, paint);
235 canvas->drawLine(40, 10, 50, 20, paint);
236 SkPath path;
237 path.moveTo(60, 10);
238 path.quadTo(60, 20, 70, 20);
239 path.conicTo(70, 10, 80, 10, 0.707f);
240 canvas->drawPath(path, paint);
241 path.reset();
242 path.moveTo(90, 10);
243 path.cubicTo(90, 20, 100, 20, 100, 10);
244 path.lineTo(110, 10);
245 canvas->drawPath(path, paint);
246 canvas->translate(0, 30);
247}
248
249DEF_SIMPLE_GM(squarehair, canvas, 240, 360) {
250 const bool aliases[] = { false, true };
251 const SkScalar widths[] = { 0, 0.999f, 1, 1.001f };
252 const SkPaint::Cap caps[] = { SkPaint::kButt_Cap, SkPaint::kSquare_Cap, SkPaint::kRound_Cap };
253 for (auto alias : aliases) {
254 canvas->save();
255 for (auto width : widths) {
256 for (auto cap : caps) {
257 draw_squarehair_tests(canvas, width, cap, alias);
258 }
259 }
260 canvas->restore();
261 canvas->translate(120, 0);
262 }
263}
264
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000265//////////////////////////////////////////////////////////////////////////////
266
Hal Canarye964c182019-01-23 10:22:01 -0500267DEF_GM( return new HairlinesGM; )
commit-bot@chromium.org912e68e2013-05-24 18:51:55 +0000268
269}