gdiplus: Implemented GdipAddPathCurve2 with tests.
[wine] / dlls / gdiplus / tests / graphicspath.c
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 "windows.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     if(todo_size) todo_wine
91         ok(size == expected_size, "Path size %d does not match expected size %d\n",
92             size, expected_size);
93     else
94         ok(size == expected_size, "Path size %d does not match expected size %d\n",
95             size, expected_size);
96
97     points = HeapAlloc(GetProcessHeap(), 0, size * sizeof(GpPointF));
98     types = HeapAlloc(GetProcessHeap(), 0, size);
99
100     if(GdipGetPathPoints(path, points, size) != Ok || GdipGetPathTypes(path, types, size) != Ok){
101         skip("Cannot perform path comparisons due to failure to retrieve path.\n");
102         goto end;
103     }
104
105     numskip = expected_size ? expected[eidx].wine_only_entries_preceding : 0;
106     while (idx < size && eidx < expected_size){
107         /* We allow a few pixels fudge in matching X and Y coordinates to account for imprecision in
108          * floating point to integer conversion */
109         BOOL match = (types[idx] == expected[eidx].type) &&
110             fabs(points[idx].X - expected[eidx].X) <= 2.0 &&
111             fabs(points[idx].Y - expected[eidx].Y) <= 2.0;
112
113         stringify_point_type(expected[eidx].type, ename);
114         stringify_point_type(types[idx], name);
115
116         if (expected[eidx].todo || numskip) todo_wine
117             ok(match, "Expected #%d: %s (%.1f,%.1f) but got %s (%.1f,%.1f)\n", eidx,
118                ename, expected[eidx].X, expected[eidx].Y,
119                name, points[idx].X, points[idx].Y);
120         else
121             ok(match, "Expected #%d: %s (%.1f,%.1f) but got %s (%.1f,%.1f)\n", eidx,
122                ename, expected[eidx].X, expected[eidx].Y,
123                name, points[idx].X, points[idx].Y);
124
125         if (match || expected[eidx].todo != 2)
126             idx++;
127         if (match || !numskip--)
128             numskip = expected[++eidx].wine_only_entries_preceding;
129     }
130
131 end:
132     HeapFree(GetProcessHeap(), 0, types);
133     HeapFree(GetProcessHeap(), 0, points);
134 }
135
136 static void test_constructor_destructor(void)
137 {
138     GpStatus status;
139     GpPath* path = NULL;
140
141     status = GdipCreatePath(FillModeAlternate, &path);
142     expect(Ok, status);
143     ok(path != NULL, "Expected path to be initialized\n");
144
145     status = GdipDeletePath(NULL);
146     expect(InvalidParameter, status);
147
148     status = GdipDeletePath(path);
149     expect(Ok, status);
150 }
151
152 static void test_getpathdata(void)
153 {
154     GpPath *path;
155     GpPathData data;
156     GpStatus status;
157     INT count;
158
159     GdipCreatePath(FillModeAlternate, &path);
160     status = GdipAddPathLine(path, 5.0, 5.0, 100.0, 50.0);
161     expect(Ok, status);
162
163     /* Prepare storage. Made by wrapper class. */
164     status = GdipGetPointCount(path, &count);
165     expect(Ok, status);
166
167     data.Count  = 2;
168     data.Types  = GdipAlloc(sizeof(BYTE) * count);
169     data.Points = GdipAlloc(sizeof(PointF) * count);
170
171     status = GdipGetPathData(path, &data);
172     expect(Ok, status);
173     expect((data.Points[0].X == 5.0) && (data.Points[0].Y == 5.0) &&
174            (data.Points[1].X == 100.0) && (data.Points[1].Y == 50.0), TRUE);
175     expect((data.Types[0] == PathPointTypeStart) && (data.Types[1] == PathPointTypeLine), TRUE);
176
177     GdipFree(data.Points);
178     GdipFree(data.Types);
179     GdipDeletePath(path);
180 }
181
182 static path_test_t line2_path[] = {
183     {0.0, 50.0, PathPointTypeStart, 0, 0}, /*0*/
184     {5.0, 45.0, PathPointTypeLine, 0, 0}, /*1*/
185     {0.0, 40.0, PathPointTypeLine, 0, 0}, /*2*/
186     {15.0, 35.0, PathPointTypeLine, 0, 0}, /*3*/
187     {0.0, 30.0, PathPointTypeLine, 0, 0}, /*4*/
188     {25.0, 25.0, PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*5*/
189     {0.0, 20.0, PathPointTypeStart, 0, 0}, /*6*/
190     {35.0, 15.0, PathPointTypeLine, 0, 0}, /*7*/
191     {0.0, 10.0, PathPointTypeLine, 0, 0} /*8*/
192     };
193
194 static void test_line2(void)
195 {
196     GpStatus status;
197     GpPath* path;
198     int i;
199     GpPointF line2_points[9];
200
201     for(i = 0; i < 9; i ++){
202         line2_points[i].X = i * 5.0 * (REAL)(i % 2);
203         line2_points[i].Y = 50.0 - i * 5.0;
204     }
205
206     GdipCreatePath(FillModeAlternate, &path);
207     status = GdipAddPathLine2(path, line2_points, 3);
208     expect(Ok, status);
209     status = GdipAddPathLine2(path, &(line2_points[3]), 3);
210     expect(Ok, status);
211     status = GdipClosePathFigure(path);
212     expect(Ok, status);
213     status = GdipAddPathLine2(path, &(line2_points[6]), 3);
214     expect(Ok, status);
215
216     ok_path(path, line2_path, sizeof(line2_path)/sizeof(path_test_t), FALSE);
217 }
218
219 static path_test_t arc_path[] = {
220     {600.0, 450.0, PathPointTypeStart, 0, 0}, /*0*/
221     {600.0, 643.3, PathPointTypeBezier, 0, 0}, /*1*/
222     {488.1, 800.0, PathPointTypeBezier, 0, 0}, /*2*/
223     {350.0, 800.0, PathPointTypeBezier, 0, 0}, /*3*/
224     {600.0, 450.0, PathPointTypeLine, 0, 0}, /*4*/
225     {600.0, 643.3, PathPointTypeBezier, 0, 0}, /*5*/
226     {488.1, 800.0, PathPointTypeBezier, 0, 0}, /*6*/
227     {350.0, 800.0, PathPointTypeBezier, 0, 0}, /*7*/
228     {329.8, 800.0, PathPointTypeBezier, 0, 0}, /*8*/
229     {309.7, 796.6, PathPointTypeBezier, 0, 0}, /*9*/
230     {290.1, 789.8, PathPointTypeBezier, 0, 0}, /*10*/
231     {409.9, 110.2, PathPointTypeLine, 0, 0}, /*11*/
232     {544.0, 156.5, PathPointTypeBezier, 0, 0}, /*12*/
233     {625.8, 346.2, PathPointTypeBezier, 0, 0}, /*13*/
234     {592.7, 533.9, PathPointTypeBezier, 0, 0}, /*14*/
235     {592.5, 535.3, PathPointTypeBezier, 0, 0}, /*15*/
236     {592.2, 536.7, PathPointTypeBezier, 0, 0}, /*16*/
237     {592.0, 538.1, PathPointTypeBezier, 0, 0}, /*17*/
238     {409.9, 789.8, PathPointTypeLine, 0, 0}, /*18*/
239     {544.0, 743.5, PathPointTypeBezier, 0, 0}, /*19*/
240     {625.8, 553.8, PathPointTypeBezier, 0, 0}, /*20*/
241     {592.7, 366.1, PathPointTypeBezier, 0, 0}, /*21*/
242     {592.5, 364.7, PathPointTypeBezier, 0, 0}, /*22*/
243     {592.2, 363.3, PathPointTypeBezier, 0, 0}, /*23*/
244     {592.0, 361.9, PathPointTypeBezier, 0, 0}, /*24*/
245     {540.4, 676.9, PathPointTypeLine, 0, 0}, /*25*/
246     {629.9, 529.7, PathPointTypeBezier, 0, 0}, /*26*/
247     {617.2, 308.8, PathPointTypeBezier, 0, 0}, /*27*/
248     {512.1, 183.5, PathPointTypeBezier, 0, 0}, /*28*/
249     {406.9, 58.2, PathPointTypeBezier, 0, 0}, /*29*/
250     {249.1, 75.9, PathPointTypeBezier, 0, 0}, /*30*/
251     {159.6, 223.1, PathPointTypeBezier, 0, 0}, /*31*/
252     {70.1, 370.3, PathPointTypeBezier, 0, 0}, /*32*/
253     {82.8, 591.2, PathPointTypeBezier, 0, 0}, /*33*/
254     {187.9, 716.5, PathPointTypeBezier, 0, 0}, /*34*/
255     {293.1, 841.8, PathPointTypeBezier, 0, 0}, /*35*/
256     {450.9, 824.1, PathPointTypeBezier, 0, 0}, /*36*/
257     {540.4, 676.9, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 1} /*37*/
258     };
259
260 static void test_arc(void)
261 {
262     GpStatus status;
263     GpPath* path;
264
265     GdipCreatePath(FillModeAlternate, &path);
266     /* Exactly 90 degrees */
267     status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 90.0);
268     expect(Ok, status);
269     /* Over 90 degrees */
270     status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
271     expect(Ok, status);
272     /* Negative start angle */
273     status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, -80.0, 100.0);
274     expect(Ok, status);
275     /* Negative sweep angle */
276     status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 80.0, -100.0);
277     expect(Ok, status);
278     /* More than a full revolution */
279     status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 50.0, -400.0);
280     expect(Ok, status);
281     /* 0 sweep angle */
282     status = GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 50.0, 0.0);
283     expect(Ok, status);
284
285     ok_path(path, arc_path, sizeof(arc_path)/sizeof(path_test_t), FALSE);
286 }
287
288 static void test_worldbounds(void)
289 {
290     GpStatus status;
291     GpPath *path;
292     GpPen *pen;
293     GpMatrix *matrix;
294     GpRectF bounds;
295     GpPointF line2_points[10];
296     int i;
297
298     for(i = 0; i < 10; i ++){
299         line2_points[i].X = 200.0 + i * 50.0 * (i % 2);
300         line2_points[i].Y = 200.0 + i * 50.0 * !(i % 2);
301     }
302     GdipCreatePen1((ARGB)0xdeadbeef, 20.0, UnitWorld, &pen);
303     GdipSetPenEndCap(pen, LineCapSquareAnchor);
304     GdipCreateMatrix2(1.5, 0.0, 1.0, 1.2, 10.4, 10.2, &matrix);
305
306     GdipCreatePath(FillModeAlternate, &path);
307     GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
308     GdipAddPathLine2(path, &(line2_points[0]), 10);
309     status = GdipGetPathWorldBounds(path, &bounds, NULL, NULL);
310     expect(Ok, status);
311     GdipDeletePath(path);
312
313     expectf(200.0, bounds.X);
314     expectf(200.0, bounds.Y);
315     expectf(450.0, bounds.Width);
316     expectf(600.0, bounds.Height);
317
318     GdipCreatePath(FillModeAlternate, &path);
319     GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
320     GdipAddPathLine2(path, &(line2_points[0]), 10);
321     status = GdipGetPathWorldBounds(path, &bounds, matrix, NULL);
322     expect(Ok, status);
323     GdipDeletePath(path);
324
325     expectf(510.4, bounds.X);
326     expectf(250.2, bounds.Y);
327     expectf(1275.0, bounds.Width);
328     expectf(720.0, bounds.Height);
329
330     GdipCreatePath(FillModeAlternate, &path);
331     GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
332     GdipAddPathLine2(path, &(line2_points[0]), 10);
333     status = GdipGetPathWorldBounds(path, &bounds, NULL, pen);
334     expect(Ok, status);
335     GdipDeletePath(path);
336
337     expectf(100.0, bounds.X);
338     expectf(100.0, bounds.Y);
339     expectf(650.0, bounds.Width);
340     expectf(800.0, bounds.Height);
341
342     GdipCreatePath(FillModeAlternate, &path);
343     GdipAddPathLine2(path, &(line2_points[0]), 2);
344     status = GdipGetPathWorldBounds(path, &bounds, NULL, pen);
345     expect(Ok, status);
346     GdipDeletePath(path);
347
348     expectf(156.0, bounds.X);
349     expectf(156.0, bounds.Y);
350     expectf(138.0, bounds.Width);
351     expectf(88.0, bounds.Height);
352
353     line2_points[2].X = 2 * line2_points[1].X - line2_points[0].X;
354     line2_points[2].Y = 2 * line2_points[1].Y - line2_points[0].Y;
355
356     GdipCreatePath(FillModeAlternate, &path);
357     GdipAddPathLine2(path, &(line2_points[0]), 3);
358     status = GdipGetPathWorldBounds(path, &bounds, NULL, pen);
359     expect(Ok, status);
360     GdipDeletePath(path);
361
362     expectf(100.0, bounds.X);
363     expectf(100.0, bounds.Y);
364     expectf(300.0, bounds.Width);
365     expectf(200.0, bounds.Height);
366
367     GdipCreatePath(FillModeAlternate, &path);
368     GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 45.0, 20.0);
369     status = GdipGetPathWorldBounds(path, &bounds, NULL, pen);
370     expect(Ok, status);
371     GdipDeletePath(path);
372
373     expectf(386.7, bounds.X);
374     expectf(553.4, bounds.Y);
375     expectf(266.8, bounds.Width);
376     expectf(289.6, bounds.Height);
377
378     GdipCreatePath(FillModeAlternate, &path);
379     status = GdipGetPathWorldBounds(path, &bounds, matrix, pen);
380     expect(Ok, status);
381     GdipDeletePath(path);
382
383     expectf(0.0, bounds.X);
384     expectf(0.0, bounds.Y);
385     expectf(0.0, bounds.Width);
386     expectf(0.0, bounds.Height);
387
388     GdipCreatePath(FillModeAlternate, &path);
389     GdipAddPathLine2(path, &(line2_points[0]), 2);
390     status = GdipGetPathWorldBounds(path, &bounds, matrix, pen);
391     expect(Ok, status);
392     GdipDeletePath(path);
393
394     todo_wine{
395         expectf(427.9, bounds.X);
396         expectf(167.7, bounds.Y);
397         expectf(239.9, bounds.Width);
398         expectf(164.9, bounds.Height);
399     }
400
401     GdipDeleteMatrix(matrix);
402     GdipCreateMatrix2(0.9, -0.5, -0.5, -1.2, 10.4, 10.2, &matrix);
403     GdipCreatePath(FillModeAlternate, &path);
404     GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, 0.0, 100.0);
405     GdipAddPathLine2(path, &(line2_points[0]), 10);
406     status = GdipGetPathWorldBounds(path, &bounds, matrix, NULL);
407     expect(Ok, status);
408     GdipDeletePath(path);
409
410     expectf(-209.6, bounds.X);
411     expectf(-1274.8, bounds.Y);
412     expectf(705.0, bounds.Width);
413     expectf(945.0, bounds.Height);
414 }
415
416 static path_test_t pathpath_path[] = {
417     {600.00, 450.00, PathPointTypeStart, 0, 0}, /*0*/
418     {600.00, 643.30, PathPointTypeBezier, 0, 0}, /*1*/
419     {488.07, 800.00, PathPointTypeBezier, 0, 0}, /*2*/
420     {350.00, 800.00, PathPointTypeBezier, 0, 0}, /*3*/
421     {319.61, 797.40, PathPointTypeStart, 0, 0}, /*4*/
422     {182.56, 773.90, PathPointTypeBezier, 0, 0}, /*5*/
423     {85.07, 599.31, PathPointTypeBezier, 0, 0}, /*6*/
424     {101.85, 407.45, PathPointTypeBezier, 0, 0}, /*7*/
425     {102.54, 399.66, PathPointTypeBezier, 0, 0}, /*8*/
426     {103.40, 391.91, PathPointTypeBezier, 0, 0}, /*9*/
427     {104.46, 384.21, PathPointTypeBezier, 0, 0}, /*10*/
428     {409.92, 110.20, PathPointTypeLine, 0, 0}, /*11*/
429     {543.96, 156.53, PathPointTypeBezier, 0, 0}, /*12*/
430     {625.80, 346.22, PathPointTypeBezier, 0, 0}, /*13*/
431     {592.71, 533.88, PathPointTypeBezier, 0, 0}, /*14*/
432     {592.47, 535.28, PathPointTypeBezier, 0, 0}, /*15*/
433     {592.22, 536.67, PathPointTypeBezier, 0, 0}, /*16*/
434     {591.96, 538.06, PathPointTypeBezier, 0, 0}, /*17*/
435     {319.61, 797.40, PathPointTypeLine, 0, 0}, /*18*/
436     {182.56, 773.90, PathPointTypeBezier, 0, 0}, /*19*/
437     {85.07, 599.31, PathPointTypeBezier, 0, 0}, /*20*/
438     {101.85, 407.45, PathPointTypeBezier, 0, 0}, /*21*/
439     {102.54, 399.66, PathPointTypeBezier, 0, 0}, /*22*/
440     {103.40, 391.91, PathPointTypeBezier, 0, 0}, /*23*/
441     {104.46, 384.21, PathPointTypeBezier, 0, 0} /*24*/
442     };
443
444 static void test_pathpath(void)
445 {
446     GpStatus status;
447     GpPath* path1, *path2;
448
449     GdipCreatePath(FillModeAlternate, &path2);
450     GdipAddPathArc(path2, 100.0, 100.0, 500.0, 700.0, 95.0, 100.0);
451
452     GdipCreatePath(FillModeAlternate, &path1);
453     GdipAddPathArc(path1, 100.0, 100.0, 500.0, 700.0, 0.0, 90.0);
454     status = GdipAddPathPath(path1, path2, FALSE);
455     expect(Ok, status);
456     GdipAddPathArc(path1, 100.0, 100.0, 500.0, 700.0, -80.0, 100.0);
457     status = GdipAddPathPath(path1, path2, TRUE);
458     expect(Ok, status);
459
460     ok_path(path1, pathpath_path, sizeof(pathpath_path)/sizeof(path_test_t), FALSE);
461
462     GdipDeletePath(path1);
463     GdipDeletePath(path2);
464 }
465
466 static path_test_t ellipse_path[] = {
467     {30.00, 125.25, PathPointTypeStart, 0, 0}, /*0*/
468     {30.00, 139.20, PathPointTypeBezier, 0, 0}, /*1*/
469     {25.52, 150.50, PathPointTypeBezier, 0, 0}, /*2*/
470     {20.00, 150.50, PathPointTypeBezier, 0, 0}, /*3*/
471     {14.48, 150.50, PathPointTypeBezier, 0, 0}, /*4*/
472     {10.00, 139.20, PathPointTypeBezier, 0, 0}, /*5*/
473     {10.00, 125.25, PathPointTypeBezier, 0, 0}, /*6*/
474     {10.00, 111.30, PathPointTypeBezier, 0, 0}, /*7*/
475     {14.48, 100.00, PathPointTypeBezier, 0, 0}, /*8*/
476     {20.00, 100.00, PathPointTypeBezier, 0, 0}, /*9*/
477     {25.52, 100.00, PathPointTypeBezier, 0, 0}, /*10*/
478     {30.00, 111.30, PathPointTypeBezier, 0, 0}, /*11*/
479     {30.00, 125.25, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 0}, /*12*/
480     {7.00, 11.00, PathPointTypeStart, 0, 0}, /*13*/
481     {13.00, 17.00, PathPointTypeLine, 0, 0}, /*14*/
482     {5.00, 195.00, PathPointTypeStart, 0, 0}, /*15*/
483     {5.00, 192.24, PathPointTypeBezier, 0, 0}, /*16*/
484     {6.12, 190.00, PathPointTypeBezier, 0, 0}, /*17*/
485     {7.50, 190.00, PathPointTypeBezier, 0, 0}, /*18*/
486     {8.88, 190.00, PathPointTypeBezier, 0, 0}, /*19*/
487     {10.00, 192.24, PathPointTypeBezier, 0, 0}, /*20*/
488     {10.00, 195.00, PathPointTypeBezier, 0, 0}, /*21*/
489     {10.00, 197.76, PathPointTypeBezier, 0, 0}, /*22*/
490     {8.88, 200.00, PathPointTypeBezier, 0, 0}, /*23*/
491     {7.50, 200.00, PathPointTypeBezier, 0, 0}, /*24*/
492     {6.12, 200.00, PathPointTypeBezier, 0, 0}, /*25*/
493     {5.00, 197.76, PathPointTypeBezier, 0, 0}, /*26*/
494     {5.00, 195.00, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 0}, /*27*/
495     {10.00, 300.50, PathPointTypeStart, 0, 0}, /*28*/
496     {10.00, 300.78, PathPointTypeBezier, 0, 0}, /*29*/
497     {10.00, 301.00, PathPointTypeBezier, 0, 0}, /*30*/
498     {10.00, 301.00, PathPointTypeBezier, 0, 0}, /*31*/
499     {10.00, 301.00, PathPointTypeBezier, 0, 0}, /*32*/
500     {10.00, 300.78, PathPointTypeBezier, 0, 0}, /*33*/
501     {10.00, 300.50, PathPointTypeBezier, 0, 0}, /*34*/
502     {10.00, 300.22, PathPointTypeBezier, 0, 0}, /*35*/
503     {10.00, 300.00, PathPointTypeBezier, 0, 0}, /*36*/
504     {10.00, 300.00, PathPointTypeBezier, 0, 0}, /*37*/
505     {10.00, 300.00, PathPointTypeBezier, 0, 0}, /*38*/
506     {10.00, 300.22, PathPointTypeBezier, 0, 0}, /*39*/
507     {10.00, 300.50, PathPointTypeBezier | PathPointTypeCloseSubpath, 0, 0} /*40*/
508     };
509
510 static void test_ellipse(void)
511 {
512     GpStatus status;
513     GpPath *path;
514     GpPointF points[2];
515
516     points[0].X = 7.0;
517     points[0].Y = 11.0;
518     points[1].X = 13.0;
519     points[1].Y = 17.0;
520
521     GdipCreatePath(FillModeAlternate, &path);
522     status = GdipAddPathEllipse(path, 10.0, 100.0, 20.0, 50.5);
523     expect(Ok, status);
524     GdipAddPathLine2(path, points, 2);
525     status = GdipAddPathEllipse(path, 10.0, 200.0, -5.0, -10.0);
526     expect(Ok, status);
527     GdipClosePathFigure(path);
528     status = GdipAddPathEllipse(path, 10.0, 300.0, 0.0, 1.0);
529     expect(Ok, status);
530
531     ok_path(path, ellipse_path, sizeof(ellipse_path)/sizeof(path_test_t), FALSE);
532
533     GdipDeletePath(path);
534 }
535
536 static path_test_t linei_path[] = {
537     {5.00, 5.00, PathPointTypeStart, 0, 0}, /*0*/
538     {6.00, 8.00, PathPointTypeLine, 0, 0}, /*1*/
539     {409.92, 110.20, PathPointTypeLine, 0, 0}, /*2*/
540     {543.96, 156.53, PathPointTypeBezier, 0, 0}, /*3*/
541     {625.80, 346.22, PathPointTypeBezier, 0, 0}, /*4*/
542     {592.71, 533.88, PathPointTypeBezier, 0, 0}, /*5*/
543     {592.47, 535.28, PathPointTypeBezier, 0, 0}, /*6*/
544     {592.22, 536.67, PathPointTypeBezier, 0, 0}, /*7*/
545     {591.96, 538.06, PathPointTypeBezier, 0, 0}, /*8*/
546     {15.00, 15.00, PathPointTypeLine, 0, 0}, /*9*/
547     {26.00, 28.00, PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*10*/
548     {35.00, 35.00, PathPointTypeStart, 0, 0}, /*11*/
549     {36.00, 38.00, PathPointTypeLine, 0, 0} /*12*/
550     };
551
552 static void test_linei(void)
553 {
554     GpStatus status;
555     GpPath *path;
556     GpPointF points[2];
557
558     points[0].X = 7.0;
559     points[0].Y = 11.0;
560     points[1].X = 13.0;
561     points[1].Y = 17.0;
562
563     GdipCreatePath(FillModeAlternate, &path);
564     status = GdipAddPathLineI(path, 5.0, 5.0, 6.0, 8.0);
565     expect(Ok, status);
566     GdipAddPathArc(path, 100.0, 100.0, 500.0, 700.0, -80.0, 100.0);
567     status = GdipAddPathLineI(path, 15.0, 15.0, 26.0, 28.0);
568     expect(Ok, status);
569     GdipClosePathFigure(path);
570     status = GdipAddPathLineI(path, 35.0, 35.0, 36.0, 38.0);
571     expect(Ok, status);
572
573     ok_path(path, linei_path, sizeof(linei_path)/sizeof(path_test_t), FALSE);
574
575     GdipDeletePath(path);
576 }
577
578 static path_test_t poly_path[] = {
579     {5.00, 5.00, PathPointTypeStart, 0, 0},   /*1*/
580     {6.00, 8.00, PathPointTypeLine, 0, 0},    /*2*/
581     {0.00,  0.00,  PathPointTypeStart, 0, 0}, /*3*/
582     {10.00, 10.00, PathPointTypeLine, 0, 0},  /*4*/
583     {10.00, 20.00, PathPointTypeLine, 0, 0},  /*5*/
584     {30.00, 10.00, PathPointTypeLine, 0, 0},  /*6*/
585     {20.00, 0.00, PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*7*/
586     };
587
588 static void test_polygon(void)
589 {
590     GpStatus status;
591     GpPath *path;
592     GpPointF points[5];
593
594     points[0].X = 0.0;
595     points[0].Y = 0.0;
596     points[1].X = 10.0;
597     points[1].Y = 10.0;
598     points[2].X = 10.0;
599     points[2].Y = 20.0;
600     points[3].X = 30.0;
601     points[3].Y = 10.0;
602     points[4].X = 20.0;
603     points[4].Y = 0.0;
604
605     GdipCreatePath(FillModeAlternate, &path);
606
607     /* NULL args */
608     status = GdipAddPathPolygon(NULL, points, 5);
609     expect(InvalidParameter, status);
610     status = GdipAddPathPolygon(path, NULL, 5);
611     expect(InvalidParameter, status);
612     /* Polygon should have 3 points at least */
613     status = GdipAddPathPolygon(path, points, 2);
614     expect(InvalidParameter, status);
615
616     /* to test how it prolongs not empty path */
617     status = GdipAddPathLine(path, 5.0, 5.0, 6.0, 8.0);
618     expect(Ok, status);
619     status = GdipAddPathPolygon(path, points, 5);
620     expect(Ok, status);
621     /* check resulting path */
622     ok_path(path, poly_path, sizeof(poly_path)/sizeof(path_test_t), FALSE);
623
624     GdipDeletePath(path);
625 }
626
627 static path_test_t rect_path[] = {
628     {5.0, 5.0,       PathPointTypeStart, 0, 0}, /*0*/
629     {105.0, 5.0,     PathPointTypeLine,  0, 0}, /*1*/
630     {105.0, 55.0,    PathPointTypeLine,  0, 0}, /*2*/
631     {5.0, 55.0,      PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}, /*3*/
632
633     {100.0, 50.0,    PathPointTypeStart, 0, 0}, /*4*/
634     {220.0, 50.0,    PathPointTypeLine,  0, 0}, /*5*/
635     {220.0, 80.0,    PathPointTypeLine,  0, 0}, /*6*/
636     {100.0, 80.0,    PathPointTypeLine | PathPointTypeCloseSubpath, 0, 0}  /*7*/
637     };
638
639 static void test_rect(void)
640 {
641     GpStatus status;
642     GpPath *path;
643     GpRectF rects[2];
644
645     GdipCreatePath(FillModeAlternate, &path);
646     status = GdipAddPathRectangle(path, 5.0, 5.0, 100.0, 50.0);
647     expect(Ok, status);
648     status = GdipAddPathRectangle(path, 100.0, 50.0, 120.0, 30.0);
649     expect(Ok, status);
650
651     ok_path(path, rect_path, sizeof(rect_path)/sizeof(path_test_t), FALSE);
652
653     GdipDeletePath(path);
654
655     GdipCreatePath(FillModeAlternate, &path);
656
657     rects[0].X      = 5.0;
658     rects[0].Y      = 5.0;
659     rects[0].Width  = 100.0;
660     rects[0].Height = 50.0;
661     rects[1].X      = 100.0;
662     rects[1].Y      = 50.0;
663     rects[1].Width  = 120.0;
664     rects[1].Height = 30.0;
665
666     status = GdipAddPathRectangles(path, (GDIPCONST GpRectF*)&rects, 2);
667     expect(Ok, status);
668
669     ok_path(path, rect_path, sizeof(rect_path)/sizeof(path_test_t), FALSE);
670
671     GdipDeletePath(path);
672 }
673
674 static void test_lastpoint(void)
675 {
676     GpStatus status;
677     GpPath *path;
678     GpPointF ptf;
679
680     GdipCreatePath(FillModeAlternate, &path);
681     status = GdipAddPathRectangle(path, 5.0, 5.0, 100.0, 50.0);
682     expect(Ok, status);
683
684     /* invalid args */
685     status = GdipGetPathLastPoint(NULL, &ptf);
686     expect(InvalidParameter, status);
687     status = GdipGetPathLastPoint(path, NULL);
688     expect(InvalidParameter, status);
689     status = GdipGetPathLastPoint(NULL, NULL);
690     expect(InvalidParameter, status);
691
692     status = GdipGetPathLastPoint(path, &ptf);
693     expect(Ok, status);
694     expect(TRUE, (ptf.X == 5.0) && (ptf.Y == 55.0));
695
696     GdipDeletePath(path);
697 }
698
699 static path_test_t addcurve_path[] = {
700     {0.0, 0.0,   PathPointTypeStart,  0, 0}, /*0*/
701     {3.3, 3.3,   PathPointTypeBezier, 0, 0}, /*1*/
702     {6.7, 3.3,   PathPointTypeBezier, 0, 0}, /*2*/
703     {10.0, 10.0, PathPointTypeBezier, 0, 0}, /*3*/
704     {13.3, 16.7, PathPointTypeBezier, 0, 0}, /*4*/
705     {3.3,  20.0, PathPointTypeBezier, 0, 0}, /*5*/
706     {10.0, 20.0, PathPointTypeBezier, 0, 0}, /*6*/
707     {16.7, 20.0, PathPointTypeBezier, 0, 0}, /*7*/
708     {23.3, 13.3, PathPointTypeBezier, 0, 0}, /*8*/
709     {30.0, 10.0, PathPointTypeBezier, 0, 0}  /*9*/
710     };
711 static path_test_t addcurve_path2[] = {
712     {100.0,120.0,PathPointTypeStart,  0, 0}, /*0*/
713     {123.0,10.0, PathPointTypeLine,   0, 0}, /*1*/
714     {0.0, 0.0,   PathPointTypeLine,   0, 0}, /*2*/
715     {3.3, 3.3,   PathPointTypeBezier, 0, 0}, /*3*/
716     {6.7, 3.3,   PathPointTypeBezier, 0, 0}, /*4*/
717     {10.0, 10.0, PathPointTypeBezier, 0, 0}, /*5*/
718     {13.3, 16.7, PathPointTypeBezier, 0, 0}, /*6*/
719     {3.3,  20.0, PathPointTypeBezier, 0, 0}, /*7*/
720     {10.0, 20.0, PathPointTypeBezier, 0, 0}, /*8*/
721     {16.7, 20.0, PathPointTypeBezier, 0, 0}, /*9*/
722     {23.3, 13.3, PathPointTypeBezier, 0, 0}, /*10*/
723     {30.0, 10.0, PathPointTypeBezier, 0, 0}  /*11*/
724     };
725 static void test_addcurve(void)
726 {
727     GpStatus status;
728     GpPath *path;
729     GpPointF points[4];
730
731     points[0].X = 0.0;
732     points[0].Y = 0.0;
733     points[1].X = 10.0;
734     points[1].Y = 10.0;
735     points[2].X = 10.0;
736     points[2].Y = 20.0;
737     points[3].X = 30.0;
738     points[3].Y = 10.0;
739
740     GdipCreatePath(FillModeAlternate, &path);
741
742     /* NULL args */
743     status = GdipAddPathCurve2(NULL, NULL, 0, 0.0);
744     expect(InvalidParameter, status);
745     status = GdipAddPathCurve2(path, NULL, 0, 0.0);
746     expect(InvalidParameter, status);
747     status = GdipAddPathCurve2(path, points, -1, 0.0);
748     expect(InvalidParameter, status);
749     status = GdipAddPathCurve2(path, points, 1, 1.0);
750     expect(InvalidParameter, status);
751
752     /* add to empty path */
753     status = GdipAddPathCurve2(path, points, 4, 1.0);
754     expect(Ok, status);
755     ok_path(path, addcurve_path, sizeof(addcurve_path)/sizeof(path_test_t), FALSE);
756     GdipDeletePath(path);
757
758     /* add to notempty path and opened figure */
759     GdipCreatePath(FillModeAlternate, &path);
760     GdipAddPathLine(path, 100.0, 120.0, 123.0, 10.0);
761     status = GdipAddPathCurve2(path, points, 4, 1.0);
762     expect(Ok, status);
763     ok_path(path, addcurve_path2, sizeof(addcurve_path2)/sizeof(path_test_t), FALSE);
764     GdipDeletePath(path);
765 }
766
767 START_TEST(graphicspath)
768 {
769     struct GdiplusStartupInput gdiplusStartupInput;
770     ULONG_PTR gdiplusToken;
771
772     gdiplusStartupInput.GdiplusVersion              = 1;
773     gdiplusStartupInput.DebugEventCallback          = NULL;
774     gdiplusStartupInput.SuppressBackgroundThread    = 0;
775     gdiplusStartupInput.SuppressExternalCodecs      = 0;
776
777     GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
778
779     test_constructor_destructor();
780     test_getpathdata();
781     test_line2();
782     test_arc();
783     test_worldbounds();
784     test_pathpath();
785     test_ellipse();
786     test_linei();
787     test_rect();
788     test_polygon();
789     test_lastpoint();
790     test_addcurve();
791
792     GdiplusShutdown(gdiplusToken);
793 }