blob: 5ae37791725adc31fc2da3359be10e40d0ee9ad8 [file] [log] [blame]
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001/*
2 * Graphics paths (BeginPath, EndPath etc.)
3 *
Alexandre Julliard60ce85c1998-02-01 18:33:27 +00004 * Copyright 1997, 1998 Martin Boehme
Huw D M Daviesb8e94b61999-12-20 04:14:48 +00005 * 1999 Huw D M Davies
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00006 */
7
8#include <assert.h>
Alexandre Julliard60ce85c1998-02-01 18:33:27 +00009#include <math.h>
Jeff Garzikc3e1f721999-02-19 15:42:11 +000010#include <string.h>
Robert Pouliot0a997521999-04-16 06:55:26 +000011#include "config.h"
12#if defined(HAVE_FLOAT_H)
13#include <float.h>
14#endif
Alexandre Julliard44ed71f1997-12-21 19:17:50 +000015
Marcus Meissner04c3e1d1999-02-19 10:37:02 +000016#include "winbase.h"
17#include "wingdi.h"
Alexandre Julliard44ed71f1997-12-21 19:17:50 +000018#include "winerror.h"
19
20#include "dc.h"
Alexandre Julliard61fece01999-06-26 19:09:08 +000021#include "debugtools.h"
Alexandre Julliard44ed71f1997-12-21 19:17:50 +000022#include "path.h"
23
Patrik Stridvallb4b9fae1999-04-19 14:56:29 +000024DEFAULT_DEBUG_CHANNEL(gdi)
25
Alexandre Julliard44ed71f1997-12-21 19:17:50 +000026/* Notes on the implementation
27 *
28 * The implementation is based on dynamically resizable arrays of points and
29 * flags. I dithered for a bit before deciding on this implementation, and
30 * I had even done a bit of work on a linked list version before switching
31 * to arrays. It's a bit of a tradeoff. When you use linked lists, the
32 * implementation of FlattenPath is easier, because you can rip the
33 * PT_BEZIERTO entries out of the middle of the list and link the
34 * corresponding PT_LINETO entries in. However, when you use arrays,
35 * PathToRegion becomes easier, since you can essentially just pass your array
36 * of points to CreatePolyPolygonRgn. Also, if I'd used linked lists, I would
37 * have had the extra effort of creating a chunk-based allocation scheme
38 * in order to use memory effectively. That's why I finally decided to use
39 * arrays. Note by the way that the array based implementation has the same
40 * linear time complexity that linked lists would have since the arrays grow
41 * exponentially.
42 *
43 * The points are stored in the path in device coordinates. This is
44 * consistent with the way Windows does things (for instance, see the Win32
45 * SDK documentation for GetPath).
46 *
47 * The word "stroke" appears in several places (e.g. in the flag
48 * GdiPath.newStroke). A stroke consists of a PT_MOVETO followed by one or
49 * more PT_LINETOs or PT_BEZIERTOs, up to, but not including, the next
50 * PT_MOVETO. Note that this is not the same as the definition of a figure;
51 * a figure can contain several strokes.
52 *
53 * I modified the drawing functions (MoveTo, LineTo etc.) to test whether
54 * the path is open and to call the corresponding function in path.c if this
55 * is the case. A more elegant approach would be to modify the function
56 * pointers in the DC_FUNCTIONS structure; however, this would be a lot more
57 * complex. Also, the performance degradation caused by my approach in the
58 * case where no path is open is so small that it cannot be measured.
59 *
60 * Martin Boehme
61 */
62
63/* FIXME: A lot of stuff isn't implemented yet. There is much more to come. */
64
65#define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
66#define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
67#define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
68
69
Niels Kristian Bech Jensenc9742b32000-03-28 20:44:59 +000070static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode,
Alexandre Julliarda3960291999-02-26 11:11:13 +000071 HRGN *pHrgn);
Alexandre Julliard44ed71f1997-12-21 19:17:50 +000072static void PATH_EmptyPath(GdiPath *pPath);
Alexandre Julliarda3960291999-02-26 11:11:13 +000073static BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint,
Alexandre Julliarddadf78f1998-05-17 17:13:43 +000074 BYTE flags);
Alexandre Julliarda3960291999-02-26 11:11:13 +000075static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries);
76static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath);
77static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
78 double angleStart, double angleEnd, BOOL addMoveTo);
Alexandre Julliarddadf78f1998-05-17 17:13:43 +000079static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
Alexandre Julliarda3960291999-02-26 11:11:13 +000080 double y, POINT *pPoint);
Alexandre Julliarddadf78f1998-05-17 17:13:43 +000081static void PATH_NormalizePoint(FLOAT_POINT corners[], const FLOAT_POINT
82 *pPoint, double *pX, double *pY);
Alexandre Julliard60ce85c1998-02-01 18:33:27 +000083
84
85/***********************************************************************
86 * BeginPath16 (GDI.512)
87 */
88BOOL16 WINAPI BeginPath16(HDC16 hdc)
89{
Alexandre Julliarda3960291999-02-26 11:11:13 +000090 return (BOOL16)BeginPath((HDC)hdc);
Alexandre Julliard60ce85c1998-02-01 18:33:27 +000091}
92
Alexandre Julliard44ed71f1997-12-21 19:17:50 +000093
94/***********************************************************************
Huw D M Daviesf0f8da51999-12-05 23:54:02 +000095 * BeginPath (GDI32.9)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +000096 */
Alexandre Julliarda3960291999-02-26 11:11:13 +000097BOOL WINAPI BeginPath(HDC hdc)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +000098{
Huw D M Daviesf0f8da51999-12-05 23:54:02 +000099 DC *dc = DC_GetDCPtr( hdc );
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000100 GdiPath *pPath;
101
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000102 if(!dc) {
103 SetLastError(ERROR_INVALID_HANDLE);
104 return FALSE;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000105 }
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000106
107 if(dc->funcs->pBeginPath)
108 return dc->funcs->pBeginPath(dc);
109
110 pPath = &dc->w.path;
111
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000112 /* If path is already open, do nothing */
113 if(pPath->state==PATH_Open)
114 return TRUE;
115
116 /* Make sure that path is empty */
117 PATH_EmptyPath(pPath);
118
119 /* Initialize variables for new path */
120 pPath->newStroke=TRUE;
121 pPath->state=PATH_Open;
122
123 return TRUE;
124}
125
126
127/***********************************************************************
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000128 * EndPath16 (GDI.514)
129 */
130BOOL16 WINAPI EndPath16(HDC16 hdc)
131{
Alexandre Julliarda3960291999-02-26 11:11:13 +0000132 return (BOOL16)EndPath((HDC)hdc);
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000133}
134
135
136/***********************************************************************
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000137 * EndPath (GDI32.78)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000138 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000139BOOL WINAPI EndPath(HDC hdc)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000140{
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000141 DC *dc = DC_GetDCPtr( hdc );
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000142 GdiPath *pPath;
143
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000144 if(!dc) {
145 SetLastError(ERROR_INVALID_HANDLE);
146 return FALSE;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000147 }
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000148
149 if(dc->funcs->pEndPath)
150 return dc->funcs->pEndPath(dc);
151
152 pPath = &dc->w.path;
153
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000154 /* Check that path is currently being constructed */
155 if(pPath->state!=PATH_Open)
156 {
157 SetLastError(ERROR_CAN_NOT_COMPLETE);
158 return FALSE;
159 }
160
161 /* Set flag to indicate that path is finished */
162 pPath->state=PATH_Closed;
163
164 return TRUE;
165}
166
167
168/***********************************************************************
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000169 * AbortPath16 (GDI.511)
170 */
171BOOL16 WINAPI AbortPath16(HDC16 hdc)
172{
Alexandre Julliarda3960291999-02-26 11:11:13 +0000173 return (BOOL16)AbortPath((HDC)hdc);
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000174}
175
176
Alexandre Julliard46ea8b31998-05-03 19:01:20 +0000177/******************************************************************************
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000178 * AbortPath [GDI32.1]
Alexandre Julliard46ea8b31998-05-03 19:01:20 +0000179 * Closes and discards paths from device context
180 *
181 * NOTES
182 * Check that SetLastError is being called correctly
183 *
184 * PARAMS
185 * hdc [I] Handle to device context
186 *
187 * RETURNS STD
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000188 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000189BOOL WINAPI AbortPath( HDC hdc )
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000190{
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000191 DC *dc = DC_GetDCPtr( hdc );
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000192 GdiPath *pPath;
193
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000194 if(!dc) {
195 SetLastError(ERROR_INVALID_HANDLE);
196 return FALSE;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000197 }
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000198
199 if(dc->funcs->pAbortPath)
200 return dc->funcs->pAbortPath(dc);
201
202 pPath = &dc->w.path;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000203
204 /* Remove all entries from the path */
205 PATH_EmptyPath(pPath);
206
207 return TRUE;
208}
209
210
211/***********************************************************************
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000212 * CloseFigure16 (GDI.513)
213 */
214BOOL16 WINAPI CloseFigure16(HDC16 hdc)
215{
Alexandre Julliarda3960291999-02-26 11:11:13 +0000216 return (BOOL16)CloseFigure((HDC)hdc);
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000217}
218
219
220/***********************************************************************
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000221 * CloseFigure (GDI32.16)
Alexandre Julliard829fe321998-07-26 14:27:39 +0000222 *
223 * FIXME: Check that SetLastError is being called correctly
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000224 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000225BOOL WINAPI CloseFigure(HDC hdc)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000226{
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000227 DC *dc = DC_GetDCPtr( hdc );
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000228 GdiPath *pPath;
229
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000230 if(!dc) {
231 SetLastError(ERROR_INVALID_HANDLE);
232 return FALSE;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000233 }
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000234
235 if(dc->funcs->pCloseFigure)
236 return dc->funcs->pCloseFigure(dc);
237
238 pPath = &dc->w.path;
239
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000240 /* Check that path is open */
241 if(pPath->state!=PATH_Open)
242 {
243 SetLastError(ERROR_CAN_NOT_COMPLETE);
244 return FALSE;
245 }
246
247 /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
248 if(pPath->numEntriesUsed)
249 {
250 pPath->pFlags[pPath->numEntriesUsed-1]|=PT_CLOSEFIGURE;
251 pPath->newStroke=TRUE;
252 }
253
254 return TRUE;
255}
256
257
258/***********************************************************************
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000259 * GetPath16 (GDI.517)
260 */
261INT16 WINAPI GetPath16(HDC16 hdc, LPPOINT16 pPoints, LPBYTE pTypes,
262 INT16 nSize)
263{
Alexandre Julliard61fece01999-06-26 19:09:08 +0000264 FIXME("(%d,%p,%p): stub\n",hdc,pPoints,pTypes);
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000265
266 return 0;
267}
268
269
270/***********************************************************************
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000271 * GetPath (GDI32.210)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000272 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000273INT WINAPI GetPath(HDC hdc, LPPOINT pPoints, LPBYTE pTypes,
274 INT nSize)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000275{
276 GdiPath *pPath;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000277
278 /* Get pointer to path */
279 if(!PATH_GetPathFromHDC(hdc, &pPath))
280 {
281 SetLastError(ERROR_INVALID_PARAMETER);
282 return -1;
283 }
284
285 /* Check that path is closed */
286 if(pPath->state!=PATH_Closed)
287 {
288 SetLastError(ERROR_CAN_NOT_COMPLETE);
289 return -1;
290 }
291
292 if(nSize==0)
293 return pPath->numEntriesUsed;
294 else if(nSize<pPath->numEntriesUsed)
295 {
296 SetLastError(ERROR_INVALID_PARAMETER);
297 return -1;
298 }
299 else
300 {
Alexandre Julliarda3960291999-02-26 11:11:13 +0000301 memcpy(pPoints, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000302 memcpy(pTypes, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
303
304 /* Convert the points to logical coordinates */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000305 if(!DPtoLP(hdc, pPoints, pPath->numEntriesUsed))
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000306 {
307 /* FIXME: Is this the correct value? */
308 SetLastError(ERROR_CAN_NOT_COMPLETE);
309 return -1;
310 }
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000311
312 return pPath->numEntriesUsed;
313 }
314}
315
Patrik Stridvall900290a1999-10-24 21:32:43 +0000316/***********************************************************************
317 * PathToRegion16 (GDI.518)
318 */
319HRGN16 WINAPI PathToRegion16(HDC16 hdc)
320{
321 return (HRGN16) PathToRegion((HDC) hdc);
322}
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000323
324/***********************************************************************
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000325 * PathToRegion (GDI32.261)
Alexandre Julliard829fe321998-07-26 14:27:39 +0000326 *
327 * FIXME
328 * Check that SetLastError is being called correctly
329 *
330 * The documentation does not state this explicitly, but a test under Windows
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000331 * shows that the region which is returned should be in device coordinates.
332 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000333HRGN WINAPI PathToRegion(HDC hdc)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000334{
335 GdiPath *pPath;
Alexandre Julliarda3960291999-02-26 11:11:13 +0000336 HRGN hrgnRval;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000337
338 /* Get pointer to path */
339 if(!PATH_GetPathFromHDC(hdc, &pPath))
340 {
341 SetLastError(ERROR_INVALID_PARAMETER);
342 return 0;
343 }
344
345 /* Check that path is closed */
346 if(pPath->state!=PATH_Closed)
347 {
348 SetLastError(ERROR_CAN_NOT_COMPLETE);
349 return 0;
350 }
351
352 /* FIXME: Should we empty the path even if conversion failed? */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000353 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnRval))
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000354 PATH_EmptyPath(pPath);
355 else
356 hrgnRval=0;
357
358 return hrgnRval;
359}
360
Huw D M Daviesa3d3ff82000-04-18 11:54:12 +0000361static BOOL PATH_FillPath(HDC hdc, GdiPath *pPath)
Patrik Stridvall900290a1999-10-24 21:32:43 +0000362{
Alexandre Julliarda3960291999-02-26 11:11:13 +0000363 INT mapMode, graphicsMode;
364 SIZE ptViewportExt, ptWindowExt;
365 POINT ptViewportOrg, ptWindowOrg;
Huw D M Daviesa3d3ff82000-04-18 11:54:12 +0000366 XFORM xform;
Alexandre Julliarda3960291999-02-26 11:11:13 +0000367 HRGN hrgn;
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000368
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000369 /* Check that path is closed */
370 if(pPath->state!=PATH_Closed)
371 {
372 SetLastError(ERROR_CAN_NOT_COMPLETE);
373 return FALSE;
374 }
375
376 /* Construct a region from the path and fill it */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000377 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgn))
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000378 {
379 /* Since PaintRgn interprets the region as being in logical coordinates
380 * but the points we store for the path are already in device
381 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000382 * Using SaveDC to save information about the mapping mode / world
383 * transform would be easier but would require more overhead, especially
384 * now that SaveDC saves the current path.
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000385 */
386
387 /* Save the information about the old mapping mode */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000388 mapMode=GetMapMode(hdc);
389 GetViewportExtEx(hdc, &ptViewportExt);
390 GetViewportOrgEx(hdc, &ptViewportOrg);
391 GetWindowExtEx(hdc, &ptWindowExt);
392 GetWindowOrgEx(hdc, &ptWindowOrg);
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000393
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000394 /* Save world transform
395 * NB: The Windows documentation on world transforms would lead one to
396 * believe that this has to be done only in GM_ADVANCED; however, my
397 * tests show that resetting the graphics mode to GM_COMPATIBLE does
398 * not reset the world transform.
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000399 */
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000400 GetWorldTransform(hdc, &xform);
401
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000402 /* Set MM_TEXT */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000403 SetMapMode(hdc, MM_TEXT);
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000404 SetViewportOrgEx(hdc, 0, 0, NULL);
405 SetWindowOrgEx(hdc, 0, 0, NULL);
406
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000407 /* Paint the region */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000408 PaintRgn(hdc, hrgn);
Huw D M Daviesa3d3ff82000-04-18 11:54:12 +0000409 DeleteObject(hrgn);
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000410 /* Restore the old mapping mode */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000411 SetMapMode(hdc, mapMode);
412 SetViewportExtEx(hdc, ptViewportExt.cx, ptViewportExt.cy, NULL);
413 SetViewportOrgEx(hdc, ptViewportOrg.x, ptViewportOrg.y, NULL);
414 SetWindowExtEx(hdc, ptWindowExt.cx, ptWindowExt.cy, NULL);
415 SetWindowOrgEx(hdc, ptWindowOrg.x, ptWindowOrg.y, NULL);
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000416
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000417 /* Go to GM_ADVANCED temporarily to restore the world transform */
418 graphicsMode=GetGraphicsMode(hdc);
419 SetGraphicsMode(hdc, GM_ADVANCED);
420 SetWorldTransform(hdc, &xform);
421 SetGraphicsMode(hdc, graphicsMode);
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000422 return TRUE;
423 }
Huw D M Daviesa3d3ff82000-04-18 11:54:12 +0000424 return FALSE;
425}
426
427/***********************************************************************
428 * FillPath16 (GDI.515)
429 */
430BOOL16 WINAPI FillPath16(HDC16 hdc)
431{
432 return (BOOL16) FillPath((HDC) hdc);
433}
434
435/***********************************************************************
436 * FillPath (GDI32.100)
437 *
438 * FIXME
439 * Check that SetLastError is being called correctly
440 */
441BOOL WINAPI FillPath(HDC hdc)
442{
443 DC *dc = DC_GetDCPtr( hdc );
444
445 if(!dc) {
446 SetLastError(ERROR_INVALID_HANDLE);
447 return FALSE;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000448 }
Huw D M Daviesa3d3ff82000-04-18 11:54:12 +0000449
450 if(dc->funcs->pFillPath)
451 return dc->funcs->pFillPath(dc);
452
453 if(!PATH_FillPath(hdc, &dc->w.path))
454 return FALSE;
455
456 /* FIXME: Should the path be emptied even if conversion failed? */
457 PATH_EmptyPath(&dc->w.path);
458 return TRUE;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000459}
460
Patrik Stridvall900290a1999-10-24 21:32:43 +0000461/***********************************************************************
462 * SelectClipPath16 (GDI.519)
463 */
464BOOL16 WINAPI SelectClipPath16(HDC16 hdc, INT16 iMode)
465{
466 return (BOOL16) SelectClipPath((HDC) hdc, iMode);
467}
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000468
469/***********************************************************************
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000470 * SelectClipPath (GDI32.296)
Alexandre Julliard829fe321998-07-26 14:27:39 +0000471 * FIXME
472 * Check that SetLastError is being called correctly
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000473 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000474BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000475{
476 GdiPath *pPath;
Alexandre Julliarda3960291999-02-26 11:11:13 +0000477 HRGN hrgnPath;
478 BOOL success;
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000479 DC *dc = DC_GetDCPtr( hdc );
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000480
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000481 if(!dc) {
482 SetLastError(ERROR_INVALID_HANDLE);
483 return FALSE;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000484 }
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000485
486 if(dc->funcs->pSelectClipPath)
487 return dc->funcs->pSelectClipPath(dc, iMode);
488
489 pPath = &dc->w.path;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000490
491 /* Check that path is closed */
492 if(pPath->state!=PATH_Closed)
493 {
494 SetLastError(ERROR_CAN_NOT_COMPLETE);
495 return FALSE;
496 }
497
498 /* Construct a region from the path */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000499 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnPath))
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000500 {
Ulrich Weigandd4663661998-10-11 18:47:02 +0000501 success = ExtSelectClipRgn( hdc, hrgnPath, iMode ) != ERROR;
Alexandre Julliarda3960291999-02-26 11:11:13 +0000502 DeleteObject(hrgnPath);
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000503
504 /* Empty the path */
505 if(success)
506 PATH_EmptyPath(pPath);
507 /* FIXME: Should this function delete the path even if it failed? */
508
509 return success;
510 }
511 else
512 return FALSE;
513}
514
515
516/***********************************************************************
517 * Exported functions
518 */
519
520/* PATH_InitGdiPath
521 *
522 * Initializes the GdiPath structure.
523 */
524void PATH_InitGdiPath(GdiPath *pPath)
525{
526 assert(pPath!=NULL);
527
528 pPath->state=PATH_Null;
529 pPath->pPoints=NULL;
530 pPath->pFlags=NULL;
531 pPath->numEntriesUsed=0;
532 pPath->numEntriesAllocated=0;
533}
534
535/* PATH_DestroyGdiPath
536 *
537 * Destroys a GdiPath structure (frees the memory in the arrays).
538 */
539void PATH_DestroyGdiPath(GdiPath *pPath)
540{
541 assert(pPath!=NULL);
542
Rob Farnumebe78312000-05-05 18:25:29 +0000543 if (pPath->pPoints) HeapFree( GetProcessHeap(), 0, pPath->pPoints );
544 if (pPath->pFlags) HeapFree( GetProcessHeap(), 0, pPath->pFlags );
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000545}
546
547/* PATH_AssignGdiPath
548 *
549 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
550 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
551 * not just the pointers. Since this means that the arrays in pPathDest may
552 * need to be resized, pPathDest should have been initialized using
553 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
554 * not a copy constructor).
555 * Returns TRUE if successful, else FALSE.
556 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000557BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000558{
559 assert(pPathDest!=NULL && pPathSrc!=NULL);
560
561 /* Make sure destination arrays are big enough */
562 if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
563 return FALSE;
564
565 /* Perform the copy operation */
566 memcpy(pPathDest->pPoints, pPathSrc->pPoints,
Alexandre Julliarda3960291999-02-26 11:11:13 +0000567 sizeof(POINT)*pPathSrc->numEntriesUsed);
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000568 memcpy(pPathDest->pFlags, pPathSrc->pFlags,
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000569 sizeof(BYTE)*pPathSrc->numEntriesUsed);
570
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000571 pPathDest->state=pPathSrc->state;
572 pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
573 pPathDest->newStroke=pPathSrc->newStroke;
574
575 return TRUE;
576}
577
578/* PATH_MoveTo
579 *
580 * Should be called when a MoveTo is performed on a DC that has an
581 * open path. This starts a new stroke. Returns TRUE if successful, else
582 * FALSE.
583 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000584BOOL PATH_MoveTo(HDC hdc)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000585{
586 GdiPath *pPath;
587
588 /* Get pointer to path */
589 if(!PATH_GetPathFromHDC(hdc, &pPath))
590 return FALSE;
591
592 /* Check that path is open */
593 if(pPath->state!=PATH_Open)
594 /* FIXME: Do we have to call SetLastError? */
595 return FALSE;
596
597 /* Start a new stroke */
598 pPath->newStroke=TRUE;
599
600 return TRUE;
601}
602
603/* PATH_LineTo
604 *
605 * Should be called when a LineTo is performed on a DC that has an
606 * open path. This adds a PT_LINETO entry to the path (and possibly
607 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
608 * Returns TRUE if successful, else FALSE.
609 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000610BOOL PATH_LineTo(HDC hdc, INT x, INT y)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000611{
612 GdiPath *pPath;
Alexandre Julliarda3960291999-02-26 11:11:13 +0000613 POINT point, pointCurPos;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000614
615 /* Get pointer to path */
616 if(!PATH_GetPathFromHDC(hdc, &pPath))
617 return FALSE;
618
619 /* Check that path is open */
620 if(pPath->state!=PATH_Open)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000621 return FALSE;
622
623 /* Convert point to device coordinates */
624 point.x=x;
625 point.y=y;
Alexandre Julliarda3960291999-02-26 11:11:13 +0000626 if(!LPtoDP(hdc, &point, 1))
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000627 return FALSE;
628
629 /* Add a PT_MOVETO if necessary */
630 if(pPath->newStroke)
631 {
632 pPath->newStroke=FALSE;
Alexandre Julliarda3960291999-02-26 11:11:13 +0000633 if(!GetCurrentPositionEx(hdc, &pointCurPos) ||
634 !LPtoDP(hdc, &pointCurPos, 1))
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000635 return FALSE;
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000636 if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000637 return FALSE;
638 }
639
640 /* Add a PT_LINETO entry */
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000641 return PATH_AddEntry(pPath, &point, PT_LINETO);
642}
643
644/* PATH_Rectangle
645 *
646 * Should be called when a call to Rectangle is performed on a DC that has
647 * an open path. Returns TRUE if successful, else FALSE.
648 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000649BOOL PATH_Rectangle(HDC hdc, INT x1, INT y1, INT x2, INT y2)
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000650{
651 GdiPath *pPath;
Alexandre Julliarda3960291999-02-26 11:11:13 +0000652 POINT corners[2], pointTemp;
653 INT temp;
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000654
655 /* Get pointer to path */
656 if(!PATH_GetPathFromHDC(hdc, &pPath))
657 return FALSE;
658
659 /* Check that path is open */
660 if(pPath->state!=PATH_Open)
661 return FALSE;
662
663 /* Convert points to device coordinates */
664 corners[0].x=x1;
665 corners[0].y=y1;
666 corners[1].x=x2;
667 corners[1].y=y2;
Alexandre Julliarda3960291999-02-26 11:11:13 +0000668 if(!LPtoDP(hdc, corners, 2))
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000669 return FALSE;
670
671 /* Make sure first corner is top left and second corner is bottom right */
672 if(corners[0].x>corners[1].x)
673 {
674 temp=corners[0].x;
675 corners[0].x=corners[1].x;
676 corners[1].x=temp;
677 }
678 if(corners[0].y>corners[1].y)
679 {
680 temp=corners[0].y;
681 corners[0].y=corners[1].y;
682 corners[1].y=temp;
683 }
684
685 /* In GM_COMPATIBLE, don't include bottom and right edges */
686 if(GetGraphicsMode(hdc)==GM_COMPATIBLE)
687 {
688 corners[1].x--;
689 corners[1].y--;
690 }
691
692 /* Close any previous figure */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000693 if(!CloseFigure(hdc))
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000694 {
695 /* The CloseFigure call shouldn't have failed */
696 assert(FALSE);
697 return FALSE;
698 }
699
700 /* Add four points to the path */
701 pointTemp.x=corners[1].x;
702 pointTemp.y=corners[0].y;
703 if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
704 return FALSE;
705 if(!PATH_AddEntry(pPath, corners, PT_LINETO))
706 return FALSE;
707 pointTemp.x=corners[0].x;
708 pointTemp.y=corners[1].y;
709 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
710 return FALSE;
711 if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
712 return FALSE;
713
714 /* Close the rectangle figure */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000715 if(!CloseFigure(hdc))
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000716 {
717 /* The CloseFigure call shouldn't have failed */
718 assert(FALSE);
719 return FALSE;
720 }
721
722 return TRUE;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000723}
724
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000725/* PATH_Ellipse
726 *
727 * Should be called when a call to Ellipse is performed on a DC that has
728 * an open path. This adds four Bezier splines representing the ellipse
729 * to the path. Returns TRUE if successful, else FALSE.
730 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000731BOOL PATH_Ellipse(HDC hdc, INT x1, INT y1, INT x2, INT y2)
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000732{
Marcus Meissner73458b01998-12-26 12:54:29 +0000733 /* TODO: This should probably be revised to call PATH_AngleArc */
734 /* (once it exists) */
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000735 return PATH_Arc(hdc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2);
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000736}
737
738/* PATH_Arc
739 *
740 * Should be called when a call to Arc is performed on a DC that has
741 * an open path. This adds up to five Bezier splines representing the arc
742 * to the path. Returns TRUE if successful, else FALSE.
743 */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000744BOOL PATH_Arc(HDC hdc, INT x1, INT y1, INT x2, INT y2,
745 INT xStart, INT yStart, INT xEnd, INT yEnd)
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000746{
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000747 GdiPath *pPath;
748 DC *pDC;
749 double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
750 /* Initialize angleEndQuadrant to silence gcc's warning */
751 double x, y;
752 FLOAT_POINT corners[2], pointStart, pointEnd;
Alexandre Julliarda3960291999-02-26 11:11:13 +0000753 BOOL start, end;
754 INT temp;
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000755
756 /* FIXME: This function should check for all possible error returns */
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000757 /* FIXME: Do we have to respect newStroke? */
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000758
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000759 /* Get pointer to DC */
760 pDC=DC_GetDCPtr(hdc);
761 if(pDC==NULL)
762 return FALSE;
763
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000764 /* Get pointer to path */
765 if(!PATH_GetPathFromHDC(hdc, &pPath))
766 return FALSE;
767
768 /* Check that path is open */
769 if(pPath->state!=PATH_Open)
770 return FALSE;
771
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000772 /* FIXME: Do we have to close the current figure? */
773
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000774 /* Check for zero height / width */
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000775 /* FIXME: Only in GM_COMPATIBLE? */
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000776 if(x1==x2 || y1==y2)
777 return TRUE;
778
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000779 /* Convert points to device coordinates */
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000780 corners[0].x=(FLOAT)x1;
781 corners[0].y=(FLOAT)y1;
782 corners[1].x=(FLOAT)x2;
783 corners[1].y=(FLOAT)y2;
784 pointStart.x=(FLOAT)xStart;
785 pointStart.y=(FLOAT)yStart;
786 pointEnd.x=(FLOAT)xEnd;
787 pointEnd.y=(FLOAT)yEnd;
788 INTERNAL_LPTODP_FLOAT(pDC, corners);
789 INTERNAL_LPTODP_FLOAT(pDC, corners+1);
790 INTERNAL_LPTODP_FLOAT(pDC, &pointStart);
791 INTERNAL_LPTODP_FLOAT(pDC, &pointEnd);
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000792
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000793 /* Make sure first corner is top left and second corner is bottom right */
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000794 if(corners[0].x>corners[1].x)
795 {
796 temp=corners[0].x;
797 corners[0].x=corners[1].x;
798 corners[1].x=temp;
799 }
800 if(corners[0].y>corners[1].y)
801 {
802 temp=corners[0].y;
803 corners[0].y=corners[1].y;
804 corners[1].y=temp;
805 }
806
807 /* Compute start and end angle */
808 PATH_NormalizePoint(corners, &pointStart, &x, &y);
809 angleStart=atan2(y, x);
810 PATH_NormalizePoint(corners, &pointEnd, &x, &y);
811 angleEnd=atan2(y, x);
812
813 /* Make sure the end angle is "on the right side" of the start angle */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000814 if(GetArcDirection(hdc)==AD_CLOCKWISE)
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000815 {
816 if(angleEnd<=angleStart)
817 {
818 angleEnd+=2*M_PI;
819 assert(angleEnd>=angleStart);
820 }
821 }
822 else
823 {
824 if(angleEnd>=angleStart)
825 {
826 angleEnd-=2*M_PI;
827 assert(angleEnd<=angleStart);
828 }
829 }
830
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000831 /* In GM_COMPATIBLE, don't include bottom and right edges */
832 if(GetGraphicsMode(hdc)==GM_COMPATIBLE)
833 {
834 corners[1].x--;
835 corners[1].y--;
836 }
837
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000838 /* Add the arc to the path with one Bezier spline per quadrant that the
839 * arc spans */
840 start=TRUE;
841 end=FALSE;
842 do
843 {
844 /* Determine the start and end angles for this quadrant */
845 if(start)
846 {
847 angleStartQuadrant=angleStart;
Alexandre Julliarda3960291999-02-26 11:11:13 +0000848 if(GetArcDirection(hdc)==AD_CLOCKWISE)
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000849 angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
850 else
851 angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
852 }
853 else
854 {
855 angleStartQuadrant=angleEndQuadrant;
Alexandre Julliarda3960291999-02-26 11:11:13 +0000856 if(GetArcDirection(hdc)==AD_CLOCKWISE)
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000857 angleEndQuadrant+=M_PI_2;
858 else
859 angleEndQuadrant-=M_PI_2;
860 }
861
862 /* Have we reached the last part of the arc? */
Alexandre Julliarda3960291999-02-26 11:11:13 +0000863 if((GetArcDirection(hdc)==AD_CLOCKWISE &&
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000864 angleEnd<angleEndQuadrant) ||
Alexandre Julliarda3960291999-02-26 11:11:13 +0000865 (GetArcDirection(hdc)==AD_COUNTERCLOCKWISE &&
Alexandre Julliarddadf78f1998-05-17 17:13:43 +0000866 angleEnd>angleEndQuadrant))
Alexandre Julliard60ce85c1998-02-01 18:33:27 +0000867 {
868 /* Adjust the end angle for this quadrant */
869 angleEndQuadrant=angleEnd;
870 end=TRUE;
871 }
872
873 /* Add the Bezier spline to the path */
874 PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant,
875 start);
876 start=FALSE;
877 } while(!end);
878
879 return TRUE;
880}
Alexandre Julliard44ed71f1997-12-21 19:17:50 +0000881
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000882BOOL PATH_PolyBezierTo(HDC hdc, const POINT *pts, DWORD cbPoints)
883{
884 GdiPath *pPath;
885 POINT pt;
886 INT i;
887
888 if(!PATH_GetPathFromHDC(hdc, &pPath))
889 return FALSE;
890
891 /* Check that path is open */
892 if(pPath->state!=PATH_Open)
893 return FALSE;
894
895 /* Add a PT_MOVETO if necessary */
896 if(pPath->newStroke)
897 {
898 pPath->newStroke=FALSE;
899 if(!GetCurrentPositionEx(hdc, &pt) ||
900 !LPtoDP(hdc, &pt, 1))
901 return FALSE;
902 if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
903 return FALSE;
904 }
Huw D M Daviesb8e94b61999-12-20 04:14:48 +0000905
Huw D M Daviesf0f8da51999-12-05 23:54:02 +0000906 for(i = 0; i < cbPoints; i++) {
907 pt = pts[i];
908 if(!LPtoDP(hdc, &pt, 1))
909 return FALSE;
910 PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
911 }
912 return TRUE;
913}
914
Huw D M Daviesb8e94b61999-12-20 04:14:48 +0000915BOOL PATH_PolyBezier(HDC hdc, const POINT *pts, DWORD cbPoints)
916{
917 GdiPath *pPath;
918 POINT pt;
919 INT i;
920
921 if(!PATH_GetPathFromHDC(hdc, &pPath))
922 return FALSE;
923
924 /* Check that path is open */
925 if(pPath->state!=PATH_Open)
926 return FALSE;
927
928 for(i = 0; i < cbPoints; i++) {
929 pt = pts[i];
930 if(!LPtoDP(hdc, &pt, 1))
931 return FALSE;
932 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO);
933 }
934 return TRUE;
935}
936
937BOOL PATH_Polyline(HDC hdc, const POINT *pts, DWORD cbPoints)
938{
939 GdiPath *pPath;
940 POINT pt;
941 INT i;
942
943 if(!PATH_GetPathFromHDC(hdc, &pPath))
944 return FALSE;
945
946 /* Check that path is open */
947 if(pPath->state!=PATH_Open)
948 return FALSE;
949
950 for(i = 0; i < cbPoints; i++) {
951 pt = pts[i];
952 if(!LPtoDP(hdc, &pt, 1))
953 return FALSE;
954 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
955 }
956 return TRUE;
957}
958
959BOOL PATH_PolylineTo(HDC hdc, const POINT *pts, DWORD cbPoints)
960{
961 GdiPath *pPath;
962 POINT pt;
963 INT i;
964
965 if(!PATH_GetPathFromHDC(hdc, &pPath))
966 return FALSE;
967
968 /* Check that path is open */
969 if(pPath->state!=PATH_Open)
970 return FALSE;
971
972 /* Add a PT_MOVETO if necessary */
973 if(pPath->newStroke)
974 {
975 pPath->newStroke=FALSE;
976 if(!GetCurrentPositionEx(hdc, &pt) ||
977 !LPtoDP(hdc, &pt, 1))
978 return FALSE;
979 if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
980 return FALSE;
981 }
982
983 for(i = 0; i < cbPoints; i++) {
984 pt = pts[i];
985 if(!LPtoDP(hdc, &pt, 1))
986 return FALSE;
987 PATH_AddEntry(pPath, &pt, PT_LINETO);
988 }
989
990 return TRUE;
991}
992
993
994BOOL PATH_Polygon(HDC hdc, const POINT *pts, DWORD cbPoints)
995{
996 GdiPath *pPath;
997 POINT pt;
998 INT i;
999
1000 if(!PATH_GetPathFromHDC(hdc, &pPath))
1001 return FALSE;
1002
1003 /* Check that path is open */
1004 if(pPath->state!=PATH_Open)
1005 return FALSE;
1006
1007 for(i = 0; i < cbPoints; i++) {
1008 pt = pts[i];
1009 if(!LPtoDP(hdc, &pt, 1))
1010 return FALSE;
1011 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
1012 ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
1013 PT_LINETO));
1014 }
1015 return TRUE;
1016}
1017
1018BOOL PATH_PolyPolygon( HDC hdc, const POINT* pts, const INT* counts,
1019 UINT polygons )
1020{
1021 GdiPath *pPath;
1022 POINT pt, startpt;
1023 INT poly, point, i;
1024
1025 if(!PATH_GetPathFromHDC(hdc, &pPath))
1026 return FALSE;
1027
1028 /* Check that path is open */
1029 if(pPath->state!=PATH_Open)
1030 return FALSE;
1031
1032 for(i = 0, poly = 0; poly < polygons; poly++) {
1033 for(point = 0; point < counts[poly]; point++, i++) {
1034 pt = pts[i];
1035 if(!LPtoDP(hdc, &pt, 1))
1036 return FALSE;
1037 if(point == 0) startpt = pt;
1038 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1039 }
1040 /* win98 adds an extra line to close the figure for some reason */
1041 PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
1042 }
1043 return TRUE;
1044}
1045
1046BOOL PATH_PolyPolyline( HDC hdc, const POINT* pts, const DWORD* counts,
1047 DWORD polylines )
1048{
1049 GdiPath *pPath;
1050 POINT pt;
1051 INT poly, point, i;
1052
1053 if(!PATH_GetPathFromHDC(hdc, &pPath))
1054 return FALSE;
1055
1056 /* Check that path is open */
1057 if(pPath->state!=PATH_Open)
1058 return FALSE;
1059
1060 for(i = 0, poly = 0; poly < polylines; poly++) {
1061 for(point = 0; point < counts[poly]; point++, i++) {
1062 pt = pts[i];
1063 if(!LPtoDP(hdc, &pt, 1))
1064 return FALSE;
1065 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1066 }
1067 }
1068 return TRUE;
1069}
1070
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001071/***********************************************************************
1072 * Internal functions
1073 */
1074
Huw D M Daviesb8e94b61999-12-20 04:14:48 +00001075
1076/* PATH_AddFlatBezier
1077 *
1078 */
1079static BOOL PATH_AddFlatBezier(GdiPath *pPath, POINT *pt, BOOL closed)
1080{
1081 POINT *pts;
1082 INT no, i;
1083
1084 pts = GDI_Bezier( pt, 4, &no );
1085 if(!pts) return FALSE;
1086
1087 for(i = 1; i < no; i++)
1088 PATH_AddEntry(pPath, &pts[i],
1089 (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
1090 HeapFree( GetProcessHeap(), 0, pts );
1091 return TRUE;
1092}
1093
1094/* PATH_FlattenPath
1095 *
1096 * Replaces Beziers with line segments
1097 *
1098 */
1099static BOOL PATH_FlattenPath(GdiPath *pPath)
1100{
1101 GdiPath newPath;
1102 INT srcpt;
1103
1104 memset(&newPath, 0, sizeof(newPath));
1105 newPath.state = PATH_Open;
1106 for(srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++) {
1107 switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE) {
1108 case PT_MOVETO:
1109 case PT_LINETO:
1110 PATH_AddEntry(&newPath, &pPath->pPoints[srcpt],
1111 pPath->pFlags[srcpt]);
1112 break;
1113 case PT_BEZIERTO:
1114 PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1],
1115 pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
1116 srcpt += 2;
1117 break;
1118 }
1119 }
1120 newPath.state = PATH_Closed;
1121 PATH_AssignGdiPath(pPath, &newPath);
1122 PATH_EmptyPath(&newPath);
1123 return TRUE;
1124}
1125
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001126/* PATH_PathToRegion
1127 *
1128 * Creates a region from the specified path using the specified polygon
1129 * filling mode. The path is left unchanged. A handle to the region that
1130 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
1131 * error occurs, SetLastError is called with the appropriate value and
1132 * FALSE is returned.
1133 */
Niels Kristian Bech Jensenc9742b32000-03-28 20:44:59 +00001134static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode,
Alexandre Julliarda3960291999-02-26 11:11:13 +00001135 HRGN *pHrgn)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001136{
1137 int numStrokes, iStroke, i;
Alexandre Julliarda3960291999-02-26 11:11:13 +00001138 INT *pNumPointsInStroke;
1139 HRGN hrgn;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001140
1141 assert(pPath!=NULL);
1142 assert(pHrgn!=NULL);
Huw D M Daviesb8e94b61999-12-20 04:14:48 +00001143
Niels Kristian Bech Jensenc9742b32000-03-28 20:44:59 +00001144 PATH_FlattenPath(pPath);
Huw D M Daviesb8e94b61999-12-20 04:14:48 +00001145
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001146 /* FIXME: What happens when number of points is zero? */
1147
1148 /* First pass: Find out how many strokes there are in the path */
1149 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
1150 numStrokes=0;
1151 for(i=0; i<pPath->numEntriesUsed; i++)
1152 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1153 numStrokes++;
1154
1155 /* Allocate memory for number-of-points-in-stroke array */
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001156 pNumPointsInStroke=(int *)HeapAlloc( GetProcessHeap(), 0,
1157 sizeof(int) * numStrokes );
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001158 if(!pNumPointsInStroke)
1159 {
1160 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1161 return FALSE;
1162 }
1163
1164 /* Second pass: remember number of points in each polygon */
1165 iStroke=-1; /* Will get incremented to 0 at beginning of first stroke */
1166 for(i=0; i<pPath->numEntriesUsed; i++)
1167 {
1168 /* Is this the beginning of a new stroke? */
1169 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1170 {
1171 iStroke++;
1172 pNumPointsInStroke[iStroke]=0;
1173 }
1174
1175 pNumPointsInStroke[iStroke]++;
1176 }
1177
1178 /* Create a region from the strokes */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001179 hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001180 numStrokes, nPolyFillMode);
Alexandre Julliarda3960291999-02-26 11:11:13 +00001181 if(hrgn==(HRGN)0)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001182 {
1183 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1184 return FALSE;
1185 }
1186
1187 /* Free memory for number-of-points-in-stroke array */
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001188 HeapFree( GetProcessHeap(), 0, pNumPointsInStroke );
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001189
1190 /* Success! */
1191 *pHrgn=hrgn;
1192 return TRUE;
1193}
1194
1195/* PATH_EmptyPath
1196 *
1197 * Removes all entries from the path and sets the path state to PATH_Null.
1198 */
1199static void PATH_EmptyPath(GdiPath *pPath)
1200{
1201 assert(pPath!=NULL);
1202
1203 pPath->state=PATH_Null;
1204 pPath->numEntriesUsed=0;
1205}
1206
1207/* PATH_AddEntry
1208 *
1209 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
1210 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
1211 * successful, FALSE otherwise (e.g. if not enough memory was available).
1212 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001213BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint, BYTE flags)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001214{
1215 assert(pPath!=NULL);
1216
Alexandre Julliarddadf78f1998-05-17 17:13:43 +00001217 /* FIXME: If newStroke is true, perhaps we want to check that we're
1218 * getting a PT_MOVETO
1219 */
Huw D M Daviesb8e94b61999-12-20 04:14:48 +00001220 TRACE("(%ld,%ld) - %d\n", pPoint->x, pPoint->y, flags);
Alexandre Julliarddadf78f1998-05-17 17:13:43 +00001221
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001222 /* Check that path is open */
1223 if(pPath->state!=PATH_Open)
1224 return FALSE;
1225
1226 /* Reserve enough memory for an extra path entry */
1227 if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
1228 return FALSE;
1229
1230 /* Store information in path entry */
Alexandre Julliarddadf78f1998-05-17 17:13:43 +00001231 pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001232 pPath->pFlags[pPath->numEntriesUsed]=flags;
1233
Alexandre Julliarddadf78f1998-05-17 17:13:43 +00001234 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
1235 if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
1236 pPath->newStroke=TRUE;
1237
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001238 /* Increment entry count */
1239 pPath->numEntriesUsed++;
1240
1241 return TRUE;
1242}
1243
1244/* PATH_ReserveEntries
1245 *
1246 * Ensures that at least "numEntries" entries (for points and flags) have
1247 * been allocated; allocates larger arrays and copies the existing entries
1248 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
1249 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001250static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001251{
Alexandre Julliarda3960291999-02-26 11:11:13 +00001252 INT numEntriesToAllocate;
1253 POINT *pPointsNew;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001254 BYTE *pFlagsNew;
1255
1256 assert(pPath!=NULL);
1257 assert(numEntries>=0);
1258
1259 /* Do we have to allocate more memory? */
1260 if(numEntries > pPath->numEntriesAllocated)
1261 {
1262 /* Find number of entries to allocate. We let the size of the array
1263 * grow exponentially, since that will guarantee linear time
1264 * complexity. */
1265 if(pPath->numEntriesAllocated)
1266 {
1267 numEntriesToAllocate=pPath->numEntriesAllocated;
1268 while(numEntriesToAllocate<numEntries)
1269 numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/
1270 GROW_FACTOR_DENOM;
1271 }
1272 else
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001273 numEntriesToAllocate=numEntries;
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001274
1275 /* Allocate new arrays */
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001276 pPointsNew=(POINT *)HeapAlloc( GetProcessHeap(), 0,
1277 numEntriesToAllocate * sizeof(POINT) );
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001278 if(!pPointsNew)
1279 return FALSE;
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001280 pFlagsNew=(BYTE *)HeapAlloc( GetProcessHeap(), 0,
1281 numEntriesToAllocate * sizeof(BYTE) );
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001282 if(!pFlagsNew)
1283 {
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001284 HeapFree( GetProcessHeap(), 0, pPointsNew );
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001285 return FALSE;
1286 }
1287
1288 /* Copy old arrays to new arrays and discard old arrays */
1289 if(pPath->pPoints)
1290 {
1291 assert(pPath->pFlags);
1292
1293 memcpy(pPointsNew, pPath->pPoints,
Alexandre Julliarda3960291999-02-26 11:11:13 +00001294 sizeof(POINT)*pPath->numEntriesUsed);
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001295 memcpy(pFlagsNew, pPath->pFlags,
1296 sizeof(BYTE)*pPath->numEntriesUsed);
1297
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001298 HeapFree( GetProcessHeap(), 0, pPath->pPoints );
1299 HeapFree( GetProcessHeap(), 0, pPath->pFlags );
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001300 }
1301 pPath->pPoints=pPointsNew;
1302 pPath->pFlags=pFlagsNew;
1303 pPath->numEntriesAllocated=numEntriesToAllocate;
1304 }
1305
1306 return TRUE;
1307}
1308
1309/* PATH_GetPathFromHDC
1310 *
1311 * Retrieves a pointer to the GdiPath structure contained in an HDC and
1312 * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
1313 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001314static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath)
Alexandre Julliard44ed71f1997-12-21 19:17:50 +00001315{
1316 DC *pDC;
1317
1318 pDC=DC_GetDCPtr(hdc);
1319 if(pDC)
1320 {
1321 *ppPath=&pDC->w.path;
1322 return TRUE;
1323 }
1324 else
1325 return FALSE;
1326}
Alexandre Julliard60ce85c1998-02-01 18:33:27 +00001327
1328/* PATH_DoArcPart
1329 *
1330 * Creates a Bezier spline that corresponds to part of an arc and appends the
1331 * corresponding points to the path. The start and end angles are passed in
1332 * "angleStart" and "angleEnd"; these angles should span a quarter circle
1333 * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
1334 * point is added to the path; otherwise, it is assumed that the current
1335 * position is equal to the first control point.
1336 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001337static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
1338 double angleStart, double angleEnd, BOOL addMoveTo)
Alexandre Julliard60ce85c1998-02-01 18:33:27 +00001339{
1340 double halfAngle, a;
1341 double xNorm[4], yNorm[4];
Alexandre Julliarda3960291999-02-26 11:11:13 +00001342 POINT point;
Alexandre Julliard60ce85c1998-02-01 18:33:27 +00001343 int i;
1344
1345 assert(fabs(angleEnd-angleStart)<=M_PI_2);
1346
1347 /* FIXME: Is there an easier way of computing this? */
1348
1349 /* Compute control points */
1350 halfAngle=(angleEnd-angleStart)/2.0;
Alexandre Julliarddadf78f1998-05-17 17:13:43 +00001351 if(fabs(halfAngle)>1e-8)
1352 {
1353 a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
1354 xNorm[0]=cos(angleStart);
1355 yNorm[0]=sin(angleStart);
1356 xNorm[1]=xNorm[0] - a*yNorm[0];
1357 yNorm[1]=yNorm[0] + a*xNorm[0];
1358 xNorm[3]=cos(angleEnd);
1359 yNorm[3]=sin(angleEnd);
1360 xNorm[2]=xNorm[3] + a*yNorm[3];
1361 yNorm[2]=yNorm[3] - a*xNorm[3];
1362 }
1363 else
1364 for(i=0; i<4; i++)
1365 {
1366 xNorm[i]=cos(angleStart);
1367 yNorm[i]=sin(angleStart);
1368 }
Alexandre Julliard60ce85c1998-02-01 18:33:27 +00001369
1370 /* Add starting point to path if desired */
1371 if(addMoveTo)
1372 {
1373 PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
Alexandre Julliarddadf78f1998-05-17 17:13:43 +00001374 if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
Alexandre Julliard60ce85c1998-02-01 18:33:27 +00001375 return FALSE;
1376 }
1377
1378 /* Add remaining control points */
1379 for(i=1; i<4; i++)
1380 {
1381 PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
Alexandre Julliarddadf78f1998-05-17 17:13:43 +00001382 if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
Alexandre Julliard60ce85c1998-02-01 18:33:27 +00001383 return FALSE;
1384 }
1385
1386 return TRUE;
1387}
1388
1389/* PATH_ScaleNormalizedPoint
1390 *
1391 * Scales a normalized point (x, y) with respect to the box whose corners are
1392 * passed in "corners". The point is stored in "*pPoint". The normalized
1393 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1394 * (1.0, 1.0) correspond to corners[1].
1395 */
Alexandre Julliarddadf78f1998-05-17 17:13:43 +00001396static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
Alexandre Julliarda3960291999-02-26 11:11:13 +00001397 double y, POINT *pPoint)
Alexandre Julliard60ce85c1998-02-01 18:33:27 +00001398{
Alexandre Julliarddadf78f1998-05-17 17:13:43 +00001399 pPoint->x=GDI_ROUND( (double)corners[0].x +
Alexandre Julliard60ce85c1998-02-01 18:33:27 +00001400 (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
Alexandre Julliarddadf78f1998-05-17 17:13:43 +00001401 pPoint->y=GDI_ROUND( (double)corners[0].y +
Alexandre Julliard60ce85c1998-02-01 18:33:27 +00001402 (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
1403}
1404
1405/* PATH_NormalizePoint
1406 *
1407 * Normalizes a point with respect to the box whose corners are passed in
1408 * "corners". The normalized coordinates are stored in "*pX" and "*pY".
1409 */
Alexandre Julliarddadf78f1998-05-17 17:13:43 +00001410static void PATH_NormalizePoint(FLOAT_POINT corners[],
1411 const FLOAT_POINT *pPoint,
Alexandre Julliard60ce85c1998-02-01 18:33:27 +00001412 double *pX, double *pY)
1413{
1414 *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) *
1415 2.0 - 1.0;
1416 *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) *
1417 2.0 - 1.0;
1418}
Paul Quinn1beaae51998-12-15 15:38:36 +00001419
1420/*******************************************************************
Patrik Stridvall900290a1999-10-24 21:32:43 +00001421 * FlattenPath16 [GDI.516]
1422 *
1423 *
1424 */
1425BOOL16 WINAPI FlattenPath16(HDC16 hdc)
1426{
1427 return (BOOL16) FlattenPath((HDC) hdc);
1428}
1429
1430/*******************************************************************
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001431 * FlattenPath [GDI32.103]
Paul Quinn1beaae51998-12-15 15:38:36 +00001432 *
1433 *
1434 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001435BOOL WINAPI FlattenPath(HDC hdc)
Paul Quinn1beaae51998-12-15 15:38:36 +00001436{
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001437 DC *dc = DC_GetDCPtr( hdc );
Huw D M Daviesb8e94b61999-12-20 04:14:48 +00001438 GdiPath *pPath;
1439 TRACE("%08x\n", hdc);
1440
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001441 if(!dc) {
1442 SetLastError(ERROR_INVALID_HANDLE);
1443 return FALSE;
1444 }
1445
1446 if(dc->funcs->pFlattenPath)
1447 return dc->funcs->pFlattenPath(dc);
1448
Huw D M Daviesb8e94b61999-12-20 04:14:48 +00001449 pPath = &dc->w.path;
1450 if(pPath->state != PATH_Closed)
1451 return FALSE;
1452 return PATH_FlattenPath(pPath);
Paul Quinn1beaae51998-12-15 15:38:36 +00001453}
1454
Huw D M Daviesa3d3ff82000-04-18 11:54:12 +00001455
1456static BOOL PATH_StrokePath(HDC hdc, GdiPath *pPath)
Patrik Stridvall900290a1999-10-24 21:32:43 +00001457{
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001458 INT i;
1459 POINT ptLastMove = {0,0};
1460
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001461 if(pPath->state != PATH_Closed)
1462 return FALSE;
1463
1464 SaveDC(hdc);
1465 SetMapMode(hdc, MM_TEXT);
1466 SetViewportOrgEx(hdc, 0, 0, NULL);
1467 SetWindowOrgEx(hdc, 0, 0, NULL);
1468 for(i = 0; i < pPath->numEntriesUsed; i++) {
Huw D M Daviesa3d3ff82000-04-18 11:54:12 +00001469 switch(pPath->pFlags[i]) {
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001470 case PT_MOVETO:
1471 TRACE("Got PT_MOVETO (%ld, %ld)\n",
1472 pPath->pPoints[i].x, pPath->pPoints[i].y);
1473 MoveToEx(hdc, pPath->pPoints[i].x, pPath->pPoints[i].y, NULL);
1474 ptLastMove = pPath->pPoints[i];
1475 break;
1476 case PT_LINETO:
1477 case (PT_LINETO | PT_CLOSEFIGURE):
1478 TRACE("Got PT_LINETO (%ld, %ld)\n",
1479 pPath->pPoints[i].x, pPath->pPoints[i].y);
1480 LineTo(hdc, pPath->pPoints[i].x, pPath->pPoints[i].y);
1481 break;
1482 case PT_BEZIERTO:
1483 TRACE("Got PT_BEZIERTO\n");
1484 if(pPath->pFlags[i+1] != PT_BEZIERTO ||
1485 (pPath->pFlags[i+2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) {
1486 ERR("Path didn't contain 3 successive PT_BEZIERTOs\n");
1487 return FALSE;
1488 }
1489 PolyBezierTo(hdc, &pPath->pPoints[i], 3);
1490 i += 2;
1491 break;
1492 default:
1493 ERR("Got path flag %d\n", (INT)pPath->pFlags[i]);
1494 return FALSE;
1495 }
1496 if(pPath->pFlags[i] & PT_CLOSEFIGURE)
1497 LineTo(hdc, ptLastMove.x, ptLastMove.y);
1498 }
1499 RestoreDC(hdc, -1);
Huw D M Daviesa3d3ff82000-04-18 11:54:12 +00001500 return TRUE;
1501}
1502
1503
1504/*******************************************************************
1505 * StrokeAndFillPath16 [GDI.520]
1506 *
1507 *
1508 */
1509BOOL16 WINAPI StrokeAndFillPath16(HDC16 hdc)
1510{
1511 return (BOOL16) StrokeAndFillPath((HDC) hdc);
1512}
1513
1514/*******************************************************************
1515 * StrokeAndFillPath [GDI32.352]
1516 *
1517 *
1518 */
1519BOOL WINAPI StrokeAndFillPath(HDC hdc)
1520{
1521 DC *dc = DC_GetDCPtr( hdc );
1522 BOOL bRet;
1523
1524 if(!dc) {
1525 SetLastError(ERROR_INVALID_HANDLE);
1526 return FALSE;
1527 }
1528
1529 if(dc->funcs->pStrokeAndFillPath)
1530 return dc->funcs->pStrokeAndFillPath(dc);
1531
1532 bRet = PATH_FillPath(hdc, &dc->w.path);
1533 if(bRet) bRet = PATH_StrokePath(hdc, &dc->w.path);
1534 if(bRet) PATH_EmptyPath(&dc->w.path);
1535 return bRet;
1536}
1537
1538/*******************************************************************
1539 * StrokePath16 [GDI.521]
1540 *
1541 *
1542 */
1543BOOL16 WINAPI StrokePath16(HDC16 hdc)
1544{
1545 return (BOOL16) StrokePath((HDC) hdc);
1546}
1547
1548/*******************************************************************
1549 * StrokePath [GDI32.353]
1550 *
1551 *
1552 */
1553BOOL WINAPI StrokePath(HDC hdc)
1554{
1555 DC *dc = DC_GetDCPtr( hdc );
1556 GdiPath *pPath;
1557
1558 TRACE("(%08x)\n", hdc);
1559 if(!dc) {
1560 SetLastError(ERROR_INVALID_HANDLE);
1561 return FALSE;
1562 }
1563
1564 if(dc->funcs->pStrokePath)
1565 return dc->funcs->pStrokePath(dc);
1566
1567 pPath = &dc->w.path;
1568 PATH_StrokePath(hdc, pPath);
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001569 PATH_EmptyPath(pPath);
1570 return TRUE;
Paul Quinn1beaae51998-12-15 15:38:36 +00001571}
1572
1573/*******************************************************************
Patrik Stridvall900290a1999-10-24 21:32:43 +00001574 * WidenPath16 [GDI.522]
1575 *
1576 *
1577 */
1578BOOL16 WINAPI WidenPath16(HDC16 hdc)
1579{
1580 return (BOOL16) WidenPath((HDC) hdc);
1581}
1582
1583/*******************************************************************
Paul Quinn1beaae51998-12-15 15:38:36 +00001584 * WidenPath [GDI32.360]
1585 *
1586 *
1587 */
Alexandre Julliarda3960291999-02-26 11:11:13 +00001588BOOL WINAPI WidenPath(HDC hdc)
Paul Quinn1beaae51998-12-15 15:38:36 +00001589{
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001590 DC *dc = DC_GetDCPtr( hdc );
Paul Quinn1beaae51998-12-15 15:38:36 +00001591
Huw D M Daviesf0f8da51999-12-05 23:54:02 +00001592 if(!dc) {
1593 SetLastError(ERROR_INVALID_HANDLE);
1594 return FALSE;
1595 }
1596
1597 if(dc->funcs->pWidenPath)
1598 return dc->funcs->pWidenPath(dc);
1599
1600 FIXME("stub\n");
1601 return 0;
1602}