• 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/gm.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkPaint.h"
12 #include "include/core/SkPath.h"
13 #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"
19 #include "include/private/SkTArray.h"
20 
21 namespace skiagm {
22 
23 class HairlinesGM : public GM {
24 protected:
25 
26 
onShortName()27     SkString onShortName() override {
28         return SkString("hairlines");
29     }
30 
onISize()31     SkISize onISize() override { return SkISize::Make(1250, 1250); }
32 
onOnceBeforeDraw()33     void onOnceBeforeDraw() override {
34         {
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         }
93 
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));
108             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);
115             problem2->close();
116         }
117 
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         }
146 
147         {
148             // Arc example to test imperfect truncation bug (crbug.com/295626)
149             constexpr SkScalar kRad = SkIntToScalar(2000);
150             constexpr SkScalar kStartAngle = 262.59717f;
151             constexpr SkScalar kSweepAngle = SkScalarHalf(17.188717f);
152 
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
160             SkPoint p0 = { kRad * SkScalarCos(SkDegreesToRadians(kStartAngle)),
161                            kRad * SkScalarSin(SkDegreesToRadians(kStartAngle)) };
162 
163             SkPoint p1 = { kRad * SkScalarCos(SkDegreesToRadians(kStartAngle + kSweepAngle)),
164                            kRad * SkScalarSin(SkDegreesToRadians(kStartAngle + kSweepAngle)) };
165 
166             bug->moveTo(p0);
167             bug->lineTo(p1);
168         }
169     }
170 
onDraw(SkCanvas * canvas)171     void onDraw(SkCanvas* canvas) override {
172         constexpr SkAlpha kAlphaValue[] = { 0xFF, 0x40 };
173         constexpr SkScalar kWidths[] = { 0, 0.5f, 1.5f };
174 
175         enum {
176             kMargin = 5,
177         };
178         int wrapX = 1250 - kMargin;
179 
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) {
188                     for (size_t w = 0; w < SK_ARRAY_COUNT(kWidths); w++) {
189                         const SkRect& bounds = fPaths[p].getBounds();
190 
191                         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 
205                         canvas->save();
206                         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);
215                     }
216                 }
217             }
218         }
219         canvas->restore();
220     }
221 
222 private:
223     SkTArray<SkPath> fPaths;
224     typedef GM INHERITED;
225 };
226 
draw_squarehair_tests(SkCanvas * canvas,SkScalar width,SkPaint::Cap cap,bool aa)227 static 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 
249 DEF_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 
265 //////////////////////////////////////////////////////////////////////////////
266 
267 DEF_GM( return new HairlinesGM; )
268 
269 }
270