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