A few corrections to bounding boxes.
[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    /* FIXME: Shouldn't we draw a line to the beginning of the figure? */
248    
249    /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
250    if(pPath->numEntriesUsed)
251    {
252       pPath->pFlags[pPath->numEntriesUsed-1]|=PT_CLOSEFIGURE;
253       pPath->newStroke=TRUE;
254    }
255
256    return TRUE;
257 }
258
259
260 /***********************************************************************
261  *           GetPath16    (GDI.517)
262  */
263 INT16 WINAPI GetPath16(HDC16 hdc, LPPOINT16 pPoints, LPBYTE pTypes,
264    INT16 nSize)
265 {
266    FIXME("(%d,%p,%p): stub\n",hdc,pPoints,pTypes);
267
268    return 0;
269 }
270
271
272 /***********************************************************************
273  *           GetPath    (GDI32.210)
274  */
275 INT WINAPI GetPath(HDC hdc, LPPOINT pPoints, LPBYTE pTypes,
276    INT nSize)
277 {
278    GdiPath *pPath;
279    
280    /* Get pointer to path */
281    if(!PATH_GetPathFromHDC(hdc, &pPath))
282    {
283       SetLastError(ERROR_INVALID_PARAMETER);
284       return -1;
285    }
286    
287    /* Check that path is closed */
288    if(pPath->state!=PATH_Closed)
289    {
290       SetLastError(ERROR_CAN_NOT_COMPLETE);
291       return -1;
292    }
293    
294    if(nSize==0)
295       return pPath->numEntriesUsed;
296    else if(nSize<pPath->numEntriesUsed)
297    {
298       SetLastError(ERROR_INVALID_PARAMETER);
299       return -1;
300    }
301    else
302    {
303       memcpy(pPoints, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
304       memcpy(pTypes, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
305
306       /* Convert the points to logical coordinates */
307       if(!DPtoLP(hdc, pPoints, pPath->numEntriesUsed))
308       {
309          /* FIXME: Is this the correct value? */
310          SetLastError(ERROR_CAN_NOT_COMPLETE);
311          return -1;
312       }
313       
314       return pPath->numEntriesUsed;
315    }
316 }
317
318 /***********************************************************************
319  *           PathToRegion16    (GDI.518)
320  */
321 HRGN16 WINAPI PathToRegion16(HDC16 hdc)
322 {
323   return (HRGN16) PathToRegion((HDC) hdc);
324 }
325
326 /***********************************************************************
327  *           PathToRegion    (GDI32.261)
328  *
329  * FIXME 
330  *   Check that SetLastError is being called correctly 
331  *
332  * The documentation does not state this explicitly, but a test under Windows
333  * shows that the region which is returned should be in device coordinates.
334  */
335 HRGN WINAPI PathToRegion(HDC hdc)
336 {
337    GdiPath *pPath;
338    HRGN  hrgnRval;
339
340    /* Get pointer to path */
341    if(!PATH_GetPathFromHDC(hdc, &pPath))
342    {
343       SetLastError(ERROR_INVALID_PARAMETER);
344       return 0;
345    }
346    
347    /* Check that path is closed */
348    if(pPath->state!=PATH_Closed)
349    {
350       SetLastError(ERROR_CAN_NOT_COMPLETE);
351       return 0;
352    }
353    
354    /* FIXME: Should we empty the path even if conversion failed? */
355    if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnRval))
356       PATH_EmptyPath(pPath);
357    else
358       hrgnRval=0;
359
360    return hrgnRval;
361 }
362
363 /***********************************************************************
364  *           FillPath16    (GDI.515)
365  */
366 BOOL16 WINAPI FillPath16(HDC16 hdc)
367 {
368   return (BOOL16) FillPath((HDC) hdc); 
369 }
370
371 /***********************************************************************
372  *           FillPath    (GDI32.100)
373  *
374  * FIXME
375  *    Check that SetLastError is being called correctly 
376  */
377 BOOL WINAPI FillPath(HDC hdc)
378 {
379    GdiPath *pPath;
380    INT   mapMode, graphicsMode;
381    SIZE  ptViewportExt, ptWindowExt;
382    POINT ptViewportOrg, ptWindowOrg;
383    XFORM   xform;
384    HRGN  hrgn;
385    DC *dc = DC_GetDCPtr( hdc );
386    
387    if(!dc) {
388      SetLastError(ERROR_INVALID_HANDLE);
389      return FALSE;
390    }
391
392    if(dc->funcs->pFillPath)
393      return dc->funcs->pFillPath(dc);
394
395    pPath = &dc->w.path;
396
397    /* Check that path is closed */
398    if(pPath->state!=PATH_Closed)
399    {
400       SetLastError(ERROR_CAN_NOT_COMPLETE);
401       return FALSE;
402    }
403    
404    /* Construct a region from the path and fill it */
405    if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgn))
406    {
407       /* Since PaintRgn interprets the region as being in logical coordinates
408        * but the points we store for the path are already in device
409        * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
410        * Using SaveDC to save information about the mapping mode / world
411        * transform would be easier but would require more overhead, especially
412        * now that SaveDC saves the current path.
413        */
414        
415       /* Save the information about the old mapping mode */
416       mapMode=GetMapMode(hdc);
417       GetViewportExtEx(hdc, &ptViewportExt);
418       GetViewportOrgEx(hdc, &ptViewportOrg);
419       GetWindowExtEx(hdc, &ptWindowExt);
420       GetWindowOrgEx(hdc, &ptWindowOrg);
421       
422       /* Save world transform
423        * NB: The Windows documentation on world transforms would lead one to
424        * believe that this has to be done only in GM_ADVANCED; however, my
425        * tests show that resetting the graphics mode to GM_COMPATIBLE does
426        * not reset the world transform.
427        */
428       GetWorldTransform(hdc, &xform);
429       
430       /* Set MM_TEXT */
431       SetMapMode(hdc, MM_TEXT);
432       SetViewportOrgEx(hdc, 0, 0, NULL);
433       SetWindowOrgEx(hdc, 0, 0, NULL);
434
435       /* Paint the region */
436       PaintRgn(hdc, hrgn);
437
438       /* Restore the old mapping mode */
439       SetMapMode(hdc, mapMode);
440       SetViewportExtEx(hdc, ptViewportExt.cx, ptViewportExt.cy, NULL);
441       SetViewportOrgEx(hdc, ptViewportOrg.x, ptViewportOrg.y, NULL);
442       SetWindowExtEx(hdc, ptWindowExt.cx, ptWindowExt.cy, NULL);
443       SetWindowOrgEx(hdc, ptWindowOrg.x, ptWindowOrg.y, NULL);
444
445       /* Go to GM_ADVANCED temporarily to restore the world transform */
446       graphicsMode=GetGraphicsMode(hdc);
447       SetGraphicsMode(hdc, GM_ADVANCED);
448       SetWorldTransform(hdc, &xform);
449       SetGraphicsMode(hdc, graphicsMode);
450
451       /* Empty the path */
452       PATH_EmptyPath(pPath);
453       return TRUE;
454    }
455    else
456    {
457       /* FIXME: Should the path be emptied even if conversion failed? */
458       /* PATH_EmptyPath(pPath); */
459       return FALSE;
460    }
461 }
462
463 /***********************************************************************
464  *           SelectClipPath16    (GDI.519)
465  */
466 BOOL16 WINAPI SelectClipPath16(HDC16 hdc, INT16 iMode)
467 {
468   return (BOOL16) SelectClipPath((HDC) hdc, iMode);
469 }
470
471 /***********************************************************************
472  *           SelectClipPath    (GDI32.296)
473  * FIXME 
474  *  Check that SetLastError is being called correctly 
475  */
476 BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
477 {
478    GdiPath *pPath;
479    HRGN  hrgnPath;
480    BOOL  success;
481    DC *dc = DC_GetDCPtr( hdc );
482    
483    if(!dc) {
484      SetLastError(ERROR_INVALID_HANDLE);
485      return FALSE;
486    }
487
488    if(dc->funcs->pSelectClipPath)
489      return dc->funcs->pSelectClipPath(dc, iMode);
490
491    pPath = &dc->w.path;
492    
493    /* Check that path is closed */
494    if(pPath->state!=PATH_Closed)
495    {
496       SetLastError(ERROR_CAN_NOT_COMPLETE);
497       return FALSE;
498    }
499    
500    /* Construct a region from the path */
501    if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnPath))
502    {
503       success = ExtSelectClipRgn( hdc, hrgnPath, iMode ) != ERROR;
504       DeleteObject(hrgnPath);
505
506       /* Empty the path */
507       if(success)
508          PATH_EmptyPath(pPath);
509       /* FIXME: Should this function delete the path even if it failed? */
510
511       return success;
512    }
513    else
514       return FALSE;
515 }
516
517
518 /***********************************************************************
519  * Exported functions
520  */
521
522 /* PATH_InitGdiPath
523  *
524  * Initializes the GdiPath structure.
525  */
526 void PATH_InitGdiPath(GdiPath *pPath)
527 {
528    assert(pPath!=NULL);
529
530    pPath->state=PATH_Null;
531    pPath->pPoints=NULL;
532    pPath->pFlags=NULL;
533    pPath->numEntriesUsed=0;
534    pPath->numEntriesAllocated=0;
535 }
536
537 /* PATH_DestroyGdiPath
538  *
539  * Destroys a GdiPath structure (frees the memory in the arrays).
540  */
541 void PATH_DestroyGdiPath(GdiPath *pPath)
542 {
543    assert(pPath!=NULL);
544
545    HeapFree( GetProcessHeap(), 0, pPath->pPoints );
546    HeapFree( GetProcessHeap(), 0, pPath->pFlags );
547 }
548
549 /* PATH_AssignGdiPath
550  *
551  * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
552  * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
553  * not just the pointers. Since this means that the arrays in pPathDest may
554  * need to be resized, pPathDest should have been initialized using
555  * PATH_InitGdiPath (in C++, this function would be an assignment operator,
556  * not a copy constructor).
557  * Returns TRUE if successful, else FALSE.
558  */
559 BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
560 {
561    assert(pPathDest!=NULL && pPathSrc!=NULL);
562
563    /* Make sure destination arrays are big enough */
564    if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
565       return FALSE;
566
567    /* Perform the copy operation */
568    memcpy(pPathDest->pPoints, pPathSrc->pPoints,
569       sizeof(POINT)*pPathSrc->numEntriesUsed);
570    memcpy(pPathDest->pFlags, pPathSrc->pFlags,
571       sizeof(BYTE)*pPathSrc->numEntriesUsed);
572
573    pPathDest->state=pPathSrc->state;
574    pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
575    pPathDest->newStroke=pPathSrc->newStroke;
576
577    return TRUE;
578 }
579
580 /* PATH_MoveTo
581  *
582  * Should be called when a MoveTo is performed on a DC that has an
583  * open path. This starts a new stroke. Returns TRUE if successful, else
584  * FALSE.
585  */
586 BOOL PATH_MoveTo(HDC hdc)
587 {
588    GdiPath *pPath;
589    
590    /* Get pointer to path */
591    if(!PATH_GetPathFromHDC(hdc, &pPath))
592       return FALSE;
593    
594    /* Check that path is open */
595    if(pPath->state!=PATH_Open)
596       /* FIXME: Do we have to call SetLastError? */
597       return FALSE;
598
599    /* Start a new stroke */
600    pPath->newStroke=TRUE;
601
602    return TRUE;
603 }
604
605 /* PATH_LineTo
606  *
607  * Should be called when a LineTo is performed on a DC that has an
608  * open path. This adds a PT_LINETO entry to the path (and possibly
609  * a PT_MOVETO entry, if this is the first LineTo in a stroke).
610  * Returns TRUE if successful, else FALSE.
611  */
612 BOOL PATH_LineTo(HDC hdc, INT x, INT y)
613 {
614    GdiPath *pPath;
615    POINT point, pointCurPos;
616    
617    /* Get pointer to path */
618    if(!PATH_GetPathFromHDC(hdc, &pPath))
619       return FALSE;
620    
621    /* Check that path is open */
622    if(pPath->state!=PATH_Open)
623       return FALSE;
624
625    /* Convert point to device coordinates */
626    point.x=x;
627    point.y=y;
628    if(!LPtoDP(hdc, &point, 1))
629       return FALSE;
630    
631    /* Add a PT_MOVETO if necessary */
632    if(pPath->newStroke)
633    {
634       pPath->newStroke=FALSE;
635       if(!GetCurrentPositionEx(hdc, &pointCurPos) ||
636          !LPtoDP(hdc, &pointCurPos, 1))
637          return FALSE;
638       if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
639          return FALSE;
640    }
641    
642    /* Add a PT_LINETO entry */
643    return PATH_AddEntry(pPath, &point, PT_LINETO);
644 }
645
646 /* PATH_Rectangle
647  *
648  * Should be called when a call to Rectangle is performed on a DC that has
649  * an open path. Returns TRUE if successful, else FALSE.
650  */
651 BOOL PATH_Rectangle(HDC hdc, INT x1, INT y1, INT x2, INT y2)
652 {
653    GdiPath *pPath;
654    POINT corners[2], pointTemp;
655    INT   temp;
656
657    /* Get pointer to path */
658    if(!PATH_GetPathFromHDC(hdc, &pPath))
659       return FALSE;
660    
661    /* Check that path is open */
662    if(pPath->state!=PATH_Open)
663       return FALSE;
664
665    /* Convert points to device coordinates */
666    corners[0].x=x1;
667    corners[0].y=y1;
668    corners[1].x=x2;
669    corners[1].y=y2;
670    if(!LPtoDP(hdc, corners, 2))
671       return FALSE;
672    
673    /* Make sure first corner is top left and second corner is bottom right */
674    if(corners[0].x>corners[1].x)
675    {
676       temp=corners[0].x;
677       corners[0].x=corners[1].x;
678       corners[1].x=temp;
679    }
680    if(corners[0].y>corners[1].y)
681    {
682       temp=corners[0].y;
683       corners[0].y=corners[1].y;
684       corners[1].y=temp;
685    }
686    
687    /* In GM_COMPATIBLE, don't include bottom and right edges */
688    if(GetGraphicsMode(hdc)==GM_COMPATIBLE)
689    {
690       corners[1].x--;
691       corners[1].y--;
692    }
693
694    /* Close any previous figure */
695    if(!CloseFigure(hdc))
696    {
697       /* The CloseFigure call shouldn't have failed */
698       assert(FALSE);
699       return FALSE;
700    }
701
702    /* Add four points to the path */
703    pointTemp.x=corners[1].x;
704    pointTemp.y=corners[0].y;
705    if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
706       return FALSE;
707    if(!PATH_AddEntry(pPath, corners, PT_LINETO))
708       return FALSE;
709    pointTemp.x=corners[0].x;
710    pointTemp.y=corners[1].y;
711    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
712       return FALSE;
713    if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
714       return FALSE;
715
716    /* Close the rectangle figure */
717    if(!CloseFigure(hdc))
718    {
719       /* The CloseFigure call shouldn't have failed */
720       assert(FALSE);
721       return FALSE;
722    }
723
724    return TRUE;
725 }
726
727 /* PATH_Ellipse
728  * 
729  * Should be called when a call to Ellipse is performed on a DC that has
730  * an open path. This adds four Bezier splines representing the ellipse
731  * to the path. Returns TRUE if successful, else FALSE.
732  */
733 BOOL PATH_Ellipse(HDC hdc, INT x1, INT y1, INT x2, INT y2)
734 {
735    /* TODO: This should probably be revised to call PATH_AngleArc */
736    /* (once it exists) */
737    return PATH_Arc(hdc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2);
738 }
739
740 /* PATH_Arc
741  *
742  * Should be called when a call to Arc is performed on a DC that has
743  * an open path. This adds up to five Bezier splines representing the arc
744  * to the path. Returns TRUE if successful, else FALSE.
745  */
746 BOOL PATH_Arc(HDC hdc, INT x1, INT y1, INT x2, INT y2,
747    INT xStart, INT yStart, INT xEnd, INT yEnd)
748 {
749    GdiPath     *pPath;
750    DC          *pDC;
751    double      angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
752                /* Initialize angleEndQuadrant to silence gcc's warning */
753    double      x, y;
754    FLOAT_POINT corners[2], pointStart, pointEnd;
755    BOOL      start, end;
756    INT       temp;
757
758    /* FIXME: This function should check for all possible error returns */
759    /* FIXME: Do we have to respect newStroke? */
760    
761    /* Get pointer to DC */
762    pDC=DC_GetDCPtr(hdc);
763    if(pDC==NULL)
764       return FALSE;
765
766    /* Get pointer to path */
767    if(!PATH_GetPathFromHDC(hdc, &pPath))
768       return FALSE;
769    
770    /* Check that path is open */
771    if(pPath->state!=PATH_Open)
772       return FALSE;
773
774    /* FIXME: Do we have to close the current figure? */
775    
776    /* Check for zero height / width */
777    /* FIXME: Only in GM_COMPATIBLE? */
778    if(x1==x2 || y1==y2)
779       return TRUE;
780    
781    /* Convert points to device coordinates */
782    corners[0].x=(FLOAT)x1;
783    corners[0].y=(FLOAT)y1;
784    corners[1].x=(FLOAT)x2;
785    corners[1].y=(FLOAT)y2;
786    pointStart.x=(FLOAT)xStart;
787    pointStart.y=(FLOAT)yStart;
788    pointEnd.x=(FLOAT)xEnd;
789    pointEnd.y=(FLOAT)yEnd;
790    INTERNAL_LPTODP_FLOAT(pDC, corners);
791    INTERNAL_LPTODP_FLOAT(pDC, corners+1);
792    INTERNAL_LPTODP_FLOAT(pDC, &pointStart);
793    INTERNAL_LPTODP_FLOAT(pDC, &pointEnd);
794
795    /* Make sure first corner is top left and second corner is bottom right */
796    if(corners[0].x>corners[1].x)
797    {
798       temp=corners[0].x;
799       corners[0].x=corners[1].x;
800       corners[1].x=temp;
801    }
802    if(corners[0].y>corners[1].y)
803    {
804       temp=corners[0].y;
805       corners[0].y=corners[1].y;
806       corners[1].y=temp;
807    }
808
809    /* Compute start and end angle */
810    PATH_NormalizePoint(corners, &pointStart, &x, &y);
811    angleStart=atan2(y, x);
812    PATH_NormalizePoint(corners, &pointEnd, &x, &y);
813    angleEnd=atan2(y, x);
814
815    /* Make sure the end angle is "on the right side" of the start angle */
816    if(GetArcDirection(hdc)==AD_CLOCKWISE)
817    {
818       if(angleEnd<=angleStart)
819       {
820          angleEnd+=2*M_PI;
821          assert(angleEnd>=angleStart);
822       }
823    }
824    else
825    {
826       if(angleEnd>=angleStart)
827       {
828          angleEnd-=2*M_PI;
829          assert(angleEnd<=angleStart);
830       }
831    }
832
833    /* In GM_COMPATIBLE, don't include bottom and right edges */
834    if(GetGraphicsMode(hdc)==GM_COMPATIBLE)
835    {
836       corners[1].x--;
837       corners[1].y--;
838    }
839    
840    /* Add the arc to the path with one Bezier spline per quadrant that the
841     * arc spans */
842    start=TRUE;
843    end=FALSE;
844    do
845    {
846       /* Determine the start and end angles for this quadrant */
847       if(start)
848       {
849          angleStartQuadrant=angleStart;
850          if(GetArcDirection(hdc)==AD_CLOCKWISE)
851             angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
852          else
853             angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
854       }
855       else
856       {
857          angleStartQuadrant=angleEndQuadrant;
858          if(GetArcDirection(hdc)==AD_CLOCKWISE)
859             angleEndQuadrant+=M_PI_2;
860          else
861             angleEndQuadrant-=M_PI_2;
862       }
863
864       /* Have we reached the last part of the arc? */
865       if((GetArcDirection(hdc)==AD_CLOCKWISE &&
866          angleEnd<angleEndQuadrant) ||
867          (GetArcDirection(hdc)==AD_COUNTERCLOCKWISE &&
868          angleEnd>angleEndQuadrant))
869       {
870          /* Adjust the end angle for this quadrant */
871          angleEndQuadrant=angleEnd;
872          end=TRUE;
873       }
874
875       /* Add the Bezier spline to the path */
876       PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant,
877          start);
878       start=FALSE;
879    }  while(!end);
880
881    return TRUE;
882 }
883
884 BOOL PATH_PolyBezierTo(HDC hdc, const POINT *pts, DWORD cbPoints)
885 {
886    GdiPath     *pPath;
887    POINT       pt;
888    INT         i;
889
890    if(!PATH_GetPathFromHDC(hdc, &pPath))
891       return FALSE;
892    
893    /* Check that path is open */
894    if(pPath->state!=PATH_Open)
895       return FALSE;
896
897    /* Add a PT_MOVETO if necessary */
898    if(pPath->newStroke)
899    {
900       pPath->newStroke=FALSE;
901       if(!GetCurrentPositionEx(hdc, &pt) ||
902          !LPtoDP(hdc, &pt, 1))
903          return FALSE;
904       if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
905          return FALSE;
906    }
907
908    for(i = 0; i < cbPoints; i++) {
909        pt = pts[i];
910        if(!LPtoDP(hdc, &pt, 1))
911            return FALSE;
912        PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
913    }
914    return TRUE;
915 }
916    
917 BOOL PATH_PolyBezier(HDC hdc, const POINT *pts, DWORD cbPoints)
918 {
919    GdiPath     *pPath;
920    POINT       pt;
921    INT         i;
922
923    if(!PATH_GetPathFromHDC(hdc, &pPath))
924       return FALSE;
925    
926    /* Check that path is open */
927    if(pPath->state!=PATH_Open)
928       return FALSE;
929
930    for(i = 0; i < cbPoints; i++) {
931        pt = pts[i];
932        if(!LPtoDP(hdc, &pt, 1))
933            return FALSE;
934        PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO);
935    }
936    return TRUE;
937 }
938
939 BOOL PATH_Polyline(HDC hdc, const POINT *pts, DWORD cbPoints)
940 {
941    GdiPath     *pPath;
942    POINT       pt;
943    INT         i;
944
945    if(!PATH_GetPathFromHDC(hdc, &pPath))
946       return FALSE;
947    
948    /* Check that path is open */
949    if(pPath->state!=PATH_Open)
950       return FALSE;
951
952    for(i = 0; i < cbPoints; i++) {
953        pt = pts[i];
954        if(!LPtoDP(hdc, &pt, 1))
955            return FALSE;
956        PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
957    }
958    return TRUE;
959 }
960    
961 BOOL PATH_PolylineTo(HDC hdc, const POINT *pts, DWORD cbPoints)
962 {
963    GdiPath     *pPath;
964    POINT       pt;
965    INT         i;
966
967    if(!PATH_GetPathFromHDC(hdc, &pPath))
968       return FALSE;
969    
970    /* Check that path is open */
971    if(pPath->state!=PATH_Open)
972       return FALSE;
973
974    /* Add a PT_MOVETO if necessary */
975    if(pPath->newStroke)
976    {
977       pPath->newStroke=FALSE;
978       if(!GetCurrentPositionEx(hdc, &pt) ||
979          !LPtoDP(hdc, &pt, 1))
980          return FALSE;
981       if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
982          return FALSE;
983    }
984
985    for(i = 0; i < cbPoints; i++) {
986        pt = pts[i];
987        if(!LPtoDP(hdc, &pt, 1))
988            return FALSE;
989        PATH_AddEntry(pPath, &pt, PT_LINETO);
990    }
991
992    return TRUE;
993 }
994
995
996 BOOL PATH_Polygon(HDC hdc, const POINT *pts, DWORD cbPoints)
997 {
998    GdiPath     *pPath;
999    POINT       pt;
1000    INT         i;
1001
1002    if(!PATH_GetPathFromHDC(hdc, &pPath))
1003       return FALSE;
1004    
1005    /* Check that path is open */
1006    if(pPath->state!=PATH_Open)
1007       return FALSE;
1008
1009    for(i = 0; i < cbPoints; i++) {
1010        pt = pts[i];
1011        if(!LPtoDP(hdc, &pt, 1))
1012            return FALSE;
1013        PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
1014                      ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
1015                       PT_LINETO));
1016    }
1017    return TRUE;
1018 }
1019
1020 BOOL PATH_PolyPolygon( HDC hdc, const POINT* pts, const INT* counts,
1021                        UINT polygons )
1022 {
1023    GdiPath     *pPath;
1024    POINT       pt, startpt;
1025    INT         poly, point, i;
1026
1027    if(!PATH_GetPathFromHDC(hdc, &pPath))
1028       return FALSE;
1029    
1030    /* Check that path is open */
1031    if(pPath->state!=PATH_Open)
1032       return FALSE;
1033
1034    for(i = 0, poly = 0; poly < polygons; poly++) {
1035        for(point = 0; point < counts[poly]; point++, i++) {
1036            pt = pts[i];
1037            if(!LPtoDP(hdc, &pt, 1))
1038                return FALSE;
1039            if(point == 0) startpt = pt;
1040            PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1041        }
1042        /* win98 adds an extra line to close the figure for some reason */
1043        PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
1044    }
1045    return TRUE;
1046 }
1047
1048 BOOL PATH_PolyPolyline( HDC hdc, const POINT* pts, const DWORD* counts,
1049                         DWORD polylines )
1050 {
1051    GdiPath     *pPath;
1052    POINT       pt;
1053    INT         poly, point, i;
1054
1055    if(!PATH_GetPathFromHDC(hdc, &pPath))
1056       return FALSE;
1057    
1058    /* Check that path is open */
1059    if(pPath->state!=PATH_Open)
1060       return FALSE;
1061
1062    for(i = 0, poly = 0; poly < polylines; poly++) {
1063        for(point = 0; point < counts[poly]; point++, i++) {
1064            pt = pts[i];
1065            if(!LPtoDP(hdc, &pt, 1))
1066                return FALSE;
1067            PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1068        }
1069    }
1070    return TRUE;
1071 }
1072    
1073 /***********************************************************************
1074  * Internal functions
1075  */
1076
1077
1078 /* PATH_AddFlatBezier
1079  *
1080  */
1081 static BOOL PATH_AddFlatBezier(GdiPath *pPath, POINT *pt, BOOL closed)
1082 {
1083     POINT *pts;
1084     INT no, i;
1085
1086     pts = GDI_Bezier( pt, 4, &no );
1087     if(!pts) return FALSE;
1088
1089     for(i = 1; i < no; i++)
1090         PATH_AddEntry(pPath, &pts[i], 
1091             (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
1092     HeapFree( GetProcessHeap(), 0, pts );
1093     return TRUE;
1094 }
1095
1096 /* PATH_FlattenPath
1097  *
1098  * Replaces Beziers with line segments
1099  *
1100  */
1101 static BOOL PATH_FlattenPath(GdiPath *pPath)
1102 {
1103     GdiPath newPath;
1104     INT srcpt;
1105
1106     memset(&newPath, 0, sizeof(newPath));
1107     newPath.state = PATH_Open;
1108     for(srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++) {
1109         switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE) {
1110         case PT_MOVETO:
1111         case PT_LINETO:
1112             PATH_AddEntry(&newPath, &pPath->pPoints[srcpt],
1113                           pPath->pFlags[srcpt]);
1114             break;
1115         case PT_BEZIERTO:
1116           PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1],
1117                              pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
1118             srcpt += 2;
1119             break;
1120         }
1121     }
1122     newPath.state = PATH_Closed;
1123     PATH_AssignGdiPath(pPath, &newPath);
1124     PATH_EmptyPath(&newPath);
1125     return TRUE;
1126 }
1127           
1128 /* PATH_PathToRegion
1129  *
1130  * Creates a region from the specified path using the specified polygon
1131  * filling mode. The path is left unchanged. A handle to the region that
1132  * was created is stored in *pHrgn. If successful, TRUE is returned; if an
1133  * error occurs, SetLastError is called with the appropriate value and
1134  * FALSE is returned.
1135  */
1136 static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode,
1137    HRGN *pHrgn)
1138 {
1139    int    numStrokes, iStroke, i;
1140    INT  *pNumPointsInStroke;
1141    HRGN hrgn;
1142
1143    assert(pPath!=NULL);
1144    assert(pHrgn!=NULL);
1145
1146    PATH_FlattenPath(pPath);
1147
1148    /* FIXME: What happens when number of points is zero? */
1149    
1150    /* First pass: Find out how many strokes there are in the path */
1151    /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
1152    numStrokes=0;
1153    for(i=0; i<pPath->numEntriesUsed; i++)
1154       if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1155          numStrokes++;
1156
1157    /* Allocate memory for number-of-points-in-stroke array */
1158    pNumPointsInStroke=(int *)HeapAlloc( GetProcessHeap(), 0, 
1159                                         sizeof(int) * numStrokes );
1160    if(!pNumPointsInStroke)
1161    {
1162       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1163       return FALSE;
1164    }
1165    
1166    /* Second pass: remember number of points in each polygon */
1167    iStroke=-1;  /* Will get incremented to 0 at beginning of first stroke */
1168    for(i=0; i<pPath->numEntriesUsed; i++)
1169    {
1170       /* Is this the beginning of a new stroke? */
1171       if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1172       {
1173          iStroke++;
1174          pNumPointsInStroke[iStroke]=0;
1175       }
1176
1177       pNumPointsInStroke[iStroke]++;
1178    }
1179
1180    /* Create a region from the strokes */
1181    hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
1182       numStrokes, nPolyFillMode);
1183    if(hrgn==(HRGN)0)
1184    {
1185       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1186       return FALSE;
1187    }
1188
1189    /* Free memory for number-of-points-in-stroke array */
1190    HeapFree( GetProcessHeap(), 0, pNumPointsInStroke );
1191
1192    /* Success! */
1193    *pHrgn=hrgn;
1194    return TRUE;
1195 }
1196
1197 /* PATH_EmptyPath
1198  *
1199  * Removes all entries from the path and sets the path state to PATH_Null.
1200  */
1201 static void PATH_EmptyPath(GdiPath *pPath)
1202 {
1203    assert(pPath!=NULL);
1204
1205    pPath->state=PATH_Null;
1206    pPath->numEntriesUsed=0;
1207 }
1208
1209 /* PATH_AddEntry
1210  *
1211  * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
1212  * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
1213  * successful, FALSE otherwise (e.g. if not enough memory was available).
1214  */
1215 BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint, BYTE flags)
1216 {
1217    assert(pPath!=NULL);
1218    
1219    /* FIXME: If newStroke is true, perhaps we want to check that we're
1220     * getting a PT_MOVETO
1221     */
1222    TRACE("(%ld,%ld) - %d\n", pPoint->x, pPoint->y, flags);
1223
1224    /* Check that path is open */
1225    if(pPath->state!=PATH_Open)
1226       return FALSE;
1227    
1228    /* Reserve enough memory for an extra path entry */
1229    if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
1230       return FALSE;
1231
1232    /* Store information in path entry */
1233    pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
1234    pPath->pFlags[pPath->numEntriesUsed]=flags;
1235
1236    /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
1237    if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
1238       pPath->newStroke=TRUE;
1239
1240    /* Increment entry count */
1241    pPath->numEntriesUsed++;
1242
1243    return TRUE;
1244 }
1245
1246 /* PATH_ReserveEntries
1247  *
1248  * Ensures that at least "numEntries" entries (for points and flags) have
1249  * been allocated; allocates larger arrays and copies the existing entries
1250  * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
1251  */
1252 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries)
1253 {
1254    INT   numEntriesToAllocate;
1255    POINT *pPointsNew;
1256    BYTE    *pFlagsNew;
1257    
1258    assert(pPath!=NULL);
1259    assert(numEntries>=0);
1260
1261    /* Do we have to allocate more memory? */
1262    if(numEntries > pPath->numEntriesAllocated)
1263    {
1264       /* Find number of entries to allocate. We let the size of the array
1265        * grow exponentially, since that will guarantee linear time
1266        * complexity. */
1267       if(pPath->numEntriesAllocated)
1268       {
1269          numEntriesToAllocate=pPath->numEntriesAllocated;
1270          while(numEntriesToAllocate<numEntries)
1271             numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/
1272                GROW_FACTOR_DENOM;
1273       }
1274       else
1275          numEntriesToAllocate=numEntries;
1276
1277       /* Allocate new arrays */
1278       pPointsNew=(POINT *)HeapAlloc( GetProcessHeap(), 0, 
1279                                      numEntriesToAllocate * sizeof(POINT) );
1280       if(!pPointsNew)
1281          return FALSE;
1282       pFlagsNew=(BYTE *)HeapAlloc( GetProcessHeap(), 0, 
1283                                    numEntriesToAllocate * sizeof(BYTE) );
1284       if(!pFlagsNew)
1285       {
1286          HeapFree( GetProcessHeap(), 0, pPointsNew );
1287          return FALSE;
1288       }
1289
1290       /* Copy old arrays to new arrays and discard old arrays */
1291       if(pPath->pPoints)
1292       {
1293          assert(pPath->pFlags);
1294
1295          memcpy(pPointsNew, pPath->pPoints,
1296              sizeof(POINT)*pPath->numEntriesUsed);
1297          memcpy(pFlagsNew, pPath->pFlags,
1298              sizeof(BYTE)*pPath->numEntriesUsed);
1299
1300          HeapFree( GetProcessHeap(), 0, pPath->pPoints );
1301          HeapFree( GetProcessHeap(), 0, pPath->pFlags );
1302       }
1303       pPath->pPoints=pPointsNew;
1304       pPath->pFlags=pFlagsNew;
1305       pPath->numEntriesAllocated=numEntriesToAllocate;
1306    }
1307
1308    return TRUE;
1309 }
1310
1311 /* PATH_GetPathFromHDC
1312  *
1313  * Retrieves a pointer to the GdiPath structure contained in an HDC and
1314  * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
1315  */
1316 static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath)
1317 {
1318    DC *pDC;
1319
1320    pDC=DC_GetDCPtr(hdc);
1321    if(pDC)
1322    {
1323       *ppPath=&pDC->w.path;
1324       return TRUE;
1325    }
1326    else
1327       return FALSE;
1328 }
1329
1330 /* PATH_DoArcPart
1331  *
1332  * Creates a Bezier spline that corresponds to part of an arc and appends the
1333  * corresponding points to the path. The start and end angles are passed in
1334  * "angleStart" and "angleEnd"; these angles should span a quarter circle
1335  * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
1336  * point is added to the path; otherwise, it is assumed that the current
1337  * position is equal to the first control point.
1338  */
1339 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
1340    double angleStart, double angleEnd, BOOL addMoveTo)
1341 {
1342    double  halfAngle, a;
1343    double  xNorm[4], yNorm[4];
1344    POINT point;
1345    int     i;
1346
1347    assert(fabs(angleEnd-angleStart)<=M_PI_2);
1348
1349    /* FIXME: Is there an easier way of computing this? */
1350
1351    /* Compute control points */
1352    halfAngle=(angleEnd-angleStart)/2.0;
1353    if(fabs(halfAngle)>1e-8)
1354    {
1355       a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
1356       xNorm[0]=cos(angleStart);
1357       yNorm[0]=sin(angleStart);
1358       xNorm[1]=xNorm[0] - a*yNorm[0];
1359       yNorm[1]=yNorm[0] + a*xNorm[0];
1360       xNorm[3]=cos(angleEnd);
1361       yNorm[3]=sin(angleEnd);
1362       xNorm[2]=xNorm[3] + a*yNorm[3];
1363       yNorm[2]=yNorm[3] - a*xNorm[3];
1364    }
1365    else
1366       for(i=0; i<4; i++)
1367       {
1368          xNorm[i]=cos(angleStart);
1369          yNorm[i]=sin(angleStart);
1370       }
1371    
1372    /* Add starting point to path if desired */
1373    if(addMoveTo)
1374    {
1375       PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
1376       if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
1377          return FALSE;
1378    }
1379
1380    /* Add remaining control points */
1381    for(i=1; i<4; i++)
1382    {
1383       PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
1384       if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1385          return FALSE;
1386    }
1387
1388    return TRUE;
1389 }
1390
1391 /* PATH_ScaleNormalizedPoint
1392  *
1393  * Scales a normalized point (x, y) with respect to the box whose corners are
1394  * passed in "corners". The point is stored in "*pPoint". The normalized
1395  * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1396  * (1.0, 1.0) correspond to corners[1].
1397  */
1398 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
1399    double y, POINT *pPoint)
1400 {
1401    pPoint->x=GDI_ROUND( (double)corners[0].x +
1402       (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
1403    pPoint->y=GDI_ROUND( (double)corners[0].y +
1404       (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
1405 }
1406
1407 /* PATH_NormalizePoint
1408  *
1409  * Normalizes a point with respect to the box whose corners are passed in
1410  * "corners". The normalized coordinates are stored in "*pX" and "*pY".
1411  */
1412 static void PATH_NormalizePoint(FLOAT_POINT corners[],
1413    const FLOAT_POINT *pPoint,
1414    double *pX, double *pY)
1415 {
1416    *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) *
1417       2.0 - 1.0;
1418    *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) *
1419       2.0 - 1.0;
1420 }
1421
1422 /*******************************************************************
1423  *      FlattenPath16 [GDI.516]
1424  *
1425  *
1426  */
1427 BOOL16 WINAPI FlattenPath16(HDC16 hdc)
1428 {
1429   return (BOOL16) FlattenPath((HDC) hdc);
1430 }
1431
1432 /*******************************************************************
1433  *      FlattenPath [GDI32.103]
1434  *
1435  *
1436  */
1437 BOOL WINAPI FlattenPath(HDC hdc)
1438 {
1439    DC *dc = DC_GetDCPtr( hdc );
1440    GdiPath *pPath;
1441    TRACE("%08x\n", hdc);
1442
1443    if(!dc) {
1444      SetLastError(ERROR_INVALID_HANDLE);
1445      return FALSE;
1446    }
1447
1448    if(dc->funcs->pFlattenPath)
1449      return dc->funcs->pFlattenPath(dc);
1450
1451     pPath = &dc->w.path;
1452     if(pPath->state != PATH_Closed)
1453         return FALSE;
1454    return PATH_FlattenPath(pPath);
1455 }
1456
1457 /*******************************************************************
1458  *      StrokeAndFillPath16 [GDI.520]
1459  *
1460  *
1461  */
1462 BOOL16 WINAPI StrokeAndFillPath16(HDC16 hdc)
1463 {
1464   return (BOOL16) StrokeAndFillPath((HDC) hdc);
1465 }
1466
1467 /*******************************************************************
1468  *      StrokeAndFillPath [GDI32.352]
1469  *
1470  *
1471  */
1472 BOOL WINAPI StrokeAndFillPath(HDC hdc)
1473 {
1474    DC *dc = DC_GetDCPtr( hdc );
1475    
1476    if(!dc) {
1477      SetLastError(ERROR_INVALID_HANDLE);
1478      return FALSE;
1479    }
1480
1481    if(dc->funcs->pStrokeAndFillPath)
1482      return dc->funcs->pStrokeAndFillPath(dc);
1483
1484    FIXME("stub\n");
1485    return StrokePath(hdc);
1486 }
1487
1488 /*******************************************************************
1489  *      StrokePath16 [GDI.521]
1490  *
1491  *
1492  */
1493 BOOL16 WINAPI StrokePath16(HDC16 hdc)
1494 {
1495   return (BOOL16) StrokePath((HDC) hdc);
1496 }
1497
1498 /*******************************************************************
1499  *      StrokePath [GDI32.353]
1500  *
1501  *
1502  */
1503 BOOL WINAPI StrokePath(HDC hdc)
1504 {
1505     DC *dc = DC_GetDCPtr( hdc );
1506     GdiPath *pPath;
1507     INT i;
1508     POINT ptLastMove = {0,0};
1509
1510     TRACE("(%08x)\n", hdc);
1511     if(!dc) {
1512         SetLastError(ERROR_INVALID_HANDLE);
1513         return FALSE;
1514     }
1515
1516     if(dc->funcs->pStrokePath)
1517         return dc->funcs->pStrokePath(dc);
1518
1519     pPath = &dc->w.path;
1520     if(pPath->state != PATH_Closed)
1521         return FALSE;
1522
1523     SaveDC(hdc);
1524     SetMapMode(hdc, MM_TEXT);
1525     SetViewportOrgEx(hdc, 0, 0, NULL);
1526     SetWindowOrgEx(hdc, 0, 0, NULL);
1527     for(i = 0; i < pPath->numEntriesUsed; i++) {
1528         switch(pPath->pFlags[i]) {
1529         case PT_MOVETO:
1530             TRACE("Got PT_MOVETO (%ld, %ld)\n",
1531                   pPath->pPoints[i].x, pPath->pPoints[i].y);
1532             MoveToEx(hdc, pPath->pPoints[i].x, pPath->pPoints[i].y, NULL);
1533             ptLastMove = pPath->pPoints[i];
1534             break;
1535         case PT_LINETO:
1536         case (PT_LINETO | PT_CLOSEFIGURE):
1537             TRACE("Got PT_LINETO (%ld, %ld)\n",
1538                   pPath->pPoints[i].x, pPath->pPoints[i].y);
1539             LineTo(hdc, pPath->pPoints[i].x, pPath->pPoints[i].y);
1540             break;
1541         case PT_BEZIERTO:
1542             TRACE("Got PT_BEZIERTO\n");
1543             if(pPath->pFlags[i+1] != PT_BEZIERTO || 
1544                (pPath->pFlags[i+2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) {
1545                 ERR("Path didn't contain 3 successive PT_BEZIERTOs\n");
1546                 return FALSE;
1547             }
1548             PolyBezierTo(hdc, &pPath->pPoints[i], 3);
1549             i += 2;
1550             break;
1551         default:
1552             ERR("Got path flag %d\n", (INT)pPath->pFlags[i]);
1553             return FALSE;
1554         }
1555         if(pPath->pFlags[i] & PT_CLOSEFIGURE)
1556             LineTo(hdc, ptLastMove.x, ptLastMove.y);
1557     }
1558     RestoreDC(hdc, -1);
1559     PATH_EmptyPath(pPath);
1560     return TRUE;
1561 }
1562
1563 /*******************************************************************
1564  *      WidenPath16 [GDI.522]
1565  *
1566  *
1567  */
1568 BOOL16 WINAPI WidenPath16(HDC16 hdc)
1569 {
1570   return (BOOL16) WidenPath((HDC) hdc);
1571 }
1572
1573 /*******************************************************************
1574  *      WidenPath [GDI32.360]
1575  *
1576  *
1577  */
1578 BOOL WINAPI WidenPath(HDC hdc)
1579 {
1580    DC *dc = DC_GetDCPtr( hdc );
1581    
1582    if(!dc) {
1583      SetLastError(ERROR_INVALID_HANDLE);
1584      return FALSE;
1585    }
1586
1587    if(dc->funcs->pWidenPath)
1588      return dc->funcs->pWidenPath(dc);
1589
1590    FIXME("stub\n");
1591    return 0;
1592 }