• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 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 
create_ngon(int n,SkPoint * pts,SkScalar width,SkScalar height)10 static void create_ngon(int n, SkPoint* pts, SkScalar width, SkScalar height) {
11     float angleStep = 360.0f / n, angle = 0.0f, sin, cos;
12     if ((n % 2) == 1) {
13         angle = angleStep/2.0f;
14     }
15 
16     for (int i = 0; i < n; ++i) {
17         sin = SkScalarSinCos(SkDegreesToRadians(angle), &cos);
18         pts[i].fX = -sin * width;
19         pts[i].fY = cos * height;
20         angle += angleStep;
21     }
22 }
23 
24 namespace skiagm {
25 
26 // This GM is intended to exercise Ganesh's handling of convex line-only
27 // paths
28 class ConvexLineOnlyPathsGM : public GM {
29 public:
ConvexLineOnlyPathsGM()30     ConvexLineOnlyPathsGM() {
31         this->setBGColor(0xFFFFFFFF);
32     }
33 
34 protected:
onShortName()35     SkString onShortName() override { return SkString("convex-lineonly-paths"); }
onISize()36     SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
runAsBench() const37     bool runAsBench() const override { return true; }
38 
GetPath(int index,int offset,SkPath::Direction dir)39     static SkPath GetPath(int index, int offset, SkPath::Direction dir) {
40         // narrow rect
41         const SkPoint gPoints0[] = {
42             { -1.5f, -50.0f },
43             {  1.5f, -50.0f },
44             {  1.5f,  50.0f },
45             { -1.5f,  50.0f }
46         };
47         // narrow rect on an angle
48         const SkPoint gPoints1[] = {
49             { -50.0f, -49.0f },
50             { -49.0f, -50.0f },
51             {  50.0f,  49.0f },
52             {  49.0f,  50.0f }
53         };
54         // trap - narrow on top - wide on bottom
55         const SkPoint gPoints2[] = {
56             { -10.0f, -50.0f },
57             {  10.0f, -50.0f },
58             {  50.0f,  50.0f },
59             { -50.0f,  50.0f }
60         };
61         // wide skewed rect
62         const SkPoint gPoints3[] = {
63             { -50.0f, -50.0f },
64             {   0.0f, -50.0f },
65             {  50.0f,  50.0f },
66             {   0.0f,  50.0f }
67         };
68         // thin rect with colinear-ish lines
69         const SkPoint gPoints4[] = {
70             { -6.0f, -50.0f },
71             {  4.0f, -50.0f },
72             {  5.0f, -25.0f },
73             {  6.0f,   0.0f },
74             {  5.0f,  25.0f },
75             {  4.0f,  50.0f },
76             { -4.0f,  50.0f }
77         };
78         // degenerate
79         const SkPoint gPoints5[] = {
80             { -0.025f, -0.025f  },
81             {  0.025f, -0.025f  },
82             {  0.025f,  0.025f },
83             { -0.025f,  0.025f }
84         };
85         // Triangle in which the first point should fuse with last
86         const SkPoint gPoints6[] = {
87             { -20.0f, -13.0f },
88             { -20.0f, -13.05f },
89             {  20.0f, -13.0f },
90             {  20.0f,  27.0f }
91         };
92         // thin rect with colinear lines
93         const SkPoint gPoints7[] = {
94             { -10.0f, -50.0f },
95             {  10.0f, -50.0f },
96             {  10.0f, -25.0f },
97             {  10.0f,   0.0f },
98             {  10.0f,  25.0f },
99             {  10.0f,  50.0f },
100             { -10.0f,  50.0f }
101         };
102         // capped teardrop
103         const SkPoint gPoints8[] = {
104             {  50.00f,  50.00f },
105             {   0.00f,  50.00f },
106             { -15.45f,  47.55f },
107             { -29.39f,  40.45f },
108             { -40.45f,  29.39f },
109             { -47.55f,  15.45f },
110             { -50.00f,   0.00f },
111             { -47.55f, -15.45f },
112             { -40.45f, -29.39f },
113             { -29.39f, -40.45f },
114             { -15.45f, -47.55f },
115             {   0.00f, -50.00f },
116             {  50.00f, -50.00f }
117         };
118         // teardrop
119         const SkPoint gPoints9[] = {
120             {   4.39f,  40.45f },
121             {  -9.55f,  47.55f },
122             { -25.00f,  50.00f },
123             { -40.45f,  47.55f },
124             { -54.39f,  40.45f },
125             { -65.45f,  29.39f },
126             { -72.55f,  15.45f },
127             { -75.00f,   0.00f },
128             { -72.55f, -15.45f },
129             { -65.45f, -29.39f },
130             { -54.39f, -40.45f },
131             { -40.45f, -47.55f },
132             { -25.0f,  -50.0f },
133             {  -9.55f, -47.55f },
134             {   4.39f, -40.45f },
135             {  75.00f,   0.00f }
136         };
137         // clipped triangle
138         const SkPoint gPoints10[] = {
139             { -10.0f, -50.0f },
140             {  10.0f, -50.0f },
141             {  50.0f,  31.0f },
142             {  40.0f,  50.0f },
143             { -40.0f,  50.0f },
144             { -50.0f,  31.0f },
145         };
146 
147         const SkPoint* gPoints[] = {
148             gPoints0, gPoints1, gPoints2, gPoints3, gPoints4, gPoints5, gPoints6,
149             gPoints7, gPoints8, gPoints9, gPoints10,
150         };
151 
152         const size_t gSizes[] = {
153             SK_ARRAY_COUNT(gPoints0),
154             SK_ARRAY_COUNT(gPoints1),
155             SK_ARRAY_COUNT(gPoints2),
156             SK_ARRAY_COUNT(gPoints3),
157             SK_ARRAY_COUNT(gPoints4),
158             SK_ARRAY_COUNT(gPoints5),
159             SK_ARRAY_COUNT(gPoints6),
160             SK_ARRAY_COUNT(gPoints7),
161             SK_ARRAY_COUNT(gPoints8),
162             SK_ARRAY_COUNT(gPoints9),
163             SK_ARRAY_COUNT(gPoints10),
164         };
165         SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gSizes) == SK_ARRAY_COUNT(gPoints), array_mismatch);
166 
167         SkAutoTDeleteArray<SkPoint> data(NULL);
168         const SkPoint* points;
169         int numPts;
170         if (index < (int) SK_ARRAY_COUNT(gPoints)) {
171             // manually specified
172             points = gPoints[index];
173             numPts = (int) gSizes[index];
174         } else {
175             // procedurally generated
176             SkScalar width = kMaxPathHeight/2;
177             SkScalar height = kMaxPathHeight/2;
178             switch (index-SK_ARRAY_COUNT(gPoints)) {
179             case 0:
180                 numPts = 3;
181                 break;
182             case 1:
183                 numPts = 4;
184                 break;
185             case 2:
186                 numPts = 5;
187                 break;
188             case 3:             // squashed pentagon
189                 numPts = 5;
190                 width = kMaxPathHeight/5;
191                 break;
192             case 4:
193                 numPts = 6;
194                 break;
195             case 5:
196                 numPts = 8;
197                 break;
198             case 6:              // squashed octogon
199                 numPts = 8;
200                 width = kMaxPathHeight/5;
201                 break;
202             case 7:
203                 numPts = 20;
204                 break;
205             case 8:
206                 numPts = 100;
207                 break;
208             default:
209                 numPts = 3;
210                 break;
211             }
212 
213             data.reset(SkNEW_ARRAY(SkPoint, numPts));
214 
215             create_ngon(numPts, data.get(), width, height);
216             points = data.get();
217         }
218 
219         SkPath path;
220 
221         if (SkPath::kCW_Direction == dir) {
222             path.moveTo(points[0]);
223             for (int i = 1; i < numPts; ++i) {
224                 path.lineTo(points[i]);
225             }
226         } else {
227             path.moveTo(points[numPts-1]);
228             for (int i = numPts-2; i >= 0; --i) {
229                 path.lineTo(points[i]);
230             }
231         }
232 
233         path.close();
234 #ifdef SK_DEBUG
235         // Each path this method returns should be convex, only composed of
236         // lines, wound the right direction, and short enough to fit in one
237         // of the GMs rows.
238         SkASSERT(path.isConvex());
239         SkASSERT(SkPath::kLine_SegmentMask == path.getSegmentMasks());
240         SkPath::Direction actualDir;
241         SkASSERT(path.cheapComputeDirection(&actualDir));
242         SkASSERT(dir == actualDir);
243         SkRect bounds = path.getBounds();
244         SkASSERT(SkScalarNearlyEqual(bounds.centerX(), 0.0f));
245         SkASSERT(bounds.height() <= kMaxPathHeight);
246 #endif
247         return path;
248     }
249 
250     // Draw a single path several times, shrinking it, flipping its direction
251     // and changing its start vertex each time.
drawPath(SkCanvas * canvas,int index,SkPoint * offset)252     void drawPath(SkCanvas* canvas, int index, SkPoint* offset) {
253 
254         SkPoint center;
255         {
256             SkPath path = GetPath(index, 0, SkPath::kCW_Direction);
257             if (offset->fX+path.getBounds().width() > kGMWidth) {
258                 offset->fX = 0;
259                 offset->fY += kMaxPathHeight;
260             }
261             center = { offset->fX + SkScalarHalf(path.getBounds().width()), offset->fY};
262             offset->fX += path.getBounds().width();
263         }
264 
265         const SkColor colors[2] = { SK_ColorBLACK, SK_ColorWHITE };
266         const SkPath::Direction dirs[2] = { SkPath::kCW_Direction, SkPath::kCCW_Direction };
267         const float scales[] = { 1.0f, 0.75f, 0.5f, 0.25f, 0.1f, 0.01f, 0.001f };
268 
269         SkPaint paint;
270         paint.setAntiAlias(true);
271 
272         for (size_t i = 0; i < SK_ARRAY_COUNT(scales); ++i) {
273             SkPath path = GetPath(index, (int) i, dirs[i%2]);
274 
275             canvas->save();
276                 canvas->translate(center.fX, center.fY);
277                 canvas->scale(scales[i], scales[i]);
278                 paint.setColor(colors[i%2]);
279                 canvas->drawPath(path, paint);
280             canvas->restore();
281         }
282     }
283 
onDraw(SkCanvas * canvas)284     void onDraw(SkCanvas* canvas) override {
285         // the right edge of the last drawn path
286         SkPoint offset = { 0, SkScalarHalf(kMaxPathHeight) };
287 
288         for (int i = 0; i < kNumPaths; ++i) {
289             this->drawPath(canvas, i, &offset);
290         }
291 
292         // Repro for crbug.com/472723 (Missing AA on portions of graphic with GPU rasterization)
293         {
294             canvas->translate(356.0f, 50.0f);
295 
296             SkPaint p;
297             p.setAntiAlias(true);
298 
299             SkPath p1;
300             p1.moveTo(60.8522949f, 364.671021f);
301             p1.lineTo(59.4380493f, 364.671021f);
302             p1.lineTo(385.414276f, 690.647217f);
303             p1.lineTo(386.121399f, 689.940125f);
304             canvas->drawPath(p1, p);
305         }
306     }
307 
308 private:
309     static const int kNumPaths      = 20;
310     static const int kMaxPathHeight = 100;
311     static const int kGMWidth       = 512;
312     static const int kGMHeight      = 512;
313 
314     typedef GM INHERITED;
315 };
316 
317 //////////////////////////////////////////////////////////////////////////////
318 
319 DEF_GM( return SkNEW(ConvexLineOnlyPathsGM); )
320 
321 }
322