usp10: Implement GPOS MarkToBase.
[wine] / dlls / gdi32 / tests / path.c
1 /*
2  * Unit test suite for paths
3  *
4  * Copyright 2007 Laurent Vromman
5  * Copyright 2007 Misha Koshelev
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <assert.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28
29 #include "wine/test.h"
30
31 #include "winuser.h"
32 #include "winerror.h"
33
34 #define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
35
36 static void test_path_state(void)
37 {
38     BYTE buffer[sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD)];
39     BITMAPINFO *bi = (BITMAPINFO *)buffer;
40     HDC hdc;
41     HBITMAP orig, dib;
42     void *bits;
43     BOOL ret;
44
45     hdc = CreateCompatibleDC( 0 );
46     memset( buffer, 0, sizeof(buffer) );
47     bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
48     bi->bmiHeader.biHeight = 256;
49     bi->bmiHeader.biWidth = 256;
50     bi->bmiHeader.biBitCount = 32;
51     bi->bmiHeader.biPlanes = 1;
52     bi->bmiHeader.biCompression = BI_RGB;
53     dib = CreateDIBSection( 0, bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0 );
54     orig = SelectObject( hdc, dib );
55
56     BeginPath( hdc );
57     LineTo( hdc, 100, 100 );
58     ret = WidenPath( hdc );
59     ok( !ret, "WidenPath succeeded\n" );
60
61     /* selecting another bitmap doesn't affect the path */
62     SelectObject( hdc, orig );
63     ret = WidenPath( hdc );
64     ok( !ret, "WidenPath succeeded\n" );
65
66     SelectObject( hdc, dib );
67     ret = WidenPath( hdc );
68     ok( !ret, "WidenPath succeeded\n" );
69
70     ret = EndPath( hdc );
71     ok( ret, "EndPath failed error %u\n", GetLastError() );
72     ret = WidenPath( hdc );
73     ok( ret, "WidenPath failed error %u\n", GetLastError() );
74
75     SelectObject( hdc, orig );
76     ret = WidenPath( hdc );
77     ok( ret, "WidenPath failed error %u\n", GetLastError() );
78
79     BeginPath( hdc );
80     LineTo( hdc, 100, 100 );
81     ret = WidenPath( hdc );
82     ok( !ret, "WidenPath succeeded\n" );
83     SaveDC( hdc );
84     SelectObject( hdc, dib );
85     ret = EndPath( hdc );
86     ok( ret, "EndPath failed error %u\n", GetLastError() );
87     ret = WidenPath( hdc );
88     ok( ret, "WidenPath failed error %u\n", GetLastError() );
89
90     /* path should be open again after RestoreDC */
91     RestoreDC( hdc, -1  );
92     ret = WidenPath( hdc );
93     ok( !ret, "WidenPath succeeded\n" );
94     ret = EndPath( hdc );
95     ok( ret, "EndPath failed error %u\n", GetLastError() );
96
97     SaveDC( hdc );
98     BeginPath( hdc );
99     RestoreDC( hdc, -1  );
100     ret = WidenPath( hdc );
101     ok( ret, "WidenPath failed error %u\n", GetLastError() );
102
103     /* test all functions with no path at all */
104     AbortPath( hdc );
105     SetLastError( 0xdeadbeef );
106     ret = WidenPath( hdc );
107     ok( !ret, "WidenPath succeeded\n" );
108     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
109         "wrong error %u\n", GetLastError() );
110
111     SetLastError( 0xdeadbeef );
112     ret = FlattenPath( hdc );
113     ok( !ret, "FlattenPath succeeded\n" );
114     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
115         "wrong error %u\n", GetLastError() );
116
117     SetLastError( 0xdeadbeef );
118     ret = StrokePath( hdc );
119     ok( !ret, "StrokePath succeeded\n" );
120     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
121         "wrong error %u\n", GetLastError() );
122
123     SetLastError( 0xdeadbeef );
124     ret = FillPath( hdc );
125     ok( !ret, "FillPath succeeded\n" );
126     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
127         "wrong error %u\n", GetLastError() );
128
129     SetLastError( 0xdeadbeef );
130     ret = StrokeAndFillPath( hdc );
131     ok( !ret, "StrokeAndFillPath succeeded\n" );
132     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
133         "wrong error %u\n", GetLastError() );
134
135     SetLastError( 0xdeadbeef );
136     ret = SelectClipPath( hdc, RGN_OR );
137     ok( !ret, "SelectClipPath succeeded\n" );
138     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
139         "wrong error %u\n", GetLastError() );
140
141     SetLastError( 0xdeadbeef );
142     ret = EndPath( hdc );
143     ok( !ret, "SelectClipPath succeeded\n" );
144     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
145         "wrong error %u\n", GetLastError() );
146
147     SetLastError( 0xdeadbeef );
148     ret = CloseFigure( hdc );
149     ok( !ret, "CloseFigure succeeded\n" );
150     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
151         "wrong error %u\n", GetLastError() );
152
153     /* test all functions with an open path */
154     AbortPath( hdc );
155     BeginPath( hdc );
156     SetLastError( 0xdeadbeef );
157     ret = WidenPath( hdc );
158     ok( !ret, "WidenPath succeeded\n" );
159     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
160         "wrong error %u\n", GetLastError() );
161
162     AbortPath( hdc );
163     BeginPath( hdc );
164     SetLastError( 0xdeadbeef );
165     ret = FlattenPath( hdc );
166     ok( !ret, "FlattenPath succeeded\n" );
167     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
168         "wrong error %u\n", GetLastError() );
169
170     AbortPath( hdc );
171     BeginPath( hdc );
172     SetLastError( 0xdeadbeef );
173     ret = StrokePath( hdc );
174     ok( !ret, "StrokePath succeeded\n" );
175     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
176         "wrong error %u\n", GetLastError() );
177
178     AbortPath( hdc );
179     BeginPath( hdc );
180     SetLastError( 0xdeadbeef );
181     ret = FillPath( hdc );
182     ok( !ret, "FillPath succeeded\n" );
183     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
184         "wrong error %u\n", GetLastError() );
185
186     AbortPath( hdc );
187     BeginPath( hdc );
188     SetLastError( 0xdeadbeef );
189     ret = StrokeAndFillPath( hdc );
190     ok( !ret, "StrokeAndFillPath succeeded\n" );
191     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
192         "wrong error %u\n", GetLastError() );
193
194     AbortPath( hdc );
195     BeginPath( hdc );
196     Rectangle( hdc, 1, 1, 10, 10 );  /* region needs some contents */
197     SetLastError( 0xdeadbeef );
198     ret = SelectClipPath( hdc, RGN_OR );
199     ok( !ret, "SelectClipPath succeeded\n" );
200     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
201         "wrong error %u\n", GetLastError() );
202
203     AbortPath( hdc );
204     BeginPath( hdc );
205     ret = CloseFigure( hdc );
206     ok( ret, "CloseFigure failed\n" );
207
208     /* test all functions with a closed path */
209     AbortPath( hdc );
210     BeginPath( hdc );
211     EndPath( hdc );
212     ret = WidenPath( hdc );
213     ok( ret, "WidenPath failed\n" );
214
215     AbortPath( hdc );
216     BeginPath( hdc );
217     EndPath( hdc );
218     ret = FlattenPath( hdc );
219     ok( ret, "FlattenPath failed\n" );
220
221     AbortPath( hdc );
222     BeginPath( hdc );
223     EndPath( hdc );
224     ret = StrokePath( hdc );
225     ok( ret, "StrokePath failed\n" );
226
227     AbortPath( hdc );
228     BeginPath( hdc );
229     EndPath( hdc );
230     ret = FillPath( hdc );
231     ok( ret, "FillPath failed\n" );
232
233     AbortPath( hdc );
234     BeginPath( hdc );
235     EndPath( hdc );
236     ret = StrokeAndFillPath( hdc );
237     ok( ret, "StrokeAndFillPath failed\n" );
238
239     AbortPath( hdc );
240     BeginPath( hdc );
241     Rectangle( hdc, 1, 1, 10, 10 );  /* region needs some contents */
242     EndPath( hdc );
243     ret = SelectClipPath( hdc, RGN_OR );
244     ok( ret, "SelectClipPath failed\n" );
245
246     AbortPath( hdc );
247     BeginPath( hdc );
248     EndPath( hdc );
249     SetLastError( 0xdeadbeef );
250     ret = CloseFigure( hdc );
251     ok( !ret, "CloseFigure succeeded\n" );
252     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
253         "wrong error %u\n", GetLastError() );
254
255     AbortPath( hdc );
256     BeginPath( hdc );
257     EndPath( hdc );
258     SetLastError( 0xdeadbeef );
259     ret = EndPath( hdc );
260     ok( !ret, "SelectClipPath succeeded\n" );
261     ok( GetLastError() == ERROR_CAN_NOT_COMPLETE || broken(GetLastError() == 0xdeadbeef),
262         "wrong error %u\n", GetLastError() );
263
264     DeleteDC( hdc );
265     DeleteObject( dib );
266 }
267
268 static void test_widenpath(void)
269 {
270     HDC hdc = GetDC(0);
271     HPEN greenPen, narrowPen;
272     POINT pnt[6];
273     INT nSize, ret;
274
275     /* Create a pen to be used in WidenPath */
276     greenPen = CreatePen(PS_SOLID, 10, RGB(0,0,0));
277     SelectObject(hdc, greenPen);
278
279     /* Prepare a path */
280     pnt[0].x = 100;
281     pnt[0].y = 0;
282     pnt[1].x = 200;
283     pnt[1].y = 0;
284     pnt[2].x = 300;
285     pnt[2].y = 100;
286     pnt[3].x = 300;
287     pnt[3].y = 200;
288     pnt[4].x = 200;
289     pnt[4].y = 300;
290     pnt[5].x = 100;
291     pnt[5].y = 300;
292
293     /* Set a polyline path */
294     BeginPath(hdc);
295     Polyline(hdc, pnt, 6);
296     EndPath(hdc);
297
298     /* Widen the polyline path */
299     ok(WidenPath(hdc), "WidenPath fails while widening a poyline path.\n");
300
301     /* Test if WidenPath seems to have done his job */
302     nSize = GetPath(hdc, NULL, NULL, 0);
303     ok(nSize != -1, "GetPath fails after calling WidenPath.\n");
304     ok(nSize > 6, "Path number of points is too low. Should be more than 6 but is %d\n", nSize);
305
306     AbortPath(hdc);
307
308     /* Test WidenPath with an open path (last error only set on Win2k and later) */
309     SetLastError(0xdeadbeef);
310     BeginPath(hdc);
311     ret = WidenPath(hdc);
312     ok(ret == FALSE && (GetLastError() == ERROR_CAN_NOT_COMPLETE || GetLastError() == 0xdeadbeef),
313        "WidenPath fails while widening an open path. Return value is %d, should be %d. Error is %u\n", ret, FALSE, GetLastError());
314
315     AbortPath(hdc);
316
317     /* Test when the pen width is equal to 1. The path should change too */
318     narrowPen = CreatePen(PS_SOLID, 1, RGB(0,0,0));
319     SelectObject(hdc, narrowPen);
320     BeginPath(hdc);
321     Polyline(hdc, pnt, 6);
322     EndPath(hdc);
323     ret = WidenPath(hdc);
324     ok(ret == TRUE, "WidenPath failed: %d\n", GetLastError());
325     nSize = GetPath(hdc, NULL, NULL, 0);
326     ok(nSize > 6, "WidenPath should compute a widened path with a 1px wide pen. Path length is %d, should be more than 6\n", nSize);
327
328     ReleaseDC(0, hdc);
329     return;
330 }
331
332 /*
333  * Tests for GDI drawing functions in paths
334  */
335
336 typedef struct
337 {
338     int x, y;
339     BYTE type;
340
341     /* How many extra entries before this one only on wine
342      * but not on native? */
343     int wine_only_entries_preceding;
344
345     /* 0 - This entry matches on wine.
346      * 1 - This entry corresponds to a single entry on wine that does not match the native entry.
347      * 2 - This entry is currently skipped on wine but present on native. */
348     int todo;
349 } path_test_t;
350
351 /* Helper function to verify that the current path in the given DC matches the expected path.
352  *
353  * We use a "smart" matching algorithm that allows us to detect partial improvements
354  * in conformance. Specifically, two running indices are kept, one through the actual
355  * path and one through the expected path. The actual path index increases unless there is
356  * no match and the todo field of the appropriate path_test_t element is 2. Similarly,
357  * if the wine_entries_preceding field of the appropriate path_test_t element is non-zero,
358  * the expected path index does not increase for that many elements as long as there
359  * is no match. This allows us to todo_wine extra path elements that are present only
360  * on wine but not on native and vice versa.
361  *
362  * Note that if expected_size is zero and the WINETEST_DEBUG environment variable is
363  * greater than 2, the trace() output is a C path_test_t array structure, useful for making
364  * new tests that use this function.
365  */
366 static void ok_path(HDC hdc, const char *path_name, const path_test_t *expected, int expected_size, BOOL todo_size)
367 {
368     static const char *type_string[8] = { "Unknown (0)", "PT_CLOSEFIGURE", "PT_LINETO",
369                                           "PT_LINETO | PT_CLOSEFIGURE", "PT_BEZIERTO",
370                                           "PT_BEZIERTO | PT_CLOSEFIGURE", "PT_MOVETO", "PT_MOVETO | PT_CLOSEFIGURE"};
371     POINT *pnt = NULL;
372     BYTE *types = NULL;
373     int size, numskip,
374         idx = 0, eidx = 0;
375
376     /* Get the path */
377     assert(hdc != 0);
378     size = GetPath(hdc, NULL, NULL, 0);
379     ok(size > 0, "GetPath returned size %d, last error %d\n", size, GetLastError());
380     if (size <= 0)
381     {
382         skip("Cannot perform path comparisons due to failure to retrieve path.\n");
383         return;
384     }
385     pnt = HeapAlloc(GetProcessHeap(), 0, size*sizeof(POINT));
386     assert(pnt != 0);
387     types = HeapAlloc(GetProcessHeap(), 0, size*sizeof(BYTE));
388     assert(types != 0);
389     size = GetPath(hdc, pnt, types, size);
390     assert(size > 0);
391
392     if (todo_size) todo_wine
393         ok(size == expected_size, "Path size %d does not match expected size %d\n", size, expected_size);
394     else
395         ok(size == expected_size, "Path size %d does not match expected size %d\n", size, expected_size);
396
397     if (winetest_debug > 2)
398         trace("static const path_test_t %s[] = {\n", path_name);
399
400     numskip = expected_size ? expected[eidx].wine_only_entries_preceding : 0;
401     while (idx < size && eidx < expected_size)
402     {
403         /* We allow a few pixels fudge in matching X and Y coordinates to account for imprecision in
404          * floating point to integer conversion */
405         BOOL match = (types[idx] == expected[eidx].type) &&
406             (pnt[idx].x >= expected[eidx].x-2 && pnt[idx].x <= expected[eidx].x+2) &&
407             (pnt[idx].y >= expected[eidx].y-2 && pnt[idx].y <= expected[eidx].y+2);
408
409         if (expected[eidx].todo || numskip) todo_wine
410             ok(match, "Expected #%d: %s (%d,%d) but got %s (%d,%d)\n", eidx,
411                type_string[expected[eidx].type], expected[eidx].x, expected[eidx].y,
412                type_string[types[idx]], pnt[idx].x, pnt[idx].y);
413         else
414             ok(match, "Expected #%d: %s (%d,%d) but got %s (%d,%d)\n", eidx,
415                type_string[expected[eidx].type], expected[eidx].x, expected[eidx].y,
416                type_string[types[idx]], pnt[idx].x, pnt[idx].y);
417
418         if (match || expected[eidx].todo != 2)
419         {
420             if (winetest_debug > 2)
421                 trace("    {%d, %d, %s, 0, 0}%s /* %d */\n", pnt[idx].x, pnt[idx].y,
422                       type_string[types[idx]], idx < size-1 ? "," : "};", idx);
423             idx++;
424         }
425         if (match || !numskip--)
426             numskip = expected[++eidx].wine_only_entries_preceding;
427     }
428
429     /* If we are debugging and the actual path is longer than the expected path, make
430      * sure to display the entire path */
431     if (winetest_debug > 2 && idx < size)
432         for (; idx < size; idx++)
433             trace("    {%d, %d, %s, 0, 0}%s /* %d */\n", pnt[idx].x, pnt[idx].y,
434                   type_string[types[idx]], idx < size-1 ? "," : "};", idx);
435
436     HeapFree(GetProcessHeap(), 0, types);
437     HeapFree(GetProcessHeap(), 0, pnt);
438 }
439
440 static const path_test_t arcto_path[] = {
441     {0, 0, PT_MOVETO, 0, 0}, /* 0 */
442     {229, 215, PT_LINETO, 0, 0}, /* 1 */
443     {248, 205, PT_BEZIERTO, 0, 0}, /* 2 */
444     {273, 200, PT_BEZIERTO, 0, 0}, /* 3 */
445     {300, 200, PT_BEZIERTO, 0, 0}, /* 4 */
446     {355, 200, PT_BEZIERTO, 0, 0}, /* 5 */
447     {399, 222, PT_BEZIERTO, 0, 0}, /* 6 */
448     {399, 250, PT_BEZIERTO, 0, 0}, /* 7 */
449     {399, 263, PT_BEZIERTO, 0, 0}, /* 8 */
450     {389, 275, PT_BEZIERTO, 0, 0}, /* 9 */
451     {370, 285, PT_BEZIERTO, 0, 0}, /* 10 */
452     {363, 277, PT_LINETO, 0, 0}, /* 11 */
453     {380, 270, PT_BEZIERTO, 0, 0}, /* 12 */
454     {389, 260, PT_BEZIERTO, 0, 0}, /* 13 */
455     {389, 250, PT_BEZIERTO, 0, 0}, /* 14 */
456     {389, 228, PT_BEZIERTO, 0, 0}, /* 15 */
457     {349, 210, PT_BEZIERTO, 0, 0}, /* 16 */
458     {300, 210, PT_BEZIERTO, 0, 0}, /* 17 */
459     {276, 210, PT_BEZIERTO, 0, 0}, /* 18 */
460     {253, 214, PT_BEZIERTO, 0, 0}, /* 19 */
461     {236, 222, PT_BEZIERTO | PT_CLOSEFIGURE, 0, 0}}; /* 20 */
462
463 static void test_arcto(void)
464 {
465     HDC hdc = GetDC(0);
466
467     BeginPath(hdc);
468     SetArcDirection(hdc, AD_CLOCKWISE);
469     if (!ArcTo(hdc, 200, 200, 400, 300, 200, 200, 400, 300) &&
470         GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
471     {
472         /* ArcTo is only available on Win2k and later */
473         win_skip("ArcTo is not available\n");
474         goto done;
475     }
476     SetArcDirection(hdc, AD_COUNTERCLOCKWISE);
477     ArcTo(hdc, 210, 210, 390, 290, 390, 290, 210, 210);
478     CloseFigure(hdc);
479     EndPath(hdc);
480
481     ok_path(hdc, "arcto_path", arcto_path, sizeof(arcto_path)/sizeof(path_test_t), 0);
482 done:
483     ReleaseDC(0, hdc);
484 }
485
486 static const path_test_t anglearc_path[] = {
487     {0, 0, PT_MOVETO, 0, 0}, /* 0 */
488     {371, 229, PT_LINETO, 0, 0}, /* 1 */
489     {352, 211, PT_BEZIERTO, 0, 0}, /* 2 */
490     {327, 200, PT_BEZIERTO, 0, 0}, /* 3 */
491     {300, 200, PT_BEZIERTO, 0, 0}, /* 4 */
492     {245, 200, PT_BEZIERTO, 0, 0}, /* 5 */
493     {200, 245, PT_BEZIERTO, 0, 0}, /* 6 */
494     {200, 300, PT_BEZIERTO, 0, 0}, /* 7 */
495     {200, 300, PT_BEZIERTO, 0, 0}, /* 8 */
496     {200, 300, PT_BEZIERTO, 0, 0}, /* 9 */
497     {200, 300, PT_BEZIERTO, 0, 0}, /* 10 */
498     {231, 260, PT_LINETO, 0, 0}, /* 11 */
499     {245, 235, PT_BEZIERTO, 0, 0}, /* 12 */
500     {271, 220, PT_BEZIERTO, 0, 0}, /* 13 */
501     {300, 220, PT_BEZIERTO, 0, 0}, /* 14 */
502     {344, 220, PT_BEZIERTO, 0, 0}, /* 15 */
503     {380, 256, PT_BEZIERTO, 0, 0}, /* 16 */
504     {380, 300, PT_BEZIERTO, 0, 0}, /* 17 */
505     {380, 314, PT_BEZIERTO, 0, 0}, /* 18 */
506     {376, 328, PT_BEZIERTO, 0, 0}, /* 19 */
507     {369, 340, PT_BEZIERTO | PT_CLOSEFIGURE, 0, 0}}; /* 20 */
508
509 static void test_anglearc(void)
510 {
511     HDC hdc = GetDC(0);
512     BeginPath(hdc);
513     if (!AngleArc(hdc, 300, 300, 100, 45.0, 135.0) &&
514         GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
515     {
516         /* AngleArc is only available on Win2k and later */
517         win_skip("AngleArc is not available\n");
518         goto done;
519     }
520     AngleArc(hdc, 300, 300, 80, 150.0, -180.0);
521     CloseFigure(hdc);
522     EndPath(hdc);
523
524     ok_path(hdc, "anglearc_path", anglearc_path, sizeof(anglearc_path)/sizeof(path_test_t), 0);
525 done:
526     ReleaseDC(0, hdc);
527 }
528
529 static const path_test_t polydraw_path[] = {
530     {0, 0, PT_MOVETO, 0, 0}, /*0*/
531     {10, 10, PT_LINETO, 0, 0}, /*1*/
532     {10, 15, PT_LINETO | PT_CLOSEFIGURE, 0, 0}, /*2*/
533     {100, 100, PT_MOVETO, 0, 0}, /*3*/
534     {95, 95, PT_LINETO, 0, 0}, /*4*/
535     {10, 10, PT_LINETO, 0, 0}, /*5*/
536     {10, 15, PT_LINETO | PT_CLOSEFIGURE, 0, 0}, /*6*/
537     {100, 100, PT_MOVETO, 0, 0}, /*7*/
538     {15, 15, PT_LINETO, 0, 0}, /*8*/
539     {25, 25, PT_MOVETO, 0, 0}, /*9*/
540     {25, 30, PT_LINETO, 0, 0}, /*10*/
541     {100, 100, PT_MOVETO, 0, 0}, /*11*/
542     {30, 30, PT_BEZIERTO, 0, 0}, /*12*/
543     {30, 35, PT_BEZIERTO, 0, 0}, /*13*/
544     {35, 35, PT_BEZIERTO, 0, 0}, /*14*/
545     {35, 40, PT_LINETO, 0, 0}, /*15*/
546     {40, 40, PT_MOVETO, 0, 0}, /*16*/
547     {40, 45, PT_LINETO, 0, 0}, /*17*/
548     {35, 40, PT_MOVETO, 0, 0}, /*18*/
549     {45, 50, PT_LINETO, 0, 0}, /*19*/
550     {35, 40, PT_MOVETO, 0, 0}, /*20*/
551     {50, 55, PT_LINETO, 0, 0}, /*21*/
552     {45, 50, PT_LINETO, 0, 0}, /*22*/
553     {35, 40, PT_MOVETO, 0, 0}, /*23*/
554     {60, 60, PT_LINETO, 0, 0}, /*24*/
555     {60, 65, PT_MOVETO, 0, 0}, /*25*/
556     {65, 65, PT_LINETO, 0, 0} /*26*/
557     };
558
559 static POINT polydraw_pts[] = {
560     {10, 10}, {10, 15},
561     {15, 15}, {15, 20}, {20, 20}, {20, 25},
562     {25, 25}, {25, 30},
563     {30, 30}, {30, 35}, {35, 35}, {35, 40},
564     {40, 40}, {40, 45}, {45, 45},
565     {45, 50}, {50, 50},
566     {50, 55}, {45, 50}, {55, 60},
567     {60, 60}, {60, 65}, {65, 65}};
568
569 static BYTE polydraw_tps[] =
570     {PT_LINETO, PT_CLOSEFIGURE | PT_LINETO, /* 2 */
571      PT_LINETO, PT_BEZIERTO, PT_LINETO, PT_LINETO, /* 6 */
572      PT_MOVETO, PT_LINETO, /* 8 */
573      PT_BEZIERTO, PT_BEZIERTO, PT_BEZIERTO, PT_LINETO, /* 12 */
574      PT_MOVETO, PT_LINETO, PT_CLOSEFIGURE, /* 15 */
575      PT_LINETO, PT_MOVETO | PT_CLOSEFIGURE, /* 17 */
576      PT_LINETO, PT_LINETO, PT_MOVETO | PT_CLOSEFIGURE, /* 20 */
577      PT_LINETO, PT_MOVETO | PT_LINETO, PT_LINETO}; /* 23 */
578
579 static void test_polydraw(void)
580 {
581     BOOL retb;
582     HDC hdc = GetDC(0);
583     BeginPath(hdc);
584
585     /* closefigure with no previous moveto */
586     if (!(retb = PolyDraw(hdc, polydraw_pts, polydraw_tps, 2)) &&
587         GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
588     {
589         /* PolyDraw is only available on Win2k and later */
590         win_skip("PolyDraw is not available\n");
591         goto done;
592     }
593     expect(TRUE, retb);
594
595     MoveToEx(hdc, 100, 100, NULL);
596     LineTo(hdc, 95, 95);
597     /* closefigure with previous moveto */
598     retb = PolyDraw(hdc, polydraw_pts, polydraw_tps, 2);
599     expect(TRUE, retb);
600     /* bad bezier points */
601     retb = PolyDraw(hdc, &(polydraw_pts[2]), &(polydraw_tps[2]), 4);
602     expect(FALSE, retb);
603     retb = PolyDraw(hdc, &(polydraw_pts[6]), &(polydraw_tps[6]), 4);
604     expect(FALSE, retb);
605     /* good bezier points */
606     retb = PolyDraw(hdc, &(polydraw_pts[8]), &(polydraw_tps[8]), 4);
607     expect(TRUE, retb);
608     /* does lineto or bezierto take precedence? */
609     retb = PolyDraw(hdc, &(polydraw_pts[12]), &(polydraw_tps[12]), 4);
610     expect(FALSE, retb);
611     /* bad point type, has already moved cursor position */
612     retb = PolyDraw(hdc, &(polydraw_pts[15]), &(polydraw_tps[15]), 4);
613     expect(FALSE, retb);
614     /* bad point type, cursor position is moved, but back to its original spot */
615     retb = PolyDraw(hdc, &(polydraw_pts[17]), &(polydraw_tps[17]), 4);
616     expect(FALSE, retb);
617     /* does lineto or moveto take precedence? */
618     retb = PolyDraw(hdc, &(polydraw_pts[20]), &(polydraw_tps[20]), 3);
619     expect(TRUE, retb);
620
621     EndPath(hdc);
622     ok_path(hdc, "polydraw_path", polydraw_path, sizeof(polydraw_path)/sizeof(path_test_t), 0);
623 done:
624     ReleaseDC(0, hdc);
625 }
626
627 static void test_closefigure(void) {
628     int nSize, nSizeWitness;
629     HDC hdc = GetDC(0);
630
631     BeginPath(hdc);
632     MoveToEx(hdc, 95, 95, NULL);
633     LineTo(hdc, 95,  0);
634     LineTo(hdc,  0, 95);
635
636     CloseFigure(hdc);
637     EndPath(hdc);
638     nSize = GetPath(hdc, NULL, NULL, 0);
639
640     AbortPath(hdc);
641
642     BeginPath(hdc);
643     MoveToEx(hdc, 95, 95, NULL);
644     LineTo(hdc, 95,  0);
645     LineTo(hdc,  0, 95);
646
647     EndPath(hdc);
648     nSizeWitness = GetPath(hdc, NULL, NULL, 0);
649
650     /* This test shows CloseFigure does not have to add a point at the end of the path */
651     ok(nSize == nSizeWitness, "Wrong number of points, no point should be added by CloseFigure\n");
652
653     ReleaseDC(0, hdc);
654 }
655
656 static void WINAPI linedda_callback(INT x, INT y, LPARAM lparam)
657 {
658     POINT **pt = (POINT**)lparam;
659     ok((*pt)->x == x && (*pt)->y == y, "point mismatch expect(%d,%d) got(%d,%d)\n",
660        (*pt)->x, (*pt)->y, x, y);
661
662     (*pt)++;
663     return;
664 }
665
666 static void test_linedda(void)
667 {
668     const POINT *pt;
669     static const POINT array_10_20_20_40[] = {{10,20},{10,21},{11,22},{11,23},
670                                               {12,24},{12,25},{13,26},{13,27},
671                                               {14,28},{14,29},{15,30},{15,31},
672                                               {16,32},{16,33},{17,34},{17,35},
673                                               {18,36},{18,37},{19,38},{19,39},
674                                               {-1,-1}};
675     static const POINT array_10_20_20_43[] = {{10,20},{10,21},{11,22},{11,23},
676                                               {12,24},{12,25},{13,26},{13,27},
677                                               {13,28},{14,29},{14,30},{15,31},
678                                               {15,32},{16,33},{16,34},{17,35},
679                                               {17,36},{17,37},{18,38},{18,39},
680                                               {19,40},{19,41},{20,42},{-1,-1}};
681
682     static const POINT array_10_20_10_20[] = {{-1,-1}};
683     static const POINT array_10_20_11_27[] = {{10,20},{10,21},{10,22},{10,23},
684                                               {11,24},{11,25},{11,26},{-1,-1}};
685
686     static const POINT array_20_43_10_20[] = {{20,43},{20,42},{19,41},{19,40},
687                                               {18,39},{18,38},{17,37},{17,36},
688                                               {17,35},{16,34},{16,33},{15,32},
689                                               {15,31},{14,30},{14,29},{13,28},
690                                               {13,27},{13,26},{12,25},{12,24},
691                                               {11,23},{11,22},{10,21},{-1,-1}};
692
693     static const POINT array_20_20_10_43[] = {{20,20},{20,21},{19,22},{19,23},
694                                               {18,24},{18,25},{17,26},{17,27},
695                                               {17,28},{16,29},{16,30},{15,31},
696                                               {15,32},{14,33},{14,34},{13,35},
697                                               {13,36},{13,37},{12,38},{12,39},
698                                               {11,40},{11,41},{10,42},{-1,-1}};
699
700     static const POINT array_20_20_43_10[] = {{20,20},{21,20},{22,19},{23,19},
701                                               {24,18},{25,18},{26,17},{27,17},
702                                               {28,17},{29,16},{30,16},{31,15},
703                                               {32,15},{33,14},{34,14},{35,13},
704                                               {36,13},{37,13},{38,12},{39,12},
705                                               {40,11},{41,11},{42,10},{-1,-1}};
706
707
708     pt = array_10_20_20_40;
709     LineDDA(10, 20, 20, 40, linedda_callback, (LPARAM)&pt);
710     ok(pt->x == -1 && pt->y == -1, "didn't find terminator\n");
711
712     pt = array_10_20_20_43;
713     LineDDA(10, 20, 20, 43, linedda_callback, (LPARAM)&pt);
714     ok(pt->x == -1 && pt->y == -1, "didn't find terminator\n");
715
716     pt = array_10_20_10_20;
717     LineDDA(10, 20, 10, 20, linedda_callback, (LPARAM)&pt);
718     ok(pt->x == -1 && pt->y == -1, "didn't find terminator\n");
719
720     pt = array_10_20_11_27;
721     LineDDA(10, 20, 11, 27, linedda_callback, (LPARAM)&pt);
722     ok(pt->x == -1 && pt->y == -1, "didn't find terminator\n");
723
724     pt = array_20_43_10_20;
725     LineDDA(20, 43, 10, 20, linedda_callback, (LPARAM)&pt);
726     ok(pt->x == -1 && pt->y == -1, "didn't find terminator\n");
727
728     pt = array_20_20_10_43;
729     LineDDA(20, 20, 10, 43, linedda_callback, (LPARAM)&pt);
730     ok(pt->x == -1 && pt->y == -1, "didn't find terminator\n");
731
732     pt = array_20_20_43_10;
733     LineDDA(20, 20, 43, 10, linedda_callback, (LPARAM)&pt);
734     ok(pt->x == -1 && pt->y == -1, "didn't find terminator\n");
735 }
736
737 START_TEST(path)
738 {
739     test_path_state();
740     test_widenpath();
741     test_arcto();
742     test_anglearc();
743     test_polydraw();
744     test_closefigure();
745     test_linedda();
746 }