Removed a few dependencies on GDI internals from USER dll.
[wine] / graphics / path.c
1 /*
2  * Graphics paths (BeginPath, EndPath etc.)
3  *
4  * Copyright 1997, 1998 Martin Boehme
5  *                 1999 Huw D M Davies
6  */
7
8 #include <assert.h>
9 #include <math.h>
10 #include <string.h>
11 #include "config.h"
12 #if defined(HAVE_FLOAT_H)
13 #include <float.h>
14 #endif
15
16 #include "winbase.h"
17 #include "wingdi.h"
18 #include "winerror.h"
19
20 #include "dc.h"
21 #include "debugtools.h"
22 #include "path.h"
23
24 DEFAULT_DEBUG_CHANNEL(gdi)
25
26 /* 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
70 static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode,
71    HRGN *pHrgn);
72 static void   PATH_EmptyPath(GdiPath *pPath);
73 static BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint,
74    BYTE flags);
75 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries);
76 static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath);
77 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
78    double angleStart, double angleEnd, BOOL addMoveTo);
79 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
80    double y, POINT *pPoint);
81 static void PATH_NormalizePoint(FLOAT_POINT corners[], const FLOAT_POINT
82    *pPoint, double *pX, double *pY);
83
84
85 /***********************************************************************
86  *           BeginPath16    (GDI.512)
87  */
88 BOOL16 WINAPI BeginPath16(HDC16 hdc)
89 {
90    return (BOOL16)BeginPath((HDC)hdc);
91 }
92
93
94 /***********************************************************************
95  *           BeginPath    (GDI32.9)
96  */
97 BOOL WINAPI BeginPath(HDC hdc)
98 {
99    DC *dc = DC_GetDCPtr( hdc );
100    GdiPath *pPath;
101    
102    if(!dc) {
103      SetLastError(ERROR_INVALID_HANDLE);
104      return FALSE;
105    }
106
107    if(dc->funcs->pBeginPath)
108      return dc->funcs->pBeginPath(dc);
109
110    pPath = &dc->w.path;
111
112    /* 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 /***********************************************************************
128  *           EndPath16    (GDI.514)
129  */
130 BOOL16 WINAPI EndPath16(HDC16 hdc)
131 {
132    return (BOOL16)EndPath((HDC)hdc);
133 }
134
135
136 /***********************************************************************
137  *           EndPath    (GDI32.78)
138  */
139 BOOL WINAPI EndPath(HDC hdc)
140 {
141    DC *dc = DC_GetDCPtr( hdc );
142    GdiPath *pPath;
143    
144    if(!dc) {
145      SetLastError(ERROR_INVALID_HANDLE);
146      return FALSE;
147    }
148
149    if(dc->funcs->pEndPath)
150      return dc->funcs->pEndPath(dc);
151
152    pPath = &dc->w.path;
153
154    /* 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 /***********************************************************************
169  *           AbortPath16    (GDI.511)
170  */
171 BOOL16 WINAPI AbortPath16(HDC16 hdc)
172 {
173    return (BOOL16)AbortPath((HDC)hdc);
174 }
175
176
177 /******************************************************************************
178  * AbortPath [GDI32.1]
179  * 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
188  */
189 BOOL WINAPI AbortPath( HDC hdc )
190 {
191    DC *dc = DC_GetDCPtr( hdc );
192    GdiPath *pPath;
193    
194    if(!dc) {
195      SetLastError(ERROR_INVALID_HANDLE);
196      return FALSE;
197    }
198
199    if(dc->funcs->pAbortPath)
200      return dc->funcs->pAbortPath(dc);
201
202    pPath = &dc->w.path;
203    
204    /* Remove all entries from the path */
205    PATH_EmptyPath(pPath);
206
207    return TRUE;
208 }
209
210
211 /***********************************************************************
212  *           CloseFigure16    (GDI.513)
213  */
214 BOOL16 WINAPI CloseFigure16(HDC16 hdc)
215 {
216    return (BOOL16)CloseFigure((HDC)hdc);
217 }
218
219
220 /***********************************************************************
221  *           CloseFigure    (GDI32.16)
222  *
223  * FIXME: Check that SetLastError is being called correctly 
224  */
225 BOOL WINAPI CloseFigure(HDC hdc)
226 {
227    DC *dc = DC_GetDCPtr( hdc );
228    GdiPath *pPath;
229    
230    if(!dc) {
231      SetLastError(ERROR_INVALID_HANDLE);
232      return FALSE;
233    }
234
235    if(dc->funcs->pCloseFigure)
236      return dc->funcs->pCloseFigure(dc);
237
238    pPath = &dc->w.path;
239
240    /* 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 /***********************************************************************
259  *           GetPath16    (GDI.517)
260  */
261 INT16 WINAPI GetPath16(HDC16 hdc, LPPOINT16 pPoints, LPBYTE pTypes,
262    INT16 nSize)
263 {
264    FIXME("(%d,%p,%p): stub\n",hdc,pPoints,pTypes);
265
266    return 0;
267 }
268
269
270 /***********************************************************************
271  *           GetPath    (GDI32.210)
272  */
273 INT WINAPI GetPath(HDC hdc, LPPOINT pPoints, LPBYTE pTypes,
274    INT nSize)
275 {
276    GdiPath *pPath;
277    
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    {
301       memcpy(pPoints, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
302       memcpy(pTypes, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
303
304       /* Convert the points to logical coordinates */
305       if(!DPtoLP(hdc, pPoints, pPath->numEntriesUsed))
306       {
307          /* FIXME: Is this the correct value? */
308          SetLastError(ERROR_CAN_NOT_COMPLETE);
309          return -1;
310       }
311       
312       return pPath->numEntriesUsed;
313    }
314 }
315
316 /***********************************************************************
317  *           PathToRegion16    (GDI.518)
318  */
319 HRGN16 WINAPI PathToRegion16(HDC16 hdc)
320 {
321   return (HRGN16) PathToRegion((HDC) hdc);
322 }
323
324 /***********************************************************************
325  *           PathToRegion    (GDI32.261)
326  *
327  * FIXME 
328  *   Check that SetLastError is being called correctly 
329  *
330  * The documentation does not state this explicitly, but a test under Windows
331  * shows that the region which is returned should be in device coordinates.
332  */
333 HRGN WINAPI PathToRegion(HDC hdc)
334 {
335    GdiPath *pPath;
336    HRGN  hrgnRval;
337
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? */
353    if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnRval))
354       PATH_EmptyPath(pPath);
355    else
356       hrgnRval=0;
357
358    return hrgnRval;
359 }
360
361 static BOOL PATH_FillPath(HDC hdc, GdiPath *pPath)
362 {
363    INT   mapMode, graphicsMode;
364    SIZE  ptViewportExt, ptWindowExt;
365    POINT ptViewportOrg, ptWindowOrg;
366    XFORM xform;
367    HRGN  hrgn;
368
369    /* 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 */
377    if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgn))
378    {
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.
382        * 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.
385        */
386        
387       /* Save the information about the old mapping mode */
388       mapMode=GetMapMode(hdc);
389       GetViewportExtEx(hdc, &ptViewportExt);
390       GetViewportOrgEx(hdc, &ptViewportOrg);
391       GetWindowExtEx(hdc, &ptWindowExt);
392       GetWindowOrgEx(hdc, &ptWindowOrg);
393       
394       /* 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.
399        */
400       GetWorldTransform(hdc, &xform);
401       
402       /* Set MM_TEXT */
403       SetMapMode(hdc, MM_TEXT);
404       SetViewportOrgEx(hdc, 0, 0, NULL);
405       SetWindowOrgEx(hdc, 0, 0, NULL);
406
407       /* Paint the region */
408       PaintRgn(hdc, hrgn);
409       DeleteObject(hrgn);
410       /* Restore the old mapping mode */
411       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);
416
417       /* 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);
422       return TRUE;
423    }
424    return FALSE;
425 }
426
427 /***********************************************************************
428  *           FillPath16    (GDI.515)
429  */
430 BOOL16 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  */
441 BOOL WINAPI FillPath(HDC hdc)
442 {
443    DC *dc = DC_GetDCPtr( hdc );
444    
445    if(!dc) {
446      SetLastError(ERROR_INVALID_HANDLE);
447      return FALSE;
448    }
449
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;
459 }
460
461 /***********************************************************************
462  *           SelectClipPath16    (GDI.519)
463  */
464 BOOL16 WINAPI SelectClipPath16(HDC16 hdc, INT16 iMode)
465 {
466   return (BOOL16) SelectClipPath((HDC) hdc, iMode);
467 }
468
469 /***********************************************************************
470  *           SelectClipPath    (GDI32.296)
471  * FIXME 
472  *  Check that SetLastError is being called correctly 
473  */
474 BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
475 {
476    GdiPath *pPath;
477    HRGN  hrgnPath;
478    BOOL  success;
479    DC *dc = DC_GetDCPtr( hdc );
480    
481    if(!dc) {
482      SetLastError(ERROR_INVALID_HANDLE);
483      return FALSE;
484    }
485
486    if(dc->funcs->pSelectClipPath)
487      return dc->funcs->pSelectClipPath(dc, iMode);
488
489    pPath = &dc->w.path;
490    
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 */
499    if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnPath))
500    {
501       success = ExtSelectClipRgn( hdc, hrgnPath, iMode ) != ERROR;
502       DeleteObject(hrgnPath);
503
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  */
524 void 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  */
539 void PATH_DestroyGdiPath(GdiPath *pPath)
540 {
541    assert(pPath!=NULL);
542
543    if (pPath->pPoints) HeapFree( GetProcessHeap(), 0, pPath->pPoints );
544    if (pPath->pFlags) HeapFree( GetProcessHeap(), 0, pPath->pFlags );
545 }
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  */
557 BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
558 {
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,
567       sizeof(POINT)*pPathSrc->numEntriesUsed);
568    memcpy(pPathDest->pFlags, pPathSrc->pFlags,
569       sizeof(BYTE)*pPathSrc->numEntriesUsed);
570
571    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  */
584 BOOL PATH_MoveTo(HDC hdc)
585 {
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  */
610 BOOL PATH_LineTo(HDC hdc, INT x, INT y)
611 {
612    GdiPath *pPath;
613    POINT point, pointCurPos;
614    
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)
621       return FALSE;
622
623    /* Convert point to device coordinates */
624    point.x=x;
625    point.y=y;
626    if(!LPtoDP(hdc, &point, 1))
627       return FALSE;
628    
629    /* Add a PT_MOVETO if necessary */
630    if(pPath->newStroke)
631    {
632       pPath->newStroke=FALSE;
633       if(!GetCurrentPositionEx(hdc, &pointCurPos) ||
634          !LPtoDP(hdc, &pointCurPos, 1))
635          return FALSE;
636       if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
637          return FALSE;
638    }
639    
640    /* Add a PT_LINETO entry */
641    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  */
649 BOOL PATH_Rectangle(HDC hdc, INT x1, INT y1, INT x2, INT y2)
650 {
651    GdiPath *pPath;
652    POINT corners[2], pointTemp;
653    INT   temp;
654
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;
668    if(!LPtoDP(hdc, corners, 2))
669       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 */
693    if(!CloseFigure(hdc))
694    {
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 */
715    if(!CloseFigure(hdc))
716    {
717       /* The CloseFigure call shouldn't have failed */
718       assert(FALSE);
719       return FALSE;
720    }
721
722    return TRUE;
723 }
724
725 /* 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  */
731 BOOL PATH_Ellipse(HDC hdc, INT x1, INT y1, INT x2, INT y2)
732 {
733    /* TODO: This should probably be revised to call PATH_AngleArc */
734    /* (once it exists) */
735    return PATH_Arc(hdc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2);
736 }
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  */
744 BOOL PATH_Arc(HDC hdc, INT x1, INT y1, INT x2, INT y2,
745    INT xStart, INT yStart, INT xEnd, INT yEnd)
746 {
747    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;
753    BOOL      start, end;
754    INT       temp;
755
756    /* FIXME: This function should check for all possible error returns */
757    /* FIXME: Do we have to respect newStroke? */
758    
759    /* Get pointer to DC */
760    pDC=DC_GetDCPtr(hdc);
761    if(pDC==NULL)
762       return FALSE;
763
764    /* 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
772    /* FIXME: Do we have to close the current figure? */
773    
774    /* Check for zero height / width */
775    /* FIXME: Only in GM_COMPATIBLE? */
776    if(x1==x2 || y1==y2)
777       return TRUE;
778    
779    /* Convert points to device coordinates */
780    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);
792
793    /* Make sure first corner is top left and second corner is bottom right */
794    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 */
814    if(GetArcDirection(hdc)==AD_CLOCKWISE)
815    {
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
831    /* 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    
838    /* 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;
848          if(GetArcDirection(hdc)==AD_CLOCKWISE)
849             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;
856          if(GetArcDirection(hdc)==AD_CLOCKWISE)
857             angleEndQuadrant+=M_PI_2;
858          else
859             angleEndQuadrant-=M_PI_2;
860       }
861
862       /* Have we reached the last part of the arc? */
863       if((GetArcDirection(hdc)==AD_CLOCKWISE &&
864          angleEnd<angleEndQuadrant) ||
865          (GetArcDirection(hdc)==AD_COUNTERCLOCKWISE &&
866          angleEnd>angleEndQuadrant))
867       {
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 }
881
882 BOOL 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    }
905
906    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    
915 BOOL 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
937 BOOL 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    
959 BOOL 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
994 BOOL 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
1018 BOOL 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
1046 BOOL 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    
1071 /***********************************************************************
1072  * Internal functions
1073  */
1074
1075
1076 /* PATH_AddFlatBezier
1077  *
1078  */
1079 static 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  */
1099 static 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           
1126 /* 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  */
1134 static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode,
1135    HRGN *pHrgn)
1136 {
1137    int    numStrokes, iStroke, i;
1138    INT  *pNumPointsInStroke;
1139    HRGN hrgn;
1140
1141    assert(pPath!=NULL);
1142    assert(pHrgn!=NULL);
1143
1144    PATH_FlattenPath(pPath);
1145
1146    /* 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 */
1156    pNumPointsInStroke=(int *)HeapAlloc( GetProcessHeap(), 0, 
1157                                         sizeof(int) * numStrokes );
1158    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 */
1179    hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
1180       numStrokes, nPolyFillMode);
1181    if(hrgn==(HRGN)0)
1182    {
1183       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1184       return FALSE;
1185    }
1186
1187    /* Free memory for number-of-points-in-stroke array */
1188    HeapFree( GetProcessHeap(), 0, pNumPointsInStroke );
1189
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  */
1199 static 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  */
1213 BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint, BYTE flags)
1214 {
1215    assert(pPath!=NULL);
1216    
1217    /* FIXME: If newStroke is true, perhaps we want to check that we're
1218     * getting a PT_MOVETO
1219     */
1220    TRACE("(%ld,%ld) - %d\n", pPoint->x, pPoint->y, flags);
1221
1222    /* 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 */
1231    pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
1232    pPath->pFlags[pPath->numEntriesUsed]=flags;
1233
1234    /* 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
1238    /* 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  */
1250 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries)
1251 {
1252    INT   numEntriesToAllocate;
1253    POINT *pPointsNew;
1254    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
1273          numEntriesToAllocate=numEntries;
1274
1275       /* Allocate new arrays */
1276       pPointsNew=(POINT *)HeapAlloc( GetProcessHeap(), 0, 
1277                                      numEntriesToAllocate * sizeof(POINT) );
1278       if(!pPointsNew)
1279          return FALSE;
1280       pFlagsNew=(BYTE *)HeapAlloc( GetProcessHeap(), 0, 
1281                                    numEntriesToAllocate * sizeof(BYTE) );
1282       if(!pFlagsNew)
1283       {
1284          HeapFree( GetProcessHeap(), 0, pPointsNew );
1285          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,
1294              sizeof(POINT)*pPath->numEntriesUsed);
1295          memcpy(pFlagsNew, pPath->pFlags,
1296              sizeof(BYTE)*pPath->numEntriesUsed);
1297
1298          HeapFree( GetProcessHeap(), 0, pPath->pPoints );
1299          HeapFree( GetProcessHeap(), 0, pPath->pFlags );
1300       }
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  */
1314 static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath)
1315 {
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 }
1327
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  */
1337 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
1338    double angleStart, double angleEnd, BOOL addMoveTo)
1339 {
1340    double  halfAngle, a;
1341    double  xNorm[4], yNorm[4];
1342    POINT point;
1343    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;
1351    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       }
1369    
1370    /* Add starting point to path if desired */
1371    if(addMoveTo)
1372    {
1373       PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
1374       if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
1375          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);
1382       if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1383          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  */
1396 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
1397    double y, POINT *pPoint)
1398 {
1399    pPoint->x=GDI_ROUND( (double)corners[0].x +
1400       (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
1401    pPoint->y=GDI_ROUND( (double)corners[0].y +
1402       (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  */
1410 static void PATH_NormalizePoint(FLOAT_POINT corners[],
1411    const FLOAT_POINT *pPoint,
1412    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 }
1419
1420 /*******************************************************************
1421  *      FlattenPath16 [GDI.516]
1422  *
1423  *
1424  */
1425 BOOL16 WINAPI FlattenPath16(HDC16 hdc)
1426 {
1427   return (BOOL16) FlattenPath((HDC) hdc);
1428 }
1429
1430 /*******************************************************************
1431  *      FlattenPath [GDI32.103]
1432  *
1433  *
1434  */
1435 BOOL WINAPI FlattenPath(HDC hdc)
1436 {
1437    DC *dc = DC_GetDCPtr( hdc );
1438    GdiPath *pPath;
1439    TRACE("%08x\n", hdc);
1440
1441    if(!dc) {
1442      SetLastError(ERROR_INVALID_HANDLE);
1443      return FALSE;
1444    }
1445
1446    if(dc->funcs->pFlattenPath)
1447      return dc->funcs->pFlattenPath(dc);
1448
1449     pPath = &dc->w.path;
1450     if(pPath->state != PATH_Closed)
1451         return FALSE;
1452    return PATH_FlattenPath(pPath);
1453 }
1454
1455
1456 static BOOL PATH_StrokePath(HDC hdc, GdiPath *pPath)
1457 {
1458     INT i;
1459     POINT ptLastMove = {0,0};
1460
1461     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++) {
1469         switch(pPath->pFlags[i]) {
1470         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);
1500     return TRUE;
1501 }
1502
1503
1504 /*******************************************************************
1505  *      StrokeAndFillPath16 [GDI.520]
1506  *
1507  *
1508  */
1509 BOOL16 WINAPI StrokeAndFillPath16(HDC16 hdc)
1510 {
1511   return (BOOL16) StrokeAndFillPath((HDC) hdc);
1512 }
1513
1514 /*******************************************************************
1515  *      StrokeAndFillPath [GDI32.352]
1516  *
1517  *
1518  */
1519 BOOL 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  */
1543 BOOL16 WINAPI StrokePath16(HDC16 hdc)
1544 {
1545   return (BOOL16) StrokePath((HDC) hdc);
1546 }
1547
1548 /*******************************************************************
1549  *      StrokePath [GDI32.353]
1550  *
1551  *
1552  */
1553 BOOL 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);
1569     PATH_EmptyPath(pPath);
1570     return TRUE;
1571 }
1572
1573 /*******************************************************************
1574  *      WidenPath16 [GDI.522]
1575  *
1576  *
1577  */
1578 BOOL16 WINAPI WidenPath16(HDC16 hdc)
1579 {
1580   return (BOOL16) WidenPath((HDC) hdc);
1581 }
1582
1583 /*******************************************************************
1584  *      WidenPath [GDI32.360]
1585  *
1586  *
1587  */
1588 BOOL WINAPI WidenPath(HDC hdc)
1589 {
1590    DC *dc = DC_GetDCPtr( hdc );
1591    
1592    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 }