• 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 "SkTArray.h"
11 
12 namespace skiagm {
13 
14 class HairlinesGM : public GM {
15 protected:
onGetFlags() const16     virtual uint32_t onGetFlags() const SK_OVERRIDE {
17         return kSkipTiled_Flag;
18     }
19 
20 
onShortName()21     virtual SkString onShortName() SK_OVERRIDE {
22         return SkString("hairlines");
23     }
24 
onISize()25     virtual SkISize onISize() { return SkISize::Make(800, 600); }
26 
onOnceBeforeDraw()27     virtual void onOnceBeforeDraw() SK_OVERRIDE {
28         {
29             SkPath* lineAnglesPath = &fPaths.push_back();
30             enum {
31                 kNumAngles = 15,
32                 kRadius = 40,
33             };
34             for (int i = 0; i < kNumAngles; ++i) {
35                 SkScalar angle = SK_ScalarPI * SkIntToScalar(i) / kNumAngles;
36                 SkScalar x = kRadius * SkScalarCos(angle);
37                 SkScalar y = kRadius * SkScalarSin(angle);
38                 lineAnglesPath->moveTo(x, y);
39                 lineAnglesPath->lineTo(-x, -y);
40             }
41         }
42 
43         {
44             SkPath* kindaTightQuad = &fPaths.push_back();
45             kindaTightQuad->moveTo(0, -10 * SK_Scalar1);
46             kindaTightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -10 * SK_Scalar1, 0);
47         }
48 
49         {
50             SkPath* tightQuad = &fPaths.push_back();
51             tightQuad->moveTo(0, -5 * SK_Scalar1);
52             tightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -5 * SK_Scalar1, 0);
53         }
54 
55         {
56             SkPath* tighterQuad = &fPaths.push_back();
57             tighterQuad->moveTo(0, -2 * SK_Scalar1);
58             tighterQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -2 * SK_Scalar1, 0);
59         }
60 
61         {
62             SkPath* unevenTighterQuad = &fPaths.push_back();
63             unevenTighterQuad->moveTo(0, -1 * SK_Scalar1);
64             SkPoint p;
65             p.set(-2 * SK_Scalar1 + 3 * SkIntToScalar(102) / 4, SkIntToScalar(75));
66             unevenTighterQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), p.fX, p.fY);
67         }
68 
69         {
70             SkPath* reallyTightQuad = &fPaths.push_back();
71             reallyTightQuad->moveTo(0, -1 * SK_Scalar1);
72             reallyTightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -1 * SK_Scalar1, 0);
73         }
74 
75         {
76             SkPath* closedQuad = &fPaths.push_back();
77             closedQuad->moveTo(0, -0);
78             closedQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), 0, 0);
79         }
80 
81         {
82             SkPath* unevenClosedQuad = &fPaths.push_back();
83             unevenClosedQuad->moveTo(0, -0);
84             unevenClosedQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100),
85                                      SkIntToScalar(75), SkIntToScalar(75));
86         }
87 
88         // Two problem cases for gpu hairline renderer found by shapeops testing. These used
89         // to assert that the computed bounding box didn't contain all the vertices.
90         {
91             SkPath* problem1 = &fPaths.push_back();
92             problem1->moveTo(SkIntToScalar(4), SkIntToScalar(6));
93             problem1->cubicTo(SkIntToScalar(5), SkIntToScalar(6),
94                               SkIntToScalar(5), SkIntToScalar(4),
95                               SkIntToScalar(4), SkIntToScalar(0));
96             problem1->close();
97         }
98 
99         {
100             SkPath* problem2 = &fPaths.push_back();
101             problem2->moveTo(SkIntToScalar(5), SkIntToScalar(1));
102             problem2->lineTo(4.32787323f, 1.67212653f);
103             problem2->cubicTo(2.75223875f, 3.24776125f,
104                               3.00581908f, 4.51236057f,
105                               3.7580452f, 4.37367964f);
106             problem2->cubicTo(4.66472578f, 3.888381f,
107                               5.f, 2.875f,
108                               5.f, 1.f);
109             problem2->close();
110         }
111 
112         // Three paths that show the same bug (missing end caps)
113         {
114             // A caret (crbug.com/131770)
115             SkPath* bug0 = &fPaths.push_back();
116             bug0->moveTo(6.5f,5.5f);
117             bug0->lineTo(3.5f,0.5f);
118             bug0->moveTo(0.5f,5.5f);
119             bug0->lineTo(3.5f,0.5f);
120         }
121 
122         {
123             // An X (crbug.com/137317)
124             SkPath* bug1 = &fPaths.push_back();
125 
126             bug1->moveTo(1, 1);
127             bug1->lineTo(6, 6);
128             bug1->moveTo(1, 6);
129             bug1->lineTo(6, 1);
130         }
131 
132         {
133             // A right angle (crbug.com/137465 and crbug.com/256776)
134             SkPath* bug2 = &fPaths.push_back();
135 
136             bug2->moveTo(5.5f, 5.5f);
137             bug2->lineTo(5.5f, 0.5f);
138             bug2->lineTo(0.5f, 0.5f);
139         }
140 
141         {
142             // Arc example to test imperfect truncation bug (crbug.com/295626)
143             static const SkScalar kRad = SkIntToScalar(2000);
144             static const SkScalar kStartAngle = 262.59717f;
145             static const SkScalar kSweepAngle = SkScalarHalf(17.188717f);
146 
147             SkPath* bug = &fPaths.push_back();
148 
149             // Add a circular arc
150             SkRect circle = SkRect::MakeLTRB(-kRad, -kRad, kRad, kRad);
151             bug->addArc(circle, kStartAngle, kSweepAngle);
152 
153             // Now add the chord that should cap the circular arc
154             SkScalar cosV, sinV = SkScalarSinCos(SkDegreesToRadians(kStartAngle), &cosV);
155 
156             SkPoint p0 = SkPoint::Make(kRad * cosV, kRad * sinV);
157 
158             sinV = SkScalarSinCos(SkDegreesToRadians(kStartAngle + kSweepAngle), &cosV);
159 
160             SkPoint p1 = SkPoint::Make(kRad * cosV, kRad * sinV);
161 
162             bug->moveTo(p0);
163             bug->lineTo(p1);
164         }
165     }
166 
onDraw(SkCanvas * canvas)167     virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
168         static const SkAlpha kAlphaValue[] = { 0xFF, 0x40 };
169 
170         enum {
171             kMargin = 5,
172         };
173         int wrapX = canvas->getDeviceSize().fWidth - kMargin;
174 
175         SkScalar maxH = 0;
176         canvas->translate(SkIntToScalar(kMargin), SkIntToScalar(kMargin));
177         canvas->save();
178 
179         SkScalar x = SkIntToScalar(kMargin);
180         for (int p = 0; p < fPaths.count(); ++p) {
181             for (size_t a = 0; a < SK_ARRAY_COUNT(kAlphaValue); ++a) {
182                 for (int aa = 0; aa < 2; ++aa) {
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(0);
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         canvas->restore();
213     }
214 
215 private:
216     SkTArray<SkPath> fPaths;
217     typedef GM INHERITED;
218 };
219 
220 //////////////////////////////////////////////////////////////////////////////
221 
MyFactory(void *)222 static GM* MyFactory(void*) { return new HairlinesGM; }
223 static GMRegistry reg(MyFactory);
224 
225 }
226