1 /*
2  * Unit test suite for paths
3  *
4  * Copyright (C) 2007 Google (Evan Stade)
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "objbase.h"
22 #include "gdiplus.h"
23 #include "wine/test.h"
24 #include <math.h>
25 
26 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
27 #define expectf(expected, got) ok(fabs(expected - got) < 2.0, "Expected %.2f, got %.2f\n", expected, got)
28 #define POINT_TYPE_MAX_LEN (75)
29 
30 static void stringify_point_type(PathPointType type, char * name)
31 {
32     *name = '\0';
33 
34     switch(type & PathPointTypePathTypeMask){
35         case PathPointTypeStart:
36             strcat(name, "PathPointTypeStart");
37             break;
38         case PathPointTypeLine:
39             strcat(name, "PathPointTypeLine");
40             break;
41         case PathPointTypeBezier:
42             strcat(name, "PathPointTypeBezier");
43             break;
44         default:
45             strcat(name, "Unknown type");
46             return;
47     }
48 
49     type &= ~PathPointTypePathTypeMask;
50     if(type & ~((PathPointTypePathMarker | PathPointTypeCloseSubpath))){
51         *name = '\0';
52         strcat(name, "Unknown type");
53         return;
54     }
55 
56     if(type & PathPointTypePathMarker)
57         strcat(name, " | PathPointTypePathMarker");
58     if(type & PathPointTypeCloseSubpath)
59         strcat(name, " | PathPointTypeCloseSubpath");
60 }
61 
62 /* this helper structure and function modeled after gdi path.c test */
63 typedef struct
64 {
65     REAL X, Y;
66     BYTE type;
67 
68     /* How many extra entries before this one only on wine
69      * but not on native? */
70     int wine_only_entries_preceding;
71 
72     /* 0 - This entry matches on wine.
73      * 1 - This entry corresponds to a single entry on wine that does not match the native entry.
74      * 2 - This entry is currently skipped on wine but present on native. */
75     int todo;
76 } path_test_t;
77 
78 static void ok_path(GpPath* path, const path_test_t *expected, INT expected_size, BOOL todo_size)
79 {
80     BYTE * types;
81     INT size, idx = 0, eidx = 0, numskip;
82     GpPointF * points;
83     char ename[POINT_TYPE_MAX_LEN], name[POINT_TYPE_MAX_LEN];
84 
85     if(GdipGetPointCount(path, &size) != Ok){
86         skip("Cannot perform path comparisons due to failure to retrieve path.\n");
87         return;
88     }
89 
90     todo_wine_if (todo_size)
91         ok(size == expected_size, "Path size %d does not match expected size %d\n",
92             size, expected_size);
93 
94     points = HeapAlloc(GetProcessHeap(), 0, size * sizeof(GpPointF));
95     types = HeapAlloc(GetProcessHeap(), 0, size);
96 
97     if(GdipGetPathPoints(path, points, size) != Ok || GdipGetPathTypes(path, types, size) != Ok){
98         skip("Cannot perform path comparisons due to failure to retrieve path.\n");
99         goto end;
100     }
101 
102     numskip = expected_size ? expected[eidx].wine_only_entries_preceding : 0;
103     while (idx < size && eidx < expected_size){
104         /* We allow a few pixels fudge in matching X and Y coordinates to account for imprecision in
105          * floating point to integer conversion */
106         BOOL match = (types[idx] == expected[eidx].type) &&
107             fabs(points[idx].X - expected[eidx].X) <= 2.0 &&
108             fabs(points[idx].Y - expected[eidx].Y) <= 2.0;
109 
110         stringify_point_type(expected[eidx].type, ename);
111         stringify_point_type(types[idx], name);
112 
113         todo_wine_if (expected[eidx].todo || numskip)
114             ok(match, "Expected #%d: %s (%.1f,%.1f) but got %s (%.1f,%.1f)\n", eidx,
115                ename, expected[eidx].X, expected[eidx].Y,
116                name, points[idx].X, points[idx].Y);
117 
118         if (match || expected[eidx].todo != 2)
119             idx++;
120         if (match || !numskip--)
121             numskip = expected[++eidx].wine_only_entries_preceding;
122     }
123 
124 end:
125     HeapFree(GetProcessHeap(), 0, types);
126     HeapFree(GetProcessHeap(), 0, points);
127 }
128 
129 static void test_constructor_destructor(void)
130 {
131     GpStatus status;
132     GpPath* path = NULL;
133 
134     status = GdipCreatePath(FillModeAlternate, &path);
135     expect(Ok, status);
136     ok(path != NULL, "Expected path to be initialized\n");
137 
138     status = GdipDeletePath(NULL);
139     expect(InvalidParameter, status);
140 
141     status = GdipDeletePath(path);
142     expect(Ok, status);
143 }
144 
145 static void test_getpathdata(void)
146 {
147     GpPath *path;
148     GpPathData data;
149     GpStatus status;
150     INT count;
151 
152     status = GdipCreatePath(FillModeAlternate, &path);
153     expect(Ok, status);
154     status = GdipAddPathLine(path, 5.0, 5.0, 100.0, 50.0);
155     expect(Ok, status);
156 
157     status = GdipGetPointCount(path, &count);
158     expect(Ok, status);
159     expect(2, count);
160 
161     data.Count  = count;
162     data.Types  = GdipAlloc(sizeof(BYTE) * count);
163     data.Points = GdipAlloc(sizeof(PointF) * count);
164 
165     status = GdipGetPathData(path, &data);
166     expect(Ok, status);
167     expect((data.Points[0].X == 5.0) && (data.Points[0].Y == 5.0) &&
168            (data.Points[1].X == 100.0) && (data.Points[1].Y == 50.0), TRUE);
169     expect((data.Types[0] == PathPointTypeStart) && (data.Types[1] == PathPointTypeLine), TRUE);
170 
171     GdipFree(data.Points);
172     GdipFree(data.Types);
173     GdipDeletePath(path);
174 }
175 
176 static path_test_t line2_path[] = {
177     {0.0, 50.0, PathPointTypeStart, 0, 0}, /*0*/
178     {5.0, 45.0, PathPointTypeLine, 0, 0}, /*1*/
179     {0.0, 40.0, PathPointTypeLine, 0, 0}, /*2*/
180     {15.0, 35.0, PathPointTypeLine, 0, 0}, /*3*/
181     {0.0, 30.0, PathPointTypeLine, 0, 0}, /*4*/
182     {25.0, 25.0, PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*5*/
183     {0.0, 20.0, PathPointTypeStart, 0, 0}, /*6*/
184     {35.0, 15.0, PathPointTypeLine, 0, 0}, /*7*/
185     {0.0, 10.0, PathPointTypeLine, 0, 0} /*8*/
186     };
187 
188 static void test_line2(void)
189 {
190     GpStatus status;
191     GpPath* path;
192     int i;
193     GpPointF line2_points[9];
194 
195     for(i = 0; i < 9; i ++){
196         line2_points[i].X = i * 5.0 * (REAL)(i % 2);
197         line2_points[i].Y = 50.0 - i * 5.0;
198     }
199 
200     GdipCreatePath(FillModeAlternate, &path);
201     status = GdipAddPathLine2(path, line2_points, 3);
202     expect(Ok, status);
203     status = GdipAddPathLine2(path, &(line2_points[3]), 3);
204     expect(Ok, status);
205     status = GdipClosePathFigure(path);
206     expect(Ok, status);
207     status = GdipAddPathLine2(path, &(line2_points[6]), 3);
208     expect(Ok, status);
209 
210     ok_path(path, line2_path, sizeof(line2_path)/sizeof(path_test_t), FALSE);
211 
212     GdipDeletePath(path);
213 }
214 
215 static path_test_t arc_path[] = {
216     {600.0, 450.0, PathPointTypeStart, 0, 0}, /*0*/
217     {600.0, 643.3, PathPointTypeBezier, 0, 0}, /*1*/
218     {488.1, 800.0, PathPointTypeBezier, 0, 0}, /*2*/
219     {350.0, 800.0, PathPointTypeBezier, 0, 0}, /*3*/
220     {600.0, 450.0, PathPointTypeLine, 0, 0}, /*4*/
221     {600.0, 643.3, PathPointTypeBezier, 0, 0}, /*5*/
222     {488.1, 800.0, PathPointTypeBezier, 0, 0}, /*6*/
223     {350.0, 800.0, PathPointTypeBezier, 0, 0}, /*7*/
224     {329.8, 800.0, PathPointTypeBezier, 0, 0}, /*8*/
225     {309.7, 796.6, PathPointTypeBezier, 0, 0}, /*9*/
226     {290.1, 789.8, PathPointTypeBezier, 0, 0}, /*10*/
227     {409.9, 110.2, PathPointTypeLine, 0, 0}, /*11*/
228     {544.0, 156.5, PathPointTypeBezier, 0, 0}, /*12*/
229     {625.8, 346.2, PathPointTypeBezier, 0, 0}, /*13*/
230     {592.7, 533.9, PathPointTypeBezier, 0, 0}, /*14*/
231     {592.5, 535.3, PathPointTypeBezier, 0, 0}, /*15*/
232     {592.2, 536.7, PathPointTypeBezier, 0, 0}, /*16*/
233     {592.0, 538.1, PathPointTypeBezier, 0, 0}, /*17*/
234     {409.9, 789.8, PathPointTypeLine, 0, 0}, /*18*/
235     {544.0, 743.5, PathPointTypeBezier, 0, 0}, /*19*/
236     {625.8, 553.8, PathPointTypeBezier, 0, 0}, /*20*/
237     {592.7, 366.1, PathPointTypeBezier, 0, 0}, /*21*/
238     {592.5, 364.7, PathPointTypeBezier, 0, 0}, /*22*/
239     {592.2, 363.3, PathPointTypeBezier, 0, 0}, /*23*/
240     {592.0, 361.9, PathPointTypeBezier, 0, 0}, /*24*/
241     {540.4, 676.9, PathPointTypeLine, 0, 0}, /*25*/
242     {629.9, 529.7, PathPointTypeBezier, 0, 0}, /*26*/
243     {617.2, 308.8, PathPointTypeBezier, 0, 0}, /*27*/
244     {512.1, 183.5, PathPointTypeBezier, 0, 0}, /*28*/
245     {406.9, 58.2, PathPointTypeBezier, 0, 0}, /*29*/
246     {249.1, 75.9, PathPointTypeBezier, 0, 0}, /*30*/
247     {159.6, 223.1, PathPointTypeBezier, 0, 0}, /*31*/
248     {70.1, 370.3, PathPointTypeBezier, 0, 0}, /*32*/
249     {82.8, 591.2, PathPointTypeBezier, 0, 0}, /*33*/
250     {187.9, 716.5, PathPointTypeBezier, 0, 0}, /*34*/
251     {293.1, 841.8, PathPointTypeBezier, 0, 0}, /*35*/
252     {450.9, 824.1, PathPointTypeBezier, 0, 0}, /*36*/
253     {540.4, 676.9, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 1} /*37*/
254     };
255 
256 static void test_arc(void)
257 {
258     GpStatus status;
259     GpPath* path;
260 
261     GdipCreatePath(FillModeAlternate, &path);
262     /* Exactly 90 degrees */
263     status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 90.0);
264     expect(Ok, status);
265     /* Over 90 degrees */
266     status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
267     expect(Ok, status);
268     /* Negative start angle */
269     status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, -80.0, 100.0);
270     expect(Ok, status);
271     /* Negative sweep angle */
272     status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 80.0, -100.0);
273     expect(Ok, status);
274     /* More than a full revolution */
275     status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 50.0, -400.0);
276     expect(Ok, status);
277     /* 0 sweep angle */
278     status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 50.0, 0.0);
279     expect(Ok, status);
280 
281     ok_path(path, arc_path, sizeof(arc_path)/sizeof(path_test_t), FALSE);
282 
283     GdipDeletePath(path);
284 }
285 
286 static void test_worldbounds(void)
287 {
288     GpStatus status;
289     GpPath *path;
290     GpPen *pen;
291     GpMatrix *matrix;
292     GpRectF bounds;
293     GpPointF line2_points[10];
294     int i;
295 
296     for(i = 0; i < 10; i ++){
297         line2_points[i].X = 200.0 + i * 50.0 * (i % 2);
298         line2_points[i].Y = 200.0 + i * 50.0 * !(i % 2);
299     }
300     GdipCreatePen1((ARGB)0xdeadbeef, 20.0, UnitWorld, &pen);
301     GdipSetPenEndCap(pen, LineCapSquareAnchor);
302     GdipCreateMatrix2(1.5, 0.0, 1.0, 1.2, 10.4, 10.2, &matrix);
303 
304     GdipCreatePath(FillModeAlternate, &path);
305     GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
306     GdipAddPathLine2(path, &(line2_points[0]), 10);
307     status = GdipGetPathWorldBounds(path, &bounds, NULL, NULL);
308     expect(Ok, status);
309     GdipDeletePath(path);
310 
311     expectf(200.0, bounds.X);
312     expectf(200.0, bounds.Y);
313     expectf(450.0, bounds.Width);
314     expectf(600.0, bounds.Height);
315 
316     GdipCreatePath(FillModeAlternate, &path);
317     GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
318     GdipAddPathLine2(path, &(line2_points[0]), 10);
319     status = GdipGetPathWorldBounds(path, &bounds, matrix, NULL);
320     expect(Ok, status);
321     GdipDeletePath(path);
322 
323     expectf(510.4, bounds.X);
324     expectf(250.2, bounds.Y);
325     expectf(1275.0, bounds.Width);
326     expectf(720.0, bounds.Height);
327 
328     GdipCreatePath(FillModeAlternate, &path);
329     GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
330     GdipAddPathLine2(path, &(line2_points[0]), 10);
331     status = GdipGetPathWorldBounds(path, &bounds, NULL, pen);
332     expect(Ok, status);
333     GdipDeletePath(path);
334 
335     expectf(100.0, bounds.X);
336     expectf(100.0, bounds.Y);
337     expectf(650.0, bounds.Width);
338     expectf(800.0, bounds.Height);
339 
340     GdipCreatePath(FillModeAlternate, &path);
341     GdipAddPathLine2(path, &(line2_points[0]), 2);
342     status = GdipGetPathWorldBounds(path, &bounds, NULL, pen);
343     expect(Ok, status);
344     GdipDeletePath(path);
345 
346     expectf(156.0, bounds.X);
347     expectf(156.0, bounds.Y);
348     expectf(138.0, bounds.Width);
349     expectf(88.0, bounds.Height);
350 
351     line2_points[2].X = 2 * line2_points[1].X - line2_points[0].X;
352     line2_points[2].Y = 2 * line2_points[1].Y - line2_points[0].Y;
353 
354     GdipCreatePath(FillModeAlternate, &path);
355     GdipAddPathLine2(path, &(line2_points[0]), 3);
356     status = GdipGetPathWorldBounds(path, &bounds, NULL, pen);
357     expect(Ok, status);
358     GdipDeletePath(path);
359 
360     expectf(100.0, bounds.X);
361     expectf(100.0, bounds.Y);
362     expectf(300.0, bounds.Width);
363     expectf(200.0, bounds.Height);
364 
365     GdipCreatePath(FillModeAlternate, &path);
366     GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 45.0, 20.0);
367     status = GdipGetPathWorldBounds(path, &bounds, NULL, pen);
368     expect(Ok, status);
369     GdipDeletePath(path);
370 
371     expectf(386.7, bounds.X);
372     expectf(553.4, bounds.Y);
373     expectf(266.8, bounds.Width);
374     expectf(289.6, bounds.Height);
375 
376     GdipCreatePath(FillModeAlternate, &path);
377     status = GdipGetPathWorldBounds(path, &bounds, matrix, pen);
378     expect(Ok, status);
379     GdipDeletePath(path);
380 
381     expectf(0.0, bounds.X);
382     expectf(0.0, bounds.Y);
383     expectf(0.0, bounds.Width);
384     expectf(0.0, bounds.Height);
385 
386     GdipCreatePath(FillModeAlternate, &path);
387     GdipAddPathLine2(path, &(line2_points[0]), 2);
388     status = GdipGetPathWorldBounds(path, &bounds, matrix, pen);
389     expect(Ok, status);
390     GdipDeletePath(path);
391 
392     todo_wine{
393         expectf(427.9, bounds.X);
394         expectf(167.7, bounds.Y);
395         expectf(239.9, bounds.Width);
396         expectf(164.9, bounds.Height);
397     }
398 
399     GdipDeleteMatrix(matrix);
400     GdipCreateMatrix2(0.9, -0.5, -0.5, -1.2, 10.4, 10.2, &matrix);
401     GdipCreatePath(FillModeAlternate, &path);
402     GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
403     GdipAddPathLine2(path, &(line2_points[0]), 10);
404     status = GdipGetPathWorldBounds(path, &bounds, matrix, NULL);
405     expect(Ok, status);
406     GdipDeletePath(path);
407     GdipDeleteMatrix(matrix);
408 
409     expectf(-209.6, bounds.X);
410     expectf(-1274.8, bounds.Y);
411     expectf(705.0, bounds.Width);
412     expectf(945.0, bounds.Height);
413 
414     GdipDeletePen(pen);
415 }
416 
417 static path_test_t pathpath_path[] = {
418     {600.00, 450.00, PathPointTypeStart, 0, 0}, /*0*/
419     {600.00, 643.30, PathPointTypeBezier, 0, 0}, /*1*/
420     {488.07, 800.00, PathPointTypeBezier, 0, 0}, /*2*/
421     {350.00, 800.00, PathPointTypeBezier, 0, 0}, /*3*/
422     {319.61, 797.40, PathPointTypeStart, 0, 0}, /*4*/
423     {182.56, 773.90, PathPointTypeBezier, 0, 0}, /*5*/
424     {85.07, 599.31, PathPointTypeBezier, 0, 0}, /*6*/
425     {101.85, 407.45, PathPointTypeBezier, 0, 0}, /*7*/
426     {102.54, 399.66, PathPointTypeBezier, 0, 0}, /*8*/
427     {103.40, 391.91, PathPointTypeBezier, 0, 0}, /*9*/
428     {104.46, 384.21, PathPointTypeBezier, 0, 0}, /*10*/
429     {409.92, 110.20, PathPointTypeLine, 0, 0}, /*11*/
430     {543.96, 156.53, PathPointTypeBezier, 0, 0}, /*12*/
431     {625.80, 346.22, PathPointTypeBezier, 0, 0}, /*13*/
432     {592.71, 533.88, PathPointTypeBezier, 0, 0}, /*14*/
433     {592.47, 535.28, PathPointTypeBezier, 0, 0}, /*15*/
434     {592.22, 536.67, PathPointTypeBezier, 0, 0}, /*16*/
435     {591.96, 538.06, PathPointTypeBezier, 0, 0}, /*17*/
436     {319.61, 797.40, PathPointTypeLine, 0, 0}, /*18*/
437     {182.56, 773.90, PathPointTypeBezier, 0, 0}, /*19*/
438     {85.07, 599.31, PathPointTypeBezier, 0, 0}, /*20*/
439     {101.85, 407.45, PathPointTypeBezier, 0, 0}, /*21*/
440     {102.54, 399.66, PathPointTypeBezier, 0, 0}, /*22*/
441     {103.40, 391.91, PathPointTypeBezier, 0, 0}, /*23*/
442     {104.46, 384.21, PathPointTypeBezier, 0, 0} /*24*/
443     };
444 
445 static void test_pathpath(void)
446 {
447     GpStatus status;
448     GpPath* path1, *path2;
449 
450     GdipCreatePath(FillModeAlternate, &path2);
451     GdipAddPathArc(path2, 100.0, 100.0, 500.0, 700.0, 95.0, 100.0);
452 
453     GdipCreatePath(FillModeAlternate, &path1);
454     GdipAddPathArc(path1, 100.0, 100.0, 500.0, 700.0, 0.0, 90.0);
455     status = GdipAddPathPath(path1, path2, FALSE);
456     expect(Ok, status);
457     GdipAddPathArc(path1, 100.0, 100.0, 500.0, 700.0, -80.0, 100.0);
458     status = GdipAddPathPath(path1, path2, TRUE);
459     expect(Ok, status);
460 
461     ok_path(path1, pathpath_path, sizeof(pathpath_path)/sizeof(path_test_t), FALSE);
462 
463     GdipDeletePath(path1);
464     GdipDeletePath(path2);
465 }
466 
467 static path_test_t ellipse_path[] = {
468     {30.00, 125.25, PathPointTypeStart, 0, 0}, /*0*/
469     {30.00, 139.20, PathPointTypeBezier, 0, 0}, /*1*/
470     {25.52, 150.50, PathPointTypeBezier, 0, 0}, /*2*/
471     {20.00, 150.50, PathPointTypeBezier, 0, 0}, /*3*/
472     {14.48, 150.50, PathPointTypeBezier, 0, 0}, /*4*/
473     {10.00, 139.20, PathPointTypeBezier, 0, 0}, /*5*/
474     {10.00, 125.25, PathPointTypeBezier, 0, 0}, /*6*/
475     {10.00, 111.30, PathPointTypeBezier, 0, 0}, /*7*/
476     {14.48, 100.00, PathPointTypeBezier, 0, 0}, /*8*/
477     {20.00, 100.00, PathPointTypeBezier, 0, 0}, /*9*/
478     {25.52, 100.00, PathPointTypeBezier, 0, 0}, /*10*/
479     {30.00, 111.30, PathPointTypeBezier, 0, 0}, /*11*/
480     {30.00, 125.25, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 0}, /*12*/
481     {7.00, 11.00, PathPointTypeStart, 0, 0}, /*13*/
482     {13.00, 17.00, PathPointTypeLine, 0, 0}, /*14*/
483     {5.00, 195.00, PathPointTypeStart, 0, 0}, /*15*/
484     {5.00, 192.24, PathPointTypeBezier, 0, 0}, /*16*/
485     {6.12, 190.00, PathPointTypeBezier, 0, 0}, /*17*/
486     {7.50, 190.00, PathPointTypeBezier, 0, 0}, /*18*/
487     {8.88, 190.00, PathPointTypeBezier, 0, 0}, /*19*/
488     {10.00, 192.24, PathPointTypeBezier, 0, 0}, /*20*/
489     {10.00, 195.00, PathPointTypeBezier, 0, 0}, /*21*/
490     {10.00, 197.76, PathPointTypeBezier, 0, 0}, /*22*/
491     {8.88, 200.00, PathPointTypeBezier, 0, 0}, /*23*/
492     {7.50, 200.00, PathPointTypeBezier, 0, 0}, /*24*/
493     {6.12, 200.00, PathPointTypeBezier, 0, 0}, /*25*/
494     {5.00, 197.76, PathPointTypeBezier, 0, 0}, /*26*/
495     {5.00, 195.00, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 0}, /*27*/
496     {10.00, 300.50, PathPointTypeStart, 0, 0}, /*28*/
497     {10.00, 300.78, PathPointTypeBezier, 0, 0}, /*29*/
498     {10.00, 301.00, PathPointTypeBezier, 0, 0}, /*30*/
499     {10.00, 301.00, PathPointTypeBezier, 0, 0}, /*31*/
500     {10.00, 301.00, PathPointTypeBezier, 0, 0}, /*32*/
501     {10.00, 300.78, PathPointTypeBezier, 0, 0}, /*33*/
502     {10.00, 300.50, PathPointTypeBezier, 0, 0}, /*34*/
503     {10.00, 300.22, PathPointTypeBezier, 0, 0}, /*35*/
504     {10.00, 300.00, PathPointTypeBezier, 0, 0}, /*36*/
505     {10.00, 300.00, PathPointTypeBezier, 0, 0}, /*37*/
506     {10.00, 300.00, PathPointTypeBezier, 0, 0}, /*38*/
507     {10.00, 300.22, PathPointTypeBezier, 0, 0}, /*39*/
508     {10.00, 300.50, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 0} /*40*/
509     };
510 
511 static void test_ellipse(void)
512 {
513     GpStatus status;
514     GpPath *path;
515     GpPointF points[2];
516 
517     points[0].X = 7.0;
518     points[0].Y = 11.0;
519     points[1].X = 13.0;
520     points[1].Y = 17.0;
521 
522     GdipCreatePath(FillModeAlternate, &path);
523     status = GdipAddPathEllipse(path, 10.0, 100.0, 20.0, 50.5);
524     expect(Ok, status);
525     GdipAddPathLine2(path, points, 2);
526     status = GdipAddPathEllipse(path, 10.0, 200.0, -5.0, -10.0);
527     expect(Ok, status);
528     GdipClosePathFigure(path);
529     status = GdipAddPathEllipse(path, 10.0, 300.0, 0.0, 1.0);
530     expect(Ok, status);
531 
532     ok_path(path, ellipse_path, sizeof(ellipse_path)/sizeof(path_test_t), FALSE);
533 
534     GdipDeletePath(path);
535 }
536 
537 static path_test_t linei_path[] = {
538     {5.00, 5.00, PathPointTypeStart, 0, 0}, /*0*/
539     {6.00, 8.00, PathPointTypeLine, 0, 0}, /*1*/
540     {409.92, 110.20, PathPointTypeLine, 0, 0}, /*2*/
541     {543.96, 156.53, PathPointTypeBezier, 0, 0}, /*3*/
542     {625.80, 346.22, PathPointTypeBezier, 0, 0}, /*4*/
543     {592.71, 533.88, PathPointTypeBezier, 0, 0}, /*5*/
544     {592.47, 535.28, PathPointTypeBezier, 0, 0}, /*6*/
545     {592.22, 536.67, PathPointTypeBezier, 0, 0}, /*7*/
546     {591.96, 538.06, PathPointTypeBezier, 0, 0}, /*8*/
547     {15.00, 15.00, PathPointTypeLine, 0, 0}, /*9*/
548     {26.00, 28.00, PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*10*/
549     {35.00, 35.00, PathPointTypeStart, 0, 0}, /*11*/
550     {36.00, 38.00, PathPointTypeLine, 0, 0} /*12*/
551     };
552 
553 static void test_linei(void)
554 {
555     GpStatus status;
556     GpPath *path;
557 
558     GdipCreatePath(FillModeAlternate, &path);
559     status = GdipAddPathLineI(path, 5.0, 5.0, 6.0, 8.0);
560     expect(Ok, status);
561     GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, -80.0, 100.0);
562     status = GdipAddPathLineI(path, 15.0, 15.0, 26.0, 28.0);
563     expect(Ok, status);
564     GdipClosePathFigure(path);
565     status = GdipAddPathLineI(path, 35.0, 35.0, 36.0, 38.0);
566     expect(Ok, status);
567 
568     ok_path(path, linei_path, sizeof(linei_path)/sizeof(path_test_t), FALSE);
569 
570     GdipDeletePath(path);
571 }
572 
573 static path_test_t poly_path[] = {
574     {5.00, 5.00, PathPointTypeStart, 0, 0},   /*1*/
575     {6.00, 8.00, PathPointTypeLine, 0, 0},    /*2*/
576     {0.00,  0.00,  PathPointTypeStart, 0, 0}, /*3*/
577     {10.00, 10.00, PathPointTypeLine, 0, 0},  /*4*/
578     {10.00, 20.00, PathPointTypeLine, 0, 0},  /*5*/
579     {30.00, 10.00, PathPointTypeLine, 0, 0},  /*6*/
580     {20.00, 0.00, PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*7*/
581     };
582 
583 static void test_polygon(void)
584 {
585     GpStatus status;
586     GpPath *path;
587     GpPointF points[5];
588 
589     points[0].X = 0.0;
590     points[0].Y = 0.0;
591     points[1].X = 10.0;
592     points[1].Y = 10.0;
593     points[2].X = 10.0;
594     points[2].Y = 20.0;
595     points[3].X = 30.0;
596     points[3].Y = 10.0;
597     points[4].X = 20.0;
598     points[4].Y = 0.0;
599 
600     GdipCreatePath(FillModeAlternate, &path);
601 
602     /* NULL args */
603     status = GdipAddPathPolygon(NULL, points, 5);
604     expect(InvalidParameter, status);
605     status = GdipAddPathPolygon(path, NULL, 5);
606     expect(InvalidParameter, status);
607     /* Polygon should have 3 points at least */
608     status = GdipAddPathPolygon(path, points, 2);
609     expect(InvalidParameter, status);
610 
611     /* to test how it prolongs not empty path */
612     status = GdipAddPathLine(path, 5.0, 5.0, 6.0, 8.0);
613     expect(Ok, status);
614     status = GdipAddPathPolygon(path, points, 5);
615     expect(Ok, status);
616     /* check resulting path */
617     ok_path(path, poly_path, sizeof(poly_path)/sizeof(path_test_t), FALSE);
618 
619     GdipDeletePath(path);
620 }
621 
622 static path_test_t rect_path[] = {
623     {5.0, 5.0,       PathPointTypeStart, 0, 0}, /*0*/
624     {105.0, 5.0,     PathPointTypeLine,  0, 0}, /*1*/
625     {105.0, 55.0,    PathPointTypeLine,  0, 0}, /*2*/
626     {5.0, 55.0,      PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*3*/
627 
628     {100.0, 50.0,    PathPointTypeStart, 0, 0}, /*4*/
629     {220.0, 50.0,    PathPointTypeLine,  0, 0}, /*5*/
630     {220.0, 80.0,    PathPointTypeLine,  0, 0}, /*6*/
631     {100.0, 80.0,    PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}  /*7*/
632     };
633 
634 static void test_rect(void)
635 {
636     GpStatus status;
637     GpPath *path;
638     GpRectF rects[2];
639 
640     GdipCreatePath(FillModeAlternate, &path);
641     status = GdipAddPathRectangle(path, 5.0, 5.0, 100.0, 50.0);
642     expect(Ok, status);
643     status = GdipAddPathRectangle(path, 100.0, 50.0, 120.0, 30.0);
644     expect(Ok, status);
645 
646     ok_path(path, rect_path, sizeof(rect_path)/sizeof(path_test_t), FALSE);
647 
648     GdipDeletePath(path);
649 
650     GdipCreatePath(FillModeAlternate, &path);
651 
652     rects[0].X      = 5.0;
653     rects[0].Y      = 5.0;
654     rects[0].Width  = 100.0;
655     rects[0].Height = 50.0;
656     rects[1].X      = 100.0;
657     rects[1].Y      = 50.0;
658     rects[1].Width  = 120.0;
659     rects[1].Height = 30.0;
660 
661     status = GdipAddPathRectangles(path, (GDIPCONST GpRectF*)&rects, 2);
662     expect(Ok, status);
663 
664     ok_path(path, rect_path, sizeof(rect_path)/sizeof(path_test_t), FALSE);
665 
666     GdipDeletePath(path);
667 }
668 
669 static void test_lastpoint(void)
670 {
671     GpStatus status;
672     GpPath *path;
673     GpPointF ptf;
674 
675     GdipCreatePath(FillModeAlternate, &path);
676     status = GdipAddPathRectangle(path, 5.0, 5.0, 100.0, 50.0);
677     expect(Ok, status);
678 
679     /* invalid args */
680     status = GdipGetPathLastPoint(NULL, &ptf);
681     expect(InvalidParameter, status);
682     status = GdipGetPathLastPoint(path, NULL);
683     expect(InvalidParameter, status);
684     status = GdipGetPathLastPoint(NULL, NULL);
685     expect(InvalidParameter, status);
686 
687     status = GdipGetPathLastPoint(path, &ptf);
688     expect(Ok, status);
689     expect(TRUE, (ptf.X == 5.0) && (ptf.Y == 55.0));
690 
691     GdipDeletePath(path);
692 }
693 
694 static path_test_t addcurve_path[] = {
695     {0.0, 0.0,   PathPointTypeStart,  0, 0}, /*0*/
696     {3.3, 3.3,   PathPointTypeBezier, 0, 0}, /*1*/
697     {6.7, 3.3,   PathPointTypeBezier, 0, 0}, /*2*/
698     {10.0, 10.0, PathPointTypeBezier, 0, 0}, /*3*/
699     {13.3, 16.7, PathPointTypeBezier, 0, 0}, /*4*/
700     {3.3,  20.0, PathPointTypeBezier, 0, 0}, /*5*/
701     {10.0, 20.0, PathPointTypeBezier, 0, 0}, /*6*/
702     {16.7, 20.0, PathPointTypeBezier, 0, 0}, /*7*/
703     {23.3, 13.3, PathPointTypeBezier, 0, 0}, /*8*/
704     {30.0, 10.0, PathPointTypeBezier, 0, 0}  /*9*/
705     };
706 static path_test_t addcurve_path2[] = {
707     {100.0,120.0,PathPointTypeStart,  0, 0}, /*0*/
708     {123.0,10.0, PathPointTypeLine,   0, 0}, /*1*/
709     {0.0, 0.0,   PathPointTypeLine,   0, 0}, /*2*/
710     {3.3, 3.3,   PathPointTypeBezier, 0, 0}, /*3*/
711     {6.7, 3.3,   PathPointTypeBezier, 0, 0}, /*4*/
712     {10.0, 10.0, PathPointTypeBezier, 0, 0}, /*5*/
713     {13.3, 16.7, PathPointTypeBezier, 0, 0}, /*6*/
714     {3.3,  20.0, PathPointTypeBezier, 0, 0}, /*7*/
715     {10.0, 20.0, PathPointTypeBezier, 0, 0}, /*8*/
716     {16.7, 20.0, PathPointTypeBezier, 0, 0}, /*9*/
717     {23.3, 13.3, PathPointTypeBezier, 0, 0}, /*10*/
718     {30.0, 10.0, PathPointTypeBezier, 0, 0}  /*11*/
719     };
720 static path_test_t addcurve_path3[] = {
721     {10.0, 10.0, PathPointTypeStart,  0, 0}, /*0*/
722     {13.3, 16.7, PathPointTypeBezier, 0, 1}, /*1*/
723     {3.3,  20.0, PathPointTypeBezier, 0, 0}, /*2*/
724     {10.0, 20.0, PathPointTypeBezier, 0, 0}, /*3*/
725     {16.7, 20.0, PathPointTypeBezier, 0, 0}, /*4*/
726     {23.3, 13.3, PathPointTypeBezier, 0, 0}, /*5*/
727     {30.0, 10.0, PathPointTypeBezier, 0, 0}  /*6*/
728     };
729 static void test_addcurve(void)
730 {
731     GpStatus status;
732     GpPath *path;
733     GpPointF points[4];
734 
735     points[0].X = 0.0;
736     points[0].Y = 0.0;
737     points[1].X = 10.0;
738     points[1].Y = 10.0;
739     points[2].X = 10.0;
740     points[2].Y = 20.0;
741     points[3].X = 30.0;
742     points[3].Y = 10.0;
743 
744     GdipCreatePath(FillModeAlternate, &path);
745 
746     /* NULL args */
747     status = GdipAddPathCurve2(NULL, NULL, 0, 0.0);
748     expect(InvalidParameter, status);
749     status = GdipAddPathCurve2(path, NULL, 0, 0.0);
750     expect(InvalidParameter, status);
751     status = GdipAddPathCurve2(path, points, -1, 0.0);
752     expect(InvalidParameter, status);
753     status = GdipAddPathCurve2(path, points, 1, 1.0);
754     expect(InvalidParameter, status);
755 
756     /* add to empty path */
757     status = GdipAddPathCurve2(path, points, 4, 1.0);
758     expect(Ok, status);
759     ok_path(path, addcurve_path, sizeof(addcurve_path)/sizeof(path_test_t), FALSE);
760     GdipDeletePath(path);
761 
762     /* add to notempty path and opened figure */
763     GdipCreatePath(FillModeAlternate, &path);
764     GdipAddPathLine(path, 100.0, 120.0, 123.0, 10.0);
765     status = GdipAddPathCurve2(path, points, 4, 1.0);
766     expect(Ok, status);
767     ok_path(path, addcurve_path2, sizeof(addcurve_path2)/sizeof(path_test_t), FALSE);
768 
769     /* NULL args */
770     GdipResetPath(path);
771     status = GdipAddPathCurve3(NULL, NULL, 0, 0, 0, 0.0);
772     expect(InvalidParameter, status);
773     status = GdipAddPathCurve3(path, NULL, 0, 0, 0, 0.0);
774     expect(InvalidParameter, status);
775     /* wrong count, offset.. */
776     status = GdipAddPathCurve3(path, points, 0, 0, 0, 0.0);
777     expect(InvalidParameter, status);
778     status = GdipAddPathCurve3(path, points, 4, 0, 0, 0.0);
779     expect(InvalidParameter, status);
780     status = GdipAddPathCurve3(path, points, 4, 0, 4, 0.0);
781     expect(InvalidParameter, status);
782     status = GdipAddPathCurve3(path, points, 4, 1, 3, 0.0);
783     expect(InvalidParameter, status);
784     status = GdipAddPathCurve3(path, points, 4, 1, 0, 0.0);
785     expect(InvalidParameter, status);
786     status = GdipAddPathCurve3(path, points, 4, 3, 1, 0.0);
787     expect(InvalidParameter, status);
788 
789     /* use all points */
790     status = GdipAddPathCurve3(path, points, 4, 0, 3, 1.0);
791     expect(Ok, status);
792     ok_path(path, addcurve_path, sizeof(addcurve_path)/sizeof(path_test_t), FALSE);
793     GdipResetPath(path);
794 
795     status = GdipAddPathCurve3(path, points, 4, 1, 2, 1.0);
796     expect(Ok, status);
797     ok_path(path, addcurve_path3, sizeof(addcurve_path3)/sizeof(path_test_t), FALSE);
798 
799     GdipDeletePath(path);
800 }
801 
802 static path_test_t addclosedcurve_path[] = {
803     {0.0, 0.0,   PathPointTypeStart,  0, 0}, /*0*/
804     {-6.7, 0.0,  PathPointTypeBezier, 0, 0}, /*1*/
805     {6.7, 3.3,   PathPointTypeBezier, 0, 0}, /*2*/
806     {10.0, 10.0, PathPointTypeBezier, 0, 0}, /*3*/
807     {13.3, 16.7, PathPointTypeBezier, 0, 0}, /*4*/
808     {3.3,  20.0, PathPointTypeBezier, 0, 0}, /*5*/
809     {10.0, 20.0, PathPointTypeBezier, 0, 0}, /*6*/
810     {16.7, 20.0, PathPointTypeBezier, 0, 0}, /*7*/
811     {33.3, 16.7, PathPointTypeBezier, 0, 0}, /*8*/
812     {30.0, 10.0, PathPointTypeBezier, 0, 0}, /*9*/
813     {26.7, 3.3,  PathPointTypeBezier, 0, 0}, /*10*/
814     {6.7,  0.0,  PathPointTypeBezier, 0, 0}, /*11*/
815     {0.0,  0.0,  PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 0}  /*12*/
816     };
817 static void test_addclosedcurve(void)
818 {
819     GpStatus status;
820     GpPath *path;
821     GpPointF points[4];
822 
823     points[0].X = 0.0;
824     points[0].Y = 0.0;
825     points[1].X = 10.0;
826     points[1].Y = 10.0;
827     points[2].X = 10.0;
828     points[2].Y = 20.0;
829     points[3].X = 30.0;
830     points[3].Y = 10.0;
831 
832     GdipCreatePath(FillModeAlternate, &path);
833 
834     /* NULL args */
835     status = GdipAddPathClosedCurve2(NULL, NULL, 0, 0.0);
836     expect(InvalidParameter, status);
837     status = GdipAddPathClosedCurve2(path, NULL, 0, 0.0);
838     expect(InvalidParameter, status);
839     status = GdipAddPathClosedCurve2(path, points, -1, 0.0);
840     expect(InvalidParameter, status);
841     status = GdipAddPathClosedCurve2(path, points, 1, 1.0);
842     expect(InvalidParameter, status);
843 
844     /* add to empty path */
845     status = GdipAddPathClosedCurve2(path, points, 4, 1.0);
846     expect(Ok, status);
847     ok_path(path, addclosedcurve_path, sizeof(addclosedcurve_path)/sizeof(path_test_t), FALSE);
848     GdipDeletePath(path);
849 }
850 
851 static path_test_t reverse_path[] = {
852     {0.0,  20.0, PathPointTypeStart, 0, 0}, /*0*/
853     {25.0, 25.0, PathPointTypeLine,  0, 0}, /*1*/
854     {0.0,  30.0, PathPointTypeLine,  0, 0}, /*2*/
855     {15.0, 35.0, PathPointTypeStart, 0, 0}, /*3*/
856     {0.0,  40.0, PathPointTypeLine,  0, 0}, /*4*/
857     {5.0,  45.0, PathPointTypeLine,  0, 0}, /*5*/
858     {0.0,  50.0, PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}  /*6*/
859     };
860 
861 static void test_reverse(void)
862 {
863     GpStatus status;
864     GpPath *path;
865     GpPointF pts[7];
866     INT i;
867 
868     for(i = 0; i < 7; i++){
869         pts[i].X = i * 5.0 * (REAL)(i % 2);
870         pts[i].Y = 50.0 - i * 5.0;
871     }
872 
873     GdipCreatePath(FillModeAlternate, &path);
874 
875     /* NULL argument */
876     status = GdipReversePath(NULL);
877     expect(InvalidParameter, status);
878 
879     /* empty path */
880     status = GdipReversePath(path);
881     expect(Ok, status);
882 
883     GdipAddPathLine2(path, pts, 4);
884     GdipClosePathFigure(path);
885     GdipAddPathLine2(path, &(pts[4]), 3);
886 
887     status = GdipReversePath(path);
888     expect(Ok, status);
889     ok_path(path, reverse_path, sizeof(reverse_path)/sizeof(path_test_t), FALSE);
890 
891     GdipDeletePath(path);
892 }
893 
894 static path_test_t addpie_path[] = {
895     {50.0, 25.0, PathPointTypeStart, 0, 0}, /*0*/
896     {97.2, 33.3, PathPointTypeLine,  0, 0}, /*1*/
897     {91.8, 40.9, PathPointTypeBezier,0, 0}, /*2*/
898     {79.4, 46.8, PathPointTypeBezier,0, 0}, /*3*/
899     {63.9, 49.0, PathPointTypeBezier | PathPointTypeCloseSubpath,  0, 0} /*4*/
900     };
901 static path_test_t addpie_path2[] = {
902     {0.0, 30.0, PathPointTypeStart | PathPointTypeCloseSubpath, 0, 0} /*0*/
903     };
904 static path_test_t addpie_path3[] = {
905     {30.0, 0.0, PathPointTypeStart | PathPointTypeCloseSubpath, 0, 0} /*0*/
906     };
907 static void test_addpie(void)
908 {
909     GpStatus status;
910     GpPath *path;
911 
912     GdipCreatePath(FillModeAlternate, &path);
913 
914     /* NULL argument */
915     status = GdipAddPathPie(NULL, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
916     expect(InvalidParameter, status);
917 
918     status = GdipAddPathPie(path, 0.0, 0.0, 100.0, 50.0, 10.0, 50.0);
919     expect(Ok, status);
920     ok_path(path, addpie_path, sizeof(addpie_path)/sizeof(path_test_t), FALSE);
921     status = GdipResetPath(path);
922     expect(Ok, status);
923 
924     /* zero width base ellipse */
925     status = GdipAddPathPie(path, 0.0, 0.0, 0.0, 60.0, -90.0, 24.0);
926     expect(InvalidParameter, status);
927     ok_path(path, addpie_path2, sizeof(addpie_path2)/sizeof(path_test_t), FALSE);
928     status = GdipResetPath(path);
929     expect(Ok, status);
930 
931     /* zero height base ellipse */
932     status = GdipAddPathPie(path, 0.0, 0.0, 60.0, 0.0 , -90.0, 24.0);
933     expect(InvalidParameter, status);
934     ok_path(path, addpie_path3, sizeof(addpie_path3)/sizeof(path_test_t), FALSE);
935 
936     GdipDeletePath(path);
937 }
938 
939 static path_test_t flattenellipse_path[] = {
940     {100.0, 25.0,PathPointTypeStart, 0, 0}, /*0*/
941     {99.0, 30.0, PathPointTypeLine,  0, 0}, /*1*/
942     {96.0, 34.8, PathPointTypeLine,  0, 0}, /*2*/
943     {91.5, 39.0, PathPointTypeLine,  0, 0}, /*3*/
944     {85.5, 42.8, PathPointTypeLine,  0, 0}, /*4*/
945     {69.5, 48.0, PathPointTypeLine,  0, 1}, /*5*/
946     {50.0, 50.0, PathPointTypeLine,  0, 1}, /*6*/
947     {30.5, 48.0, PathPointTypeLine,  0, 1}, /*7*/
948     {14.8, 42.8, PathPointTypeLine,  0, 1}, /*8*/
949     {8.5,  39.0, PathPointTypeLine,  0, 1}, /*9*/
950     {4.0,  34.8, PathPointTypeLine,  0, 1}, /*10*/
951     {1.0,  30.0, PathPointTypeLine,  0, 1}, /*11*/
952     {0.0,  25.0, PathPointTypeLine,  0, 1}, /*12*/
953     {1.0,  20.0, PathPointTypeLine,  0, 1}, /*13*/
954     {4.0,  15.3, PathPointTypeLine,  0, 1}, /*14*/
955     {8.5,  11.0, PathPointTypeLine,  0, 1}, /*15*/
956     {14.8, 7.3,  PathPointTypeLine,  0, 1}, /*16*/
957     {30.5, 2.0,  PathPointTypeLine,  0, 1}, /*17*/
958     {50.0, 0.0,  PathPointTypeLine,  0, 1}, /*18*/
959     {69.5, 2.0,  PathPointTypeLine,  0, 1}, /*19*/
960     {85.5, 7.3,  PathPointTypeLine,  0, 1}, /*20*/
961     {91.5, 11.0, PathPointTypeLine,  0, 1}, /*21*/
962     {96.0, 15.3, PathPointTypeLine,  0, 1}, /*22*/
963     {99.0, 20.0, PathPointTypeLine,  0, 1}, /*23*/
964     {100.0,25.0, PathPointTypeLine | PathPointTypeCloseSubpath,  0, 1}  /*24*/
965     };
966 
967 static path_test_t flattenline_path[] = {
968     {5.0, 10.0,PathPointTypeStart, 0, 0}, /*0*/
969     {50.0, 100.0, PathPointTypeLine,  0, 0} /*1*/
970     };
971 
972 static path_test_t flattenarc_path[] = {
973     {100.0, 25.0,PathPointTypeStart, 0, 0}, /*0*/
974     {99.0, 30.0, PathPointTypeLine,  0, 0}, /*1*/
975     {96.0, 34.8, PathPointTypeLine,  0, 0}, /*2*/
976     {91.5, 39.0, PathPointTypeLine,  0, 0}, /*3*/
977     {85.5, 42.8, PathPointTypeLine,  0, 0}, /*4*/
978     {69.5, 48.0, PathPointTypeLine,  0, 1}, /*5*/
979     {50.0, 50.0, PathPointTypeLine,  0, 1}  /*6*/
980     };
981 
982 static path_test_t flattenquater_path[] = {
983     {100.0, 50.0,PathPointTypeStart, 0, 0}, /*0*/
984     {99.0, 60.0, PathPointTypeLine,  0, 0}, /*1*/
985     {96.0, 69.5, PathPointTypeLine,  0, 0}, /*2*/
986     {91.5, 78.0, PathPointTypeLine,  0, 0}, /*3*/
987     {85.5, 85.5, PathPointTypeLine,  0, 0}, /*4*/
988     {78.0, 91.5, PathPointTypeLine,  0, 0}, /*5*/
989     {69.5, 96.0, PathPointTypeLine,  0, 0}, /*6*/
990     {60.0, 99.0, PathPointTypeLine,  0, 0}, /*7*/
991     {50.0, 100.0,PathPointTypeLine,  0, 0}  /*8*/
992     };
993 
994 static void test_flatten(void)
995 {
996     GpStatus status;
997     GpPath *path;
998     GpMatrix *m;
999 
1000     status = GdipCreatePath(FillModeAlternate, &path);
1001     expect(Ok, status);
1002     status = GdipCreateMatrix(&m);
1003     expect(Ok, status);
1004 
1005     /* NULL arguments */
1006     status = GdipFlattenPath(NULL, NULL, 0.0);
1007     expect(InvalidParameter, status);
1008     status = GdipFlattenPath(NULL, m, 0.0);
1009     expect(InvalidParameter, status);
1010 
1011     /* flatten empty path */
1012     status = GdipFlattenPath(path, NULL, 1.0);
1013     expect(Ok, status);
1014 
1015     status = GdipTransformPath(path, 0);
1016     expect(Ok, status);
1017 
1018     status = GdipAddPathEllipse(path, 0.0, 0.0, 100.0, 50.0);
1019     expect(Ok, status);
1020 
1021     status = GdipFlattenPath(path, NULL, 1.0);
1022     expect(Ok, status);
1023     ok_path(path, flattenellipse_path, sizeof(flattenellipse_path)/sizeof(path_test_t), TRUE);
1024 
1025     status = GdipResetPath(path);
1026     expect(Ok, status);
1027     status = GdipAddPathLine(path, 5.0, 10.0, 50.0, 100.0);
1028     expect(Ok, status);
1029     status = GdipFlattenPath(path, NULL, 1.0);
1030     expect(Ok, status);
1031     ok_path(path, flattenline_path, sizeof(flattenline_path)/sizeof(path_test_t), FALSE);
1032 
1033     status = GdipResetPath(path);
1034     expect(Ok, status);
1035     status = GdipAddPathArc(path, 0.0, 0.0, 100.0, 50.0, 0.0, 90.0);
1036     expect(Ok, status);
1037     status = GdipFlattenPath(path, NULL, 1.0);
1038     expect(Ok, status);
1039     ok_path(path, flattenarc_path, sizeof(flattenarc_path)/sizeof(path_test_t), TRUE);
1040 
1041     /* easy case - quater of a full circle */
1042     status = GdipResetPath(path);
1043     expect(Ok, status);
1044     status = GdipAddPathArc(path, 0.0, 0.0, 100.0, 100.0, 0.0, 90.0);
1045     expect(Ok, status);
1046     status = GdipFlattenPath(path, NULL, 1.0);
1047     expect(Ok, status);
1048     ok_path(path, flattenquater_path, sizeof(flattenquater_path)/sizeof(path_test_t), FALSE);
1049 
1050     GdipDeleteMatrix(m);
1051     GdipDeletePath(path);
1052 }
1053 
1054 static path_test_t widenline_path[] = {
1055     {5.0, 5.0,   PathPointTypeStart, 0, 0}, /*0*/
1056     {50.0, 5.0,  PathPointTypeLine,  0, 0}, /*1*/
1057     {50.0, 15.0, PathPointTypeLine,  0, 0}, /*2*/
1058     {5.0, 15.0,  PathPointTypeLine|PathPointTypeCloseSubpath,  0, 0} /*3*/
1059     };
1060 
1061 static path_test_t widenline_wide_path[] = {
1062     {5.0, 0.0,   PathPointTypeStart, 0, 0}, /*0*/
1063     {50.0, 0.0,  PathPointTypeLine,  0, 0}, /*1*/
1064     {50.0, 20.0, PathPointTypeLine,  0, 0}, /*2*/
1065     {5.0, 20.0,  PathPointTypeLine|PathPointTypeCloseSubpath,  0, 0} /*3*/
1066     };
1067 
1068 static path_test_t widenline_dash_path[] = {
1069     {5.0, 0.0,   PathPointTypeStart, 0, 0}, /*0*/
1070     {35.0, 0.0,  PathPointTypeLine,  0, 0}, /*1*/
1071     {35.0, 10.0, PathPointTypeLine,  0, 0}, /*2*/
1072     {5.0, 10.0,  PathPointTypeLine|PathPointTypeCloseSubpath,  0, 0}, /*3*/
1073     {45.0, 0.0,   PathPointTypeStart, 0, 0}, /*4*/
1074     {50.0, 0.0,  PathPointTypeLine,  0, 0}, /*5*/
1075     {50.0, 10.0, PathPointTypeLine,  0, 0}, /*6*/
1076     {45.0, 10.0,  PathPointTypeLine|PathPointTypeCloseSubpath,  0, 0}, /*7*/
1077     };
1078 
1079 static void test_widen(void)
1080 {
1081     GpStatus status;
1082     GpPath *path;
1083     GpPen *pen;
1084     GpMatrix *m;
1085     INT count=-1;
1086 
1087     status = GdipCreatePath(FillModeAlternate, &path);
1088     expect(Ok, status);
1089     status = GdipCreatePen1(0xffffffff, 10.0, UnitPixel, &pen);
1090     expect(Ok, status);
1091     status = GdipCreateMatrix(&m);
1092     expect(Ok, status);
1093 
1094     /* NULL arguments */
1095     status = GdipAddPathLine(path, 5.0, 10.0, 50.0, 10.0);
1096     expect(Ok, status);
1097     status = GdipWidenPath(NULL, NULL, NULL, 0.0);
1098     expect(InvalidParameter, status);
1099     status = GdipWidenPath(path, pen, m, 0.0);
1100     expect(Ok, status);
1101     status = GdipWidenPath(path, pen, NULL, 1.0);
1102     expect(Ok, status);
1103     status = GdipWidenPath(path, NULL, m, 1.0);
1104     expect(InvalidParameter, status);
1105     status = GdipWidenPath(NULL, pen, m, 1.0);
1106     expect(InvalidParameter, status);
1107 
1108     /* widen empty path */
1109     status = GdipResetPath(path);
1110     expect(Ok, status);
1111     status = GdipWidenPath(path, pen, m, 1.0);
1112     expect(OutOfMemory, status);
1113 
1114     /* horizontal line */
1115     status = GdipResetPath(path);
1116     expect(Ok, status);
1117     status = GdipAddPathLine(path, 5.0, 10.0, 50.0, 10.0);
1118     expect(Ok, status);
1119 
1120     status = GdipWidenPath(path, pen, m, 1.0);
1121     expect(Ok, status);
1122     ok_path(path, widenline_path, sizeof(widenline_path)/sizeof(path_test_t), FALSE);
1123 
1124     /* horizontal 2x stretch */
1125     status = GdipResetPath(path);
1126     expect(Ok, status);
1127     status = GdipAddPathLine(path, 2.5, 10.0, 25.0, 10.0);
1128     expect(Ok, status);
1129 
1130     status = GdipScaleMatrix(m, 2.0, 1.0, MatrixOrderAppend);
1131     expect(Ok, status);
1132 
1133     status = GdipWidenPath(path, pen, m, 1.0);
1134     expect(Ok, status);
1135     ok_path(path, widenline_path, sizeof(widenline_path)/sizeof(path_test_t), FALSE);
1136 
1137     /* vertical 2x stretch */
1138     status = GdipResetPath(path);
1139     expect(Ok, status);
1140     status = GdipAddPathLine(path, 5.0, 5.0, 50.0, 5.0);
1141     expect(Ok, status);
1142 
1143     status = GdipScaleMatrix(m, 0.5, 2.0, MatrixOrderAppend);
1144     expect(Ok, status);
1145 
1146     status = GdipWidenPath(path, pen, m, 1.0);
1147     expect(Ok, status);
1148     ok_path(path, widenline_path, sizeof(widenline_path)/sizeof(path_test_t), FALSE);
1149 
1150     status = GdipScaleMatrix(m, 1.0, 0.5, MatrixOrderAppend);
1151     expect(Ok, status);
1152 
1153     /* dashed line */
1154     status = GdipResetPath(path);
1155     expect(Ok, status);
1156     status = GdipAddPathLine(path, 5.0, 5.0, 50.0, 5.0);
1157     expect(Ok, status);
1158 
1159     status = GdipSetPenDashStyle(pen, DashStyleDash);
1160     expect(Ok, status);
1161 
1162     status = GdipWidenPath(path, pen, m, 1.0);
1163     expect(Ok, status);
1164     ok_path(path, widenline_dash_path, sizeof(widenline_dash_path)/sizeof(path_test_t), FALSE);
1165 
1166     status = GdipSetPenDashStyle(pen, DashStyleSolid);
1167     expect(Ok, status);
1168 
1169     /* pen width in UnitWorld */
1170     GdipDeletePen(pen);
1171     status = GdipCreatePen1(0xffffffff, 10.0, UnitWorld, &pen);
1172     expect(Ok, status);
1173 
1174     status = GdipResetPath(path);
1175     expect(Ok, status);
1176     status = GdipAddPathLine(path, 5.0, 10.0, 50.0, 10.0);
1177     expect(Ok, status);
1178 
1179     status = GdipWidenPath(path, pen, m, 1.0);
1180     expect(Ok, status);
1181     ok_path(path, widenline_path, sizeof(widenline_path)/sizeof(path_test_t), FALSE);
1182 
1183     /* horizontal 2x stretch */
1184     status = GdipResetPath(path);
1185     expect(Ok, status);
1186     status = GdipAddPathLine(path, 2.5, 10.0, 25.0, 10.0);
1187     expect(Ok, status);
1188 
1189     status = GdipScaleMatrix(m, 2.0, 1.0, MatrixOrderAppend);
1190     expect(Ok, status);
1191 
1192     status = GdipWidenPath(path, pen, m, 1.0);
1193     expect(Ok, status);
1194     ok_path(path, widenline_path, sizeof(widenline_path)/sizeof(path_test_t), FALSE);
1195 
1196     /* vertical 2x stretch */
1197     status = GdipResetPath(path);
1198     expect(Ok, status);
1199     status = GdipAddPathLine(path, 5.0, 5.0, 50.0, 5.0);
1200     expect(Ok, status);
1201 
1202     status = GdipScaleMatrix(m, 0.5, 2.0, MatrixOrderAppend);
1203     expect(Ok, status);
1204 
1205     status = GdipWidenPath(path, pen, m, 1.0);
1206     expect(Ok, status);
1207     ok_path(path, widenline_wide_path, sizeof(widenline_wide_path)/sizeof(path_test_t), FALSE);
1208 
1209     status = GdipScaleMatrix(m, 1.0, 0.5, MatrixOrderAppend);
1210     expect(Ok, status);
1211 
1212     /* pen width in UnitInch */
1213     GdipDeletePen(pen);
1214     status = GdipCreatePen1(0xffffffff, 10.0, UnitWorld, &pen);
1215     expect(Ok, status);
1216 
1217     status = GdipResetPath(path);
1218     expect(Ok, status);
1219     status = GdipAddPathLine(path, 5.0, 10.0, 50.0, 10.0);
1220     expect(Ok, status);
1221 
1222     status = GdipWidenPath(path, pen, m, 1.0);
1223     expect(Ok, status);
1224     ok_path(path, widenline_path, sizeof(widenline_path)/sizeof(path_test_t), FALSE);
1225 
1226     /* pen width = 0 pixels - native fails to widen but can draw with this pen */
1227     GdipDeletePen(pen);
1228     status = GdipCreatePen1(0xffffffff, 0.0, UnitPixel, &pen);
1229     expect(Ok, status);
1230 
1231     status = GdipResetPath(path);
1232     expect(Ok, status);
1233     status = GdipAddPathLine(path, 5.0, 10.0, 50.0, 10.0);
1234     expect(Ok, status);
1235 
1236     status = GdipWidenPath(path, pen, m, 1.0);
1237     expect(Ok, status);
1238 
1239     status = GdipGetPointCount(path, &count);
1240     expect(Ok, status);
1241     todo_wine expect(0, count);
1242 
1243     GdipDeleteMatrix(m);
1244     GdipDeletePen(pen);
1245     GdipDeletePath(path);
1246 }
1247 
1248 static void test_isvisible(void)
1249 {
1250     GpPath *path;
1251     GpGraphics *graphics = NULL;
1252     HDC hdc = GetDC(0);
1253     BOOL result;
1254     GpStatus status;
1255 
1256     status = GdipCreateFromHDC(hdc, &graphics);
1257     expect(Ok, status);
1258     status = GdipCreatePath(FillModeAlternate, &path);
1259     expect(Ok, status);
1260 
1261     /* NULL */
1262     status = GdipIsVisiblePathPoint(NULL, 0.0, 0.0, NULL, NULL);
1263     expect(InvalidParameter, status);
1264     status = GdipIsVisiblePathPoint(path, 0.0, 0.0, NULL, NULL);
1265     expect(InvalidParameter, status);
1266     status = GdipIsVisiblePathPoint(path, 0.0, 0.0, NULL, NULL);
1267     expect(InvalidParameter, status);
1268     status = GdipIsVisiblePathPoint(path, 0.0, 0.0, graphics, NULL);
1269     expect(InvalidParameter, status);
1270 
1271     /* empty path */
1272     result = TRUE;
1273     status = GdipIsVisiblePathPoint(path, 0.0, 0.0, NULL, &result);
1274     expect(Ok, status);
1275     expect(FALSE, result);
1276     /* rect */
1277     status = GdipAddPathRectangle(path, 0.0, 0.0, 10.0, 10.0);
1278     expect(Ok, status);
1279     result = FALSE;
1280     status = GdipIsVisiblePathPoint(path, 0.0, 0.0, NULL, &result);
1281     expect(Ok, status);
1282     expect(TRUE, result);
1283     result = TRUE;
1284     status = GdipIsVisiblePathPoint(path, 11.0, 11.0, NULL, &result);
1285     expect(Ok, status);
1286     expect(FALSE, result);
1287     /* not affected by clipping */
1288     status = GdipSetClipRect(graphics, 5.0, 5.0, 5.0, 5.0, CombineModeReplace);
1289     expect(Ok, status);
1290     result = FALSE;
1291     status = GdipIsVisiblePathPoint(path, 0.0, 0.0, graphics, &result);
1292     expect(Ok, status);
1293     expect(TRUE, result);
1294 
1295     GdipDeletePath(path);
1296     GdipDeleteGraphics(graphics);
1297     ReleaseDC(0, hdc);
1298 }
1299 
1300 static void test_empty_rect(void)
1301 {
1302     GpPath *path;
1303     GpStatus status;
1304     INT count;
1305     BOOL result;
1306 
1307     status = GdipCreatePath(FillModeAlternate, &path);
1308     expect(Ok, status);
1309 
1310     status = GdipAddPathRectangle(path, 0.0, 0.0, -5.0, 5.0);
1311     expect(Ok, status);
1312 
1313     status = GdipGetPointCount(path, &count);
1314     expect(Ok, status);
1315     expect(0, count);
1316 
1317     status = GdipIsVisiblePathPoint(path, -2.0, 2.0, NULL, &result);
1318     expect(Ok, status);
1319     expect(FALSE, result);
1320 
1321     status = GdipAddPathRectangle(path, 0.0, 0.0, 5.0, -5.0);
1322     expect(Ok, status);
1323 
1324     status = GdipGetPointCount(path, &count);
1325     expect(Ok, status);
1326     expect(0, count);
1327 
1328     status = GdipAddPathRectangle(path, 0.0, 0.0, 0.0, 5.0);
1329     expect(Ok, status);
1330 
1331     status = GdipGetPointCount(path, &count);
1332     expect(Ok, status);
1333     expect(0, count);
1334 
1335     status = GdipAddPathRectangle(path, 0.0, 0.0, 5.0, 0.0);
1336     expect(Ok, status);
1337 
1338     status = GdipGetPointCount(path, &count);
1339     expect(Ok, status);
1340     expect(0, count);
1341 
1342     status = GdipAddPathRectangle(path, 0.0, 0.0, 5.0, 0.1);
1343     expect(Ok, status);
1344 
1345     status = GdipGetPointCount(path, &count);
1346     expect(Ok, status);
1347     expect(4, count);
1348 
1349     GdipDeletePath(path);
1350 }
1351 
1352 START_TEST(graphicspath)
1353 {
1354     struct GdiplusStartupInput gdiplusStartupInput;
1355     ULONG_PTR gdiplusToken;
1356     HMODULE hmsvcrt;
1357     int (CDECL * _controlfp_s)(unsigned int *cur, unsigned int newval, unsigned int mask);
1358 
1359     /* Enable all FP exceptions except _EM_INEXACT, which gdi32 can trigger */
1360     hmsvcrt = LoadLibraryA("msvcrt");
1361     _controlfp_s = (void*)GetProcAddress(hmsvcrt, "_controlfp_s");
1362     if (_controlfp_s) _controlfp_s(0, 0, 0x0008001e);
1363 
1364     gdiplusStartupInput.GdiplusVersion              = 1;
1365     gdiplusStartupInput.DebugEventCallback          = NULL;
1366     gdiplusStartupInput.SuppressBackgroundThread    = 0;
1367     gdiplusStartupInput.SuppressExternalCodecs      = 0;
1368 
1369     GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
1370 
1371     test_constructor_destructor();
1372     test_getpathdata();
1373     test_line2();
1374     test_arc();
1375     test_worldbounds();
1376     test_pathpath();
1377     test_ellipse();
1378     test_linei();
1379     test_rect();
1380     test_polygon();
1381     test_lastpoint();
1382     test_addcurve();
1383     test_addclosedcurve();
1384     test_reverse();
1385     test_addpie();
1386     test_flatten();
1387     test_widen();
1388     test_isvisible();
1389     test_empty_rect();
1390 
1391     GdiplusShutdown(gdiplusToken);
1392 }
1393