• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
8 #include "gm.h"
9 #include "SkCanvas.h"
10 #include "SkPath.h"
11 #include "SkTArray.h"
12 
13 namespace skiagm {
14 
15 class HairlinesGM : public GM {
16 protected:
17 
18 
onShortName()19     SkString onShortName() override {
20         return SkString("hairlines");
21     }
22 
onISize()23     SkISize onISize() override { return SkISize::Make(1250, 1250); }
24 
onOnceBeforeDraw()25     void onOnceBeforeDraw() override {
26         {
27             SkPath* lineAnglesPath = &fPaths.push_back();
28             enum {
29                 kNumAngles = 15,
30                 kRadius = 40,
31             };
32             for (int i = 0; i < kNumAngles; ++i) {
33                 SkScalar angle = SK_ScalarPI * SkIntToScalar(i) / kNumAngles;
34                 SkScalar x = kRadius * SkScalarCos(angle);
35                 SkScalar y = kRadius * SkScalarSin(angle);
36                 lineAnglesPath->moveTo(x, y);
37                 lineAnglesPath->lineTo(-x, -y);
38             }
39         }
40 
41         {
42             SkPath* kindaTightQuad = &fPaths.push_back();
43             kindaTightQuad->moveTo(0, -10 * SK_Scalar1);
44             kindaTightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -10 * SK_Scalar1, 0);
45         }
46 
47         {
48             SkPath* tightQuad = &fPaths.push_back();
49             tightQuad->moveTo(0, -5 * SK_Scalar1);
50             tightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -5 * SK_Scalar1, 0);
51         }
52 
53         {
54             SkPath* tighterQuad = &fPaths.push_back();
55             tighterQuad->moveTo(0, -2 * SK_Scalar1);
56             tighterQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -2 * SK_Scalar1, 0);
57         }
58 
59         {
60             SkPath* unevenTighterQuad = &fPaths.push_back();
61             unevenTighterQuad->moveTo(0, -1 * SK_Scalar1);
62             SkPoint p;
63             p.set(-2 * SK_Scalar1 + 3 * SkIntToScalar(102) / 4, SkIntToScalar(75));
64             unevenTighterQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), p.fX, p.fY);
65         }
66 
67         {
68             SkPath* reallyTightQuad = &fPaths.push_back();
69             reallyTightQuad->moveTo(0, -1 * SK_Scalar1);
70             reallyTightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -1 * SK_Scalar1, 0);
71         }
72 
73         {
74             SkPath* closedQuad = &fPaths.push_back();
75             closedQuad->moveTo(0, -0);
76             closedQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), 0, 0);
77         }
78 
79         {
80             SkPath* unevenClosedQuad = &fPaths.push_back();
81             unevenClosedQuad->moveTo(0, -0);
82             unevenClosedQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100),
83                                      SkIntToScalar(75), SkIntToScalar(75));
84         }
85 
86         // Two problem cases for gpu hairline renderer found by shapeops testing. These used
87         // to assert that the computed bounding box didn't contain all the vertices.
88         {
89             SkPath* problem1 = &fPaths.push_back();
90             problem1->moveTo(SkIntToScalar(4), SkIntToScalar(6));
91             problem1->cubicTo(SkIntToScalar(5), SkIntToScalar(6),
92                               SkIntToScalar(5), SkIntToScalar(4),
93                               SkIntToScalar(4), SkIntToScalar(0));
94             problem1->close();
95         }
96 
97         {
98             SkPath* problem2 = &fPaths.push_back();
99             problem2->moveTo(SkIntToScalar(5), SkIntToScalar(1));
100             problem2->lineTo(4.32787323f, 1.67212653f);
101             problem2->cubicTo(2.75223875f, 3.24776125f,
102                               3.00581908f, 4.51236057f,
103                               3.7580452f, 4.37367964f);
104             problem2->cubicTo(4.66472578f, 3.888381f,
105                               5.f, 2.875f,
106                               5.f, 1.f);
107             problem2->close();
108         }
109 
110         // Three paths that show the same bug (missing end caps)
111         {
112             // A caret (crbug.com/131770)
113             SkPath* bug0 = &fPaths.push_back();
114             bug0->moveTo(6.5f,5.5f);
115             bug0->lineTo(3.5f,0.5f);
116             bug0->moveTo(0.5f,5.5f);
117             bug0->lineTo(3.5f,0.5f);
118         }
119 
120         {
121             // An X (crbug.com/137317)
122             SkPath* bug1 = &fPaths.push_back();
123 
124             bug1->moveTo(1, 1);
125             bug1->lineTo(6, 6);
126             bug1->moveTo(1, 6);
127             bug1->lineTo(6, 1);
128         }
129 
130         {
131             // A right angle (crbug.com/137465 and crbug.com/256776)
132             SkPath* bug2 = &fPaths.push_back();
133 
134             bug2->moveTo(5.5f, 5.5f);
135             bug2->lineTo(5.5f, 0.5f);
136             bug2->lineTo(0.5f, 0.5f);
137         }
138 
139         {
140             // Arc example to test imperfect truncation bug (crbug.com/295626)
141             constexpr SkScalar kRad = SkIntToScalar(2000);
142             constexpr SkScalar kStartAngle = 262.59717f;
143             constexpr SkScalar kSweepAngle = SkScalarHalf(17.188717f);
144 
145             SkPath* bug = &fPaths.push_back();
146 
147             // Add a circular arc
148             SkRect circle = SkRect::MakeLTRB(-kRad, -kRad, kRad, kRad);
149             bug->addArc(circle, kStartAngle, kSweepAngle);
150 
151             // Now add the chord that should cap the circular arc
152             SkScalar cosV, sinV = SkScalarSinCos(SkDegreesToRadians(kStartAngle), &cosV);
153 
154             SkPoint p0 = SkPoint::Make(kRad * cosV, kRad * sinV);
155 
156             sinV = SkScalarSinCos(SkDegreesToRadians(kStartAngle + kSweepAngle), &cosV);
157 
158             SkPoint p1 = SkPoint::Make(kRad * cosV, kRad * sinV);
159 
160             bug->moveTo(p0);
161             bug->lineTo(p1);
162         }
163     }
164 
onDraw(SkCanvas * canvas)165     void onDraw(SkCanvas* canvas) override {
166         constexpr SkAlpha kAlphaValue[] = { 0xFF, 0x40 };
167         constexpr SkScalar kWidths[] = { 0, 0.5f, 1.5f };
168 
169         enum {
170             kMargin = 5,
171         };
172         int wrapX = 1250 - kMargin;
173 
174         SkScalar maxH = 0;
175         canvas->translate(SkIntToScalar(kMargin), SkIntToScalar(kMargin));
176         canvas->save();
177 
178         SkScalar x = SkIntToScalar(kMargin);
179         for (int p = 0; p < fPaths.count(); ++p) {
180             for (size_t a = 0; a < SK_ARRAY_COUNT(kAlphaValue); ++a) {
181                 for (int aa = 0; aa < 2; ++aa) {
182                     for (size_t w = 0; w < SK_ARRAY_COUNT(kWidths); w++) {
183                         const SkRect& bounds = fPaths[p].getBounds();
184 
185                         if (x + bounds.width() > wrapX) {
186                             canvas->restore();
187                             canvas->translate(0, maxH + SkIntToScalar(kMargin));
188                             canvas->save();
189                             maxH = 0;
190                             x = SkIntToScalar(kMargin);
191                         }
192 
193                         SkPaint paint;
194                         paint.setARGB(kAlphaValue[a], 0, 0, 0);
195                         paint.setAntiAlias(SkToBool(aa));
196                         paint.setStyle(SkPaint::kStroke_Style);
197                         paint.setStrokeWidth(kWidths[w]);
198 
199                         canvas->save();
200                         canvas->translate(-bounds.fLeft, -bounds.fTop);
201                         canvas->drawPath(fPaths[p], paint);
202                         canvas->restore();
203 
204                         maxH = SkMaxScalar(maxH, bounds.height());
205 
206                         SkScalar dx = bounds.width() + SkIntToScalar(kMargin);
207                         x += dx;
208                         canvas->translate(dx, 0);
209                     }
210                 }
211             }
212         }
213         canvas->restore();
214     }
215 
216 private:
217     SkTArray<SkPath> fPaths;
218     typedef GM INHERITED;
219 };
220 
draw_squarehair_tests(SkCanvas * canvas,SkScalar width,SkPaint::Cap cap,bool aa)221 static void draw_squarehair_tests(SkCanvas* canvas, SkScalar width, SkPaint::Cap cap, bool aa) {
222     SkPaint paint;
223     paint.setStrokeCap(cap);
224     paint.setStrokeWidth(width);
225     paint.setAntiAlias(aa);
226     paint.setStyle(SkPaint::kStroke_Style);
227     canvas->drawLine(10, 10, 20, 10, paint);
228     canvas->drawLine(30, 10, 30, 20, paint);
229     canvas->drawLine(40, 10, 50, 20, paint);
230     SkPath path;
231     path.moveTo(60, 10);
232     path.quadTo(60, 20, 70, 20);
233     path.conicTo(70, 10, 80, 10, 0.707f);
234     canvas->drawPath(path, paint);
235     path.reset();
236     path.moveTo(90, 10);
237     path.cubicTo(90, 20, 100, 20, 100, 10);
238     path.lineTo(110, 10);
239     canvas->drawPath(path, paint);
240     canvas->translate(0, 30);
241 }
242 
243 DEF_SIMPLE_GM(squarehair, canvas, 240, 360) {
244     const bool aliases[] = { false, true };
245     const SkScalar widths[] = { 0, 0.999f, 1, 1.001f };
246     const SkPaint::Cap caps[] = { SkPaint::kButt_Cap, SkPaint::kSquare_Cap, SkPaint::kRound_Cap };
247     for (auto alias : aliases) {
248         canvas->save();
249         for (auto width : widths) {
250             for (auto cap : caps) {
251                 draw_squarehair_tests(canvas, width, cap, alias);
252             }
253         }
254         canvas->restore();
255         canvas->translate(120, 0);
256     }
257 }
258 
259 //////////////////////////////////////////////////////////////////////////////
260 
261 DEF_GM( return new HairlinesGM; )
262 
263 }
264