Don't flag failed opens of unsupported formats as an error.
[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  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <math.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #if defined(HAVE_FLOAT_H)
30 #include <float.h>
31 #endif
32
33 #include "windef.h"
34 #include "winbase.h"
35 #include "wingdi.h"
36 #include "winerror.h"
37
38 #include "gdi.h"
39 #include "wine/debug.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(gdi);
42
43 /* Notes on the implementation
44  *
45  * The implementation is based on dynamically resizable arrays of points and
46  * flags. I dithered for a bit before deciding on this implementation, and
47  * I had even done a bit of work on a linked list version before switching
48  * to arrays. It's a bit of a tradeoff. When you use linked lists, the
49  * implementation of FlattenPath is easier, because you can rip the
50  * PT_BEZIERTO entries out of the middle of the list and link the
51  * corresponding PT_LINETO entries in. However, when you use arrays,
52  * PathToRegion becomes easier, since you can essentially just pass your array
53  * of points to CreatePolyPolygonRgn. Also, if I'd used linked lists, I would
54  * have had the extra effort of creating a chunk-based allocation scheme
55  * in order to use memory effectively. That's why I finally decided to use
56  * arrays. Note by the way that the array based implementation has the same
57  * linear time complexity that linked lists would have since the arrays grow
58  * exponentially.
59  *
60  * The points are stored in the path in device coordinates. This is
61  * consistent with the way Windows does things (for instance, see the Win32
62  * SDK documentation for GetPath).
63  *
64  * The word "stroke" appears in several places (e.g. in the flag
65  * GdiPath.newStroke). A stroke consists of a PT_MOVETO followed by one or
66  * more PT_LINETOs or PT_BEZIERTOs, up to, but not including, the next
67  * PT_MOVETO. Note that this is not the same as the definition of a figure;
68  * a figure can contain several strokes.
69  *
70  * I modified the drawing functions (MoveTo, LineTo etc.) to test whether
71  * the path is open and to call the corresponding function in path.c if this
72  * is the case. A more elegant approach would be to modify the function
73  * pointers in the DC_FUNCTIONS structure; however, this would be a lot more
74  * complex. Also, the performance degradation caused by my approach in the
75  * case where no path is open is so small that it cannot be measured.
76  *
77  * Martin Boehme
78  */
79
80 /* FIXME: A lot of stuff isn't implemented yet. There is much more to come. */
81
82 #define NUM_ENTRIES_INITIAL 16  /* Initial size of points / flags arrays  */
83 #define GROW_FACTOR_NUMER    2  /* Numerator of grow factor for the array */
84 #define GROW_FACTOR_DENOM    1  /* Denominator of grow factor             */
85
86 /* A floating point version of the POINT structure */
87 typedef struct tagFLOAT_POINT
88 {
89    FLOAT x, y;
90 } FLOAT_POINT;
91
92
93 static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode,
94    HRGN *pHrgn);
95 static void   PATH_EmptyPath(GdiPath *pPath);
96 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries);
97 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
98    double angleStart, double angleEnd, BOOL addMoveTo);
99 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
100    double y, POINT *pPoint);
101 static void PATH_NormalizePoint(FLOAT_POINT corners[], const FLOAT_POINT
102    *pPoint, double *pX, double *pY);
103 static BOOL PATH_CheckCorners(DC *dc, POINT corners[], INT x1, INT y1, INT x2, INT y2);
104
105 /* Performs a world-to-viewport transformation on the specified point (which
106  * is in floating point format).
107  */
108 static inline void WINE_UNUSED INTERNAL_LPTODP_FLOAT(DC *dc, FLOAT_POINT *point)
109 {
110     FLOAT x, y;
111
112     /* Perform the transformation */
113     x = point->x;
114     y = point->y;
115     point->x = x * dc->xformWorld2Vport.eM11 +
116                y * dc->xformWorld2Vport.eM21 +
117                dc->xformWorld2Vport.eDx;
118     point->y = x * dc->xformWorld2Vport.eM12 +
119                y * dc->xformWorld2Vport.eM22 +
120                dc->xformWorld2Vport.eDy;
121 }
122
123
124 /***********************************************************************
125  *           BeginPath    (GDI32.@)
126  */
127 BOOL WINAPI BeginPath(HDC hdc)
128 {
129     BOOL ret = TRUE;
130     DC *dc = DC_GetDCPtr( hdc );
131
132     if(!dc) return FALSE;
133
134     if(dc->funcs->pBeginPath)
135         ret = dc->funcs->pBeginPath(dc->physDev);
136     else
137     {
138         /* If path is already open, do nothing */
139         if(dc->path.state != PATH_Open)
140         {
141             /* Make sure that path is empty */
142             PATH_EmptyPath(&dc->path);
143
144             /* Initialize variables for new path */
145             dc->path.newStroke=TRUE;
146             dc->path.state=PATH_Open;
147         }
148     }
149     GDI_ReleaseObj( hdc );
150     return ret;
151 }
152
153
154 /***********************************************************************
155  *           EndPath    (GDI32.@)
156  */
157 BOOL WINAPI EndPath(HDC hdc)
158 {
159     BOOL ret = TRUE;
160     DC *dc = DC_GetDCPtr( hdc );
161
162     if(!dc) return FALSE;
163
164     if(dc->funcs->pEndPath)
165         ret = dc->funcs->pEndPath(dc->physDev);
166     else
167     {
168         /* Check that path is currently being constructed */
169         if(dc->path.state!=PATH_Open)
170         {
171             SetLastError(ERROR_CAN_NOT_COMPLETE);
172             ret = FALSE;
173         }
174         /* Set flag to indicate that path is finished */
175         else dc->path.state=PATH_Closed;
176     }
177     GDI_ReleaseObj( hdc );
178     return ret;
179 }
180
181
182 /******************************************************************************
183  * AbortPath [GDI32.@]
184  * Closes and discards paths from device context
185  *
186  * NOTES
187  *    Check that SetLastError is being called correctly
188  *
189  * PARAMS
190  *    hdc [I] Handle to device context
191  *
192  * RETURNS STD
193  */
194 BOOL WINAPI AbortPath( HDC hdc )
195 {
196     BOOL ret = TRUE;
197     DC *dc = DC_GetDCPtr( hdc );
198
199     if(!dc) return FALSE;
200
201     if(dc->funcs->pAbortPath)
202         ret = dc->funcs->pAbortPath(dc->physDev);
203     else /* Remove all entries from the path */
204         PATH_EmptyPath( &dc->path );
205     GDI_ReleaseObj( hdc );
206     return ret;
207 }
208
209
210 /***********************************************************************
211  *           CloseFigure    (GDI32.@)
212  *
213  * FIXME: Check that SetLastError is being called correctly
214  */
215 BOOL WINAPI CloseFigure(HDC hdc)
216 {
217     BOOL ret = TRUE;
218     DC *dc = DC_GetDCPtr( hdc );
219
220     if(!dc) return FALSE;
221
222     if(dc->funcs->pCloseFigure)
223         ret = dc->funcs->pCloseFigure(dc->physDev);
224     else
225     {
226         /* Check that path is open */
227         if(dc->path.state!=PATH_Open)
228         {
229             SetLastError(ERROR_CAN_NOT_COMPLETE);
230             ret = FALSE;
231         }
232         else
233         {
234             /* FIXME: Shouldn't we draw a line to the beginning of the
235                figure? */
236             /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
237             if(dc->path.numEntriesUsed)
238             {
239                 dc->path.pFlags[dc->path.numEntriesUsed-1]|=PT_CLOSEFIGURE;
240                 dc->path.newStroke=TRUE;
241             }
242         }
243     }
244     GDI_ReleaseObj( hdc );
245     return ret;
246 }
247
248
249 /***********************************************************************
250  *           GetPath    (GDI32.@)
251  */
252 INT WINAPI GetPath(HDC hdc, LPPOINT pPoints, LPBYTE pTypes,
253    INT nSize)
254 {
255    INT ret = -1;
256    GdiPath *pPath;
257    DC *dc = DC_GetDCPtr( hdc );
258
259    if(!dc) return -1;
260
261    pPath = &dc->path;
262
263    /* Check that path is closed */
264    if(pPath->state!=PATH_Closed)
265    {
266       SetLastError(ERROR_CAN_NOT_COMPLETE);
267       goto done;
268    }
269
270    if(nSize==0)
271       ret = pPath->numEntriesUsed;
272    else if(nSize<pPath->numEntriesUsed)
273    {
274       SetLastError(ERROR_INVALID_PARAMETER);
275       goto done;
276    }
277    else
278    {
279       memcpy(pPoints, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
280       memcpy(pTypes, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
281
282       /* Convert the points to logical coordinates */
283       if(!DPtoLP(hdc, pPoints, pPath->numEntriesUsed))
284       {
285          /* FIXME: Is this the correct value? */
286          SetLastError(ERROR_CAN_NOT_COMPLETE);
287         goto done;
288       }
289      else ret = pPath->numEntriesUsed;
290    }
291  done:
292    GDI_ReleaseObj( hdc );
293    return ret;
294 }
295
296
297 /***********************************************************************
298  *           PathToRegion    (GDI32.@)
299  *
300  * FIXME
301  *   Check that SetLastError is being called correctly
302  *
303  * The documentation does not state this explicitly, but a test under Windows
304  * shows that the region which is returned should be in device coordinates.
305  */
306 HRGN WINAPI PathToRegion(HDC hdc)
307 {
308    GdiPath *pPath;
309    HRGN  hrgnRval = 0;
310    DC *dc = DC_GetDCPtr( hdc );
311
312    /* Get pointer to path */
313    if(!dc) return 0;
314
315     pPath = &dc->path;
316
317    /* Check that path is closed */
318    if(pPath->state!=PATH_Closed) SetLastError(ERROR_CAN_NOT_COMPLETE);
319    else
320    {
321        /* FIXME: Should we empty the path even if conversion failed? */
322        if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnRval))
323            PATH_EmptyPath(pPath);
324        else
325            hrgnRval=0;
326    }
327    GDI_ReleaseObj( hdc );
328    return hrgnRval;
329 }
330
331 static BOOL PATH_FillPath(DC *dc, GdiPath *pPath)
332 {
333    INT   mapMode, graphicsMode;
334    SIZE  ptViewportExt, ptWindowExt;
335    POINT ptViewportOrg, ptWindowOrg;
336    XFORM xform;
337    HRGN  hrgn;
338
339    if(dc->funcs->pFillPath)
340        return dc->funcs->pFillPath(dc->physDev);
341
342    /* Check that path is closed */
343    if(pPath->state!=PATH_Closed)
344    {
345       SetLastError(ERROR_CAN_NOT_COMPLETE);
346       return FALSE;
347    }
348
349    /* Construct a region from the path and fill it */
350    if(PATH_PathToRegion(pPath, dc->polyFillMode, &hrgn))
351    {
352       /* Since PaintRgn interprets the region as being in logical coordinates
353        * but the points we store for the path are already in device
354        * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
355        * Using SaveDC to save information about the mapping mode / world
356        * transform would be easier but would require more overhead, especially
357        * now that SaveDC saves the current path.
358        */
359
360       /* Save the information about the old mapping mode */
361       mapMode=GetMapMode(dc->hSelf);
362       GetViewportExtEx(dc->hSelf, &ptViewportExt);
363       GetViewportOrgEx(dc->hSelf, &ptViewportOrg);
364       GetWindowExtEx(dc->hSelf, &ptWindowExt);
365       GetWindowOrgEx(dc->hSelf, &ptWindowOrg);
366
367       /* Save world transform
368        * NB: The Windows documentation on world transforms would lead one to
369        * believe that this has to be done only in GM_ADVANCED; however, my
370        * tests show that resetting the graphics mode to GM_COMPATIBLE does
371        * not reset the world transform.
372        */
373       GetWorldTransform(dc->hSelf, &xform);
374
375       /* Set MM_TEXT */
376       SetMapMode(dc->hSelf, MM_TEXT);
377       SetViewportOrgEx(dc->hSelf, 0, 0, NULL);
378       SetWindowOrgEx(dc->hSelf, 0, 0, NULL);
379       graphicsMode=GetGraphicsMode(dc->hSelf);
380       SetGraphicsMode(dc->hSelf, GM_ADVANCED);
381       ModifyWorldTransform(dc->hSelf, &xform, MWT_IDENTITY);
382       SetGraphicsMode(dc->hSelf, graphicsMode);
383
384       /* Paint the region */
385       PaintRgn(dc->hSelf, hrgn);
386       DeleteObject(hrgn);
387       /* Restore the old mapping mode */
388       SetMapMode(dc->hSelf, mapMode);
389       SetViewportExtEx(dc->hSelf, ptViewportExt.cx, ptViewportExt.cy, NULL);
390       SetViewportOrgEx(dc->hSelf, ptViewportOrg.x, ptViewportOrg.y, NULL);
391       SetWindowExtEx(dc->hSelf, ptWindowExt.cx, ptWindowExt.cy, NULL);
392       SetWindowOrgEx(dc->hSelf, ptWindowOrg.x, ptWindowOrg.y, NULL);
393
394       /* Go to GM_ADVANCED temporarily to restore the world transform */
395       graphicsMode=GetGraphicsMode(dc->hSelf);
396       SetGraphicsMode(dc->hSelf, GM_ADVANCED);
397       SetWorldTransform(dc->hSelf, &xform);
398       SetGraphicsMode(dc->hSelf, graphicsMode);
399       return TRUE;
400    }
401    return FALSE;
402 }
403
404
405 /***********************************************************************
406  *           FillPath    (GDI32.@)
407  *
408  * FIXME
409  *    Check that SetLastError is being called correctly
410  */
411 BOOL WINAPI FillPath(HDC hdc)
412 {
413     DC *dc = DC_GetDCPtr( hdc );
414     BOOL bRet = FALSE;
415
416     if(!dc) return FALSE;
417
418     if(dc->funcs->pFillPath)
419         bRet = dc->funcs->pFillPath(dc->physDev);
420     else
421     {
422         bRet = PATH_FillPath(dc, &dc->path);
423         if(bRet)
424         {
425             /* FIXME: Should the path be emptied even if conversion
426                failed? */
427             PATH_EmptyPath(&dc->path);
428         }
429     }
430     GDI_ReleaseObj( hdc );
431     return bRet;
432 }
433
434
435 /***********************************************************************
436  *           SelectClipPath    (GDI32.@)
437  * FIXME
438  *  Check that SetLastError is being called correctly
439  */
440 BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
441 {
442    GdiPath *pPath;
443    HRGN  hrgnPath;
444    BOOL  success = FALSE;
445    DC *dc = DC_GetDCPtr( hdc );
446
447    if(!dc) return FALSE;
448
449    if(dc->funcs->pSelectClipPath)
450      success = dc->funcs->pSelectClipPath(dc->physDev, iMode);
451    else
452    {
453        pPath = &dc->path;
454
455        /* Check that path is closed */
456        if(pPath->state!=PATH_Closed)
457            SetLastError(ERROR_CAN_NOT_COMPLETE);
458        /* Construct a region from the path */
459        else if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnPath))
460        {
461            success = ExtSelectClipRgn( hdc, hrgnPath, iMode ) != ERROR;
462            DeleteObject(hrgnPath);
463
464            /* Empty the path */
465            if(success)
466                PATH_EmptyPath(pPath);
467            /* FIXME: Should this function delete the path even if it failed? */
468        }
469    }
470    GDI_ReleaseObj( hdc );
471    return success;
472 }
473
474
475 /***********************************************************************
476  * Exported functions
477  */
478
479 /* PATH_InitGdiPath
480  *
481  * Initializes the GdiPath structure.
482  */
483 void PATH_InitGdiPath(GdiPath *pPath)
484 {
485    assert(pPath!=NULL);
486
487    pPath->state=PATH_Null;
488    pPath->pPoints=NULL;
489    pPath->pFlags=NULL;
490    pPath->numEntriesUsed=0;
491    pPath->numEntriesAllocated=0;
492 }
493
494 /* PATH_DestroyGdiPath
495  *
496  * Destroys a GdiPath structure (frees the memory in the arrays).
497  */
498 void PATH_DestroyGdiPath(GdiPath *pPath)
499 {
500    assert(pPath!=NULL);
501
502    if (pPath->pPoints) HeapFree( GetProcessHeap(), 0, pPath->pPoints );
503    if (pPath->pFlags) HeapFree( GetProcessHeap(), 0, pPath->pFlags );
504 }
505
506 /* PATH_AssignGdiPath
507  *
508  * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
509  * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
510  * not just the pointers. Since this means that the arrays in pPathDest may
511  * need to be resized, pPathDest should have been initialized using
512  * PATH_InitGdiPath (in C++, this function would be an assignment operator,
513  * not a copy constructor).
514  * Returns TRUE if successful, else FALSE.
515  */
516 BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
517 {
518    assert(pPathDest!=NULL && pPathSrc!=NULL);
519
520    /* Make sure destination arrays are big enough */
521    if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
522       return FALSE;
523
524    /* Perform the copy operation */
525    memcpy(pPathDest->pPoints, pPathSrc->pPoints,
526       sizeof(POINT)*pPathSrc->numEntriesUsed);
527    memcpy(pPathDest->pFlags, pPathSrc->pFlags,
528       sizeof(BYTE)*pPathSrc->numEntriesUsed);
529
530    pPathDest->state=pPathSrc->state;
531    pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
532    pPathDest->newStroke=pPathSrc->newStroke;
533
534    return TRUE;
535 }
536
537 /* PATH_MoveTo
538  *
539  * Should be called when a MoveTo is performed on a DC that has an
540  * open path. This starts a new stroke. Returns TRUE if successful, else
541  * FALSE.
542  */
543 BOOL PATH_MoveTo(DC *dc)
544 {
545    GdiPath *pPath = &dc->path;
546
547    /* Check that path is open */
548    if(pPath->state!=PATH_Open)
549       /* FIXME: Do we have to call SetLastError? */
550       return FALSE;
551
552    /* Start a new stroke */
553    pPath->newStroke=TRUE;
554
555    return TRUE;
556 }
557
558 /* PATH_LineTo
559  *
560  * Should be called when a LineTo is performed on a DC that has an
561  * open path. This adds a PT_LINETO entry to the path (and possibly
562  * a PT_MOVETO entry, if this is the first LineTo in a stroke).
563  * Returns TRUE if successful, else FALSE.
564  */
565 BOOL PATH_LineTo(DC *dc, INT x, INT y)
566 {
567    GdiPath *pPath = &dc->path;
568    POINT point, pointCurPos;
569
570    /* Check that path is open */
571    if(pPath->state!=PATH_Open)
572       return FALSE;
573
574    /* Convert point to device coordinates */
575    point.x=x;
576    point.y=y;
577    if(!LPtoDP(dc->hSelf, &point, 1))
578       return FALSE;
579
580    /* Add a PT_MOVETO if necessary */
581    if(pPath->newStroke)
582    {
583       pPath->newStroke=FALSE;
584       pointCurPos.x = dc->CursPosX;
585       pointCurPos.y = dc->CursPosY;
586       if(!LPtoDP(dc->hSelf, &pointCurPos, 1))
587          return FALSE;
588       if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
589          return FALSE;
590    }
591
592    /* Add a PT_LINETO entry */
593    return PATH_AddEntry(pPath, &point, PT_LINETO);
594 }
595
596 /* PATH_RoundRect
597  *
598  * Should be called when a call to RoundRect is performed on a DC that has
599  * an open path. Returns TRUE if successful, else FALSE.
600  *
601  * FIXME: it adds the same entries to the path as windows does, but there
602  * is an error in the bezier drawing code so that there are small pixel-size
603  * gaps when the resulting path is drawn by StrokePath()
604  */
605 BOOL PATH_RoundRect(DC *dc, INT x1, INT y1, INT x2, INT y2, INT ell_width, INT ell_height)
606 {
607    GdiPath *pPath = &dc->path;
608    POINT corners[2], pointTemp;
609    FLOAT_POINT ellCorners[2];
610
611    /* Check that path is open */
612    if(pPath->state!=PATH_Open)
613       return FALSE;
614
615    if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
616       return FALSE;
617
618    /* Add points to the roundrect path */
619    ellCorners[0].x = corners[1].x-ell_width;
620    ellCorners[0].y = corners[0].y;
621    ellCorners[1].x = corners[1].x;
622    ellCorners[1].y = corners[0].y+ell_height;
623    if(!PATH_DoArcPart(pPath, ellCorners, 0, -M_PI_2, TRUE))
624       return FALSE;
625    pointTemp.x = corners[0].x+ell_width/2;
626    pointTemp.y = corners[0].y;
627    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
628       return FALSE;
629    ellCorners[0].x = corners[0].x;
630    ellCorners[1].x = corners[0].x+ell_width;
631    if(!PATH_DoArcPart(pPath, ellCorners, -M_PI_2, -M_PI, FALSE))
632       return FALSE;
633    pointTemp.x = corners[0].x;
634    pointTemp.y = corners[1].y-ell_height/2;
635    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
636       return FALSE;
637    ellCorners[0].y = corners[1].y-ell_height;
638    ellCorners[1].y = corners[1].y;
639    if(!PATH_DoArcPart(pPath, ellCorners, M_PI, M_PI_2, FALSE))
640       return FALSE;
641    pointTemp.x = corners[1].x-ell_width/2;
642    pointTemp.y = corners[1].y;
643    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
644       return FALSE;
645    ellCorners[0].x = corners[1].x-ell_width;
646    ellCorners[1].x = corners[1].x;
647    if(!PATH_DoArcPart(pPath, ellCorners, M_PI_2, 0, FALSE))
648       return FALSE;
649
650    /* Close the roundrect figure */
651    if(!CloseFigure(dc->hSelf))
652       return FALSE;
653
654    return TRUE;
655 }
656
657 /* PATH_Rectangle
658  *
659  * Should be called when a call to Rectangle is performed on a DC that has
660  * an open path. Returns TRUE if successful, else FALSE.
661  */
662 BOOL PATH_Rectangle(DC *dc, INT x1, INT y1, INT x2, INT y2)
663 {
664    GdiPath *pPath = &dc->path;
665    POINT corners[2], pointTemp;
666
667    /* Check that path is open */
668    if(pPath->state!=PATH_Open)
669       return FALSE;
670
671    if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
672       return FALSE;
673
674    /* Close any previous figure */
675    if(!CloseFigure(dc->hSelf))
676    {
677       /* The CloseFigure call shouldn't have failed */
678       assert(FALSE);
679       return FALSE;
680    }
681
682    /* Add four points to the path */
683    pointTemp.x=corners[1].x;
684    pointTemp.y=corners[0].y;
685    if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
686       return FALSE;
687    if(!PATH_AddEntry(pPath, corners, PT_LINETO))
688       return FALSE;
689    pointTemp.x=corners[0].x;
690    pointTemp.y=corners[1].y;
691    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
692       return FALSE;
693    if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
694       return FALSE;
695
696    /* Close the rectangle figure */
697    if(!CloseFigure(dc->hSelf))
698    {
699       /* The CloseFigure call shouldn't have failed */
700       assert(FALSE);
701       return FALSE;
702    }
703
704    return TRUE;
705 }
706
707 /* PATH_Ellipse
708  *
709  * Should be called when a call to Ellipse is performed on a DC that has
710  * an open path. This adds four Bezier splines representing the ellipse
711  * to the path. Returns TRUE if successful, else FALSE.
712  */
713 BOOL PATH_Ellipse(DC *dc, INT x1, INT y1, INT x2, INT y2)
714 {
715    return( PATH_Arc(dc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2,0) &&
716            CloseFigure(dc->hSelf) );
717 }
718
719 /* PATH_Arc
720  *
721  * Should be called when a call to Arc is performed on a DC that has
722  * an open path. This adds up to five Bezier splines representing the arc
723  * to the path. When 'lines' is 1, we add 1 extra line to get a chord,
724  * and when 'lines' is 2, we add 2 extra lines to get a pie.
725  * Returns TRUE if successful, else FALSE.
726  */
727 BOOL PATH_Arc(DC *dc, INT x1, INT y1, INT x2, INT y2,
728    INT xStart, INT yStart, INT xEnd, INT yEnd, INT lines)
729 {
730    GdiPath     *pPath = &dc->path;
731    double      angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
732                /* Initialize angleEndQuadrant to silence gcc's warning */
733    double      x, y;
734    FLOAT_POINT corners[2], pointStart, pointEnd;
735    POINT       centre;
736    BOOL      start, end;
737    INT       temp;
738
739    /* FIXME: This function should check for all possible error returns */
740    /* FIXME: Do we have to respect newStroke? */
741
742    /* Check that path is open */
743    if(pPath->state!=PATH_Open)
744       return FALSE;
745
746    /* Check for zero height / width */
747    /* FIXME: Only in GM_COMPATIBLE? */
748    if(x1==x2 || y1==y2)
749       return TRUE;
750
751    /* Convert points to device coordinates */
752    corners[0].x=(FLOAT)x1;
753    corners[0].y=(FLOAT)y1;
754    corners[1].x=(FLOAT)x2;
755    corners[1].y=(FLOAT)y2;
756    pointStart.x=(FLOAT)xStart;
757    pointStart.y=(FLOAT)yStart;
758    pointEnd.x=(FLOAT)xEnd;
759    pointEnd.y=(FLOAT)yEnd;
760    INTERNAL_LPTODP_FLOAT(dc, corners);
761    INTERNAL_LPTODP_FLOAT(dc, corners+1);
762    INTERNAL_LPTODP_FLOAT(dc, &pointStart);
763    INTERNAL_LPTODP_FLOAT(dc, &pointEnd);
764
765    /* Make sure first corner is top left and second corner is bottom right */
766    if(corners[0].x>corners[1].x)
767    {
768       temp=corners[0].x;
769       corners[0].x=corners[1].x;
770       corners[1].x=temp;
771    }
772    if(corners[0].y>corners[1].y)
773    {
774       temp=corners[0].y;
775       corners[0].y=corners[1].y;
776       corners[1].y=temp;
777    }
778
779    /* Compute start and end angle */
780    PATH_NormalizePoint(corners, &pointStart, &x, &y);
781    angleStart=atan2(y, x);
782    PATH_NormalizePoint(corners, &pointEnd, &x, &y);
783    angleEnd=atan2(y, x);
784
785    /* Make sure the end angle is "on the right side" of the start angle */
786    if(dc->ArcDirection==AD_CLOCKWISE)
787    {
788       if(angleEnd<=angleStart)
789       {
790          angleEnd+=2*M_PI;
791          assert(angleEnd>=angleStart);
792       }
793    }
794    else
795    {
796       if(angleEnd>=angleStart)
797       {
798          angleEnd-=2*M_PI;
799          assert(angleEnd<=angleStart);
800       }
801    }
802
803    /* In GM_COMPATIBLE, don't include bottom and right edges */
804    if(dc->GraphicsMode==GM_COMPATIBLE)
805    {
806       corners[1].x--;
807       corners[1].y--;
808    }
809
810    /* Add the arc to the path with one Bezier spline per quadrant that the
811     * arc spans */
812    start=TRUE;
813    end=FALSE;
814    do
815    {
816       /* Determine the start and end angles for this quadrant */
817       if(start)
818       {
819          angleStartQuadrant=angleStart;
820          if(dc->ArcDirection==AD_CLOCKWISE)
821             angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
822          else
823             angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
824       }
825       else
826       {
827          angleStartQuadrant=angleEndQuadrant;
828          if(dc->ArcDirection==AD_CLOCKWISE)
829             angleEndQuadrant+=M_PI_2;
830          else
831             angleEndQuadrant-=M_PI_2;
832       }
833
834       /* Have we reached the last part of the arc? */
835       if((dc->ArcDirection==AD_CLOCKWISE &&
836          angleEnd<angleEndQuadrant) ||
837          (dc->ArcDirection==AD_COUNTERCLOCKWISE &&
838          angleEnd>angleEndQuadrant))
839       {
840          /* Adjust the end angle for this quadrant */
841          angleEndQuadrant=angleEnd;
842          end=TRUE;
843       }
844
845       /* Add the Bezier spline to the path */
846       PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant,
847          start);
848       start=FALSE;
849    }  while(!end);
850
851    /* chord: close figure. pie: add line and close figure */
852    if(lines==1)
853    {
854       if(!CloseFigure(dc->hSelf))
855          return FALSE;
856    }
857    else if(lines==2)
858    {
859       centre.x = (corners[0].x+corners[1].x)/2;
860       centre.y = (corners[0].y+corners[1].y)/2;
861       if(!PATH_AddEntry(pPath, &centre, PT_LINETO | PT_CLOSEFIGURE))
862          return FALSE;
863    }
864
865    return TRUE;
866 }
867
868 BOOL PATH_PolyBezierTo(DC *dc, const POINT *pts, DWORD cbPoints)
869 {
870    GdiPath     *pPath = &dc->path;
871    POINT       pt;
872    INT         i;
873
874    /* Check that path is open */
875    if(pPath->state!=PATH_Open)
876       return FALSE;
877
878    /* Add a PT_MOVETO if necessary */
879    if(pPath->newStroke)
880    {
881       pPath->newStroke=FALSE;
882       pt.x = dc->CursPosX;
883       pt.y = dc->CursPosY;
884       if(!LPtoDP(dc->hSelf, &pt, 1))
885          return FALSE;
886       if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
887          return FALSE;
888    }
889
890    for(i = 0; i < cbPoints; i++) {
891        pt = pts[i];
892        if(!LPtoDP(dc->hSelf, &pt, 1))
893            return FALSE;
894        PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
895    }
896    return TRUE;
897 }
898
899 BOOL PATH_PolyBezier(DC *dc, const POINT *pts, DWORD cbPoints)
900 {
901    GdiPath     *pPath = &dc->path;
902    POINT       pt;
903    INT         i;
904
905    /* Check that path is open */
906    if(pPath->state!=PATH_Open)
907       return FALSE;
908
909    for(i = 0; i < cbPoints; i++) {
910        pt = pts[i];
911        if(!LPtoDP(dc->hSelf, &pt, 1))
912            return FALSE;
913        PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO);
914    }
915    return TRUE;
916 }
917
918 BOOL PATH_Polyline(DC *dc, const POINT *pts, DWORD cbPoints)
919 {
920    GdiPath     *pPath = &dc->path;
921    POINT       pt;
922    INT         i;
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(dc->hSelf, &pt, 1))
931            return FALSE;
932        PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
933    }
934    return TRUE;
935 }
936
937 BOOL PATH_PolylineTo(DC *dc, const POINT *pts, DWORD cbPoints)
938 {
939    GdiPath     *pPath = &dc->path;
940    POINT       pt;
941    INT         i;
942
943    /* Check that path is open */
944    if(pPath->state!=PATH_Open)
945       return FALSE;
946
947    /* Add a PT_MOVETO if necessary */
948    if(pPath->newStroke)
949    {
950       pPath->newStroke=FALSE;
951       pt.x = dc->CursPosX;
952       pt.y = dc->CursPosY;
953       if(!LPtoDP(dc->hSelf, &pt, 1))
954          return FALSE;
955       if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
956          return FALSE;
957    }
958
959    for(i = 0; i < cbPoints; i++) {
960        pt = pts[i];
961        if(!LPtoDP(dc->hSelf, &pt, 1))
962            return FALSE;
963        PATH_AddEntry(pPath, &pt, PT_LINETO);
964    }
965
966    return TRUE;
967 }
968
969
970 BOOL PATH_Polygon(DC *dc, const POINT *pts, DWORD cbPoints)
971 {
972    GdiPath     *pPath = &dc->path;
973    POINT       pt;
974    INT         i;
975
976    /* Check that path is open */
977    if(pPath->state!=PATH_Open)
978       return FALSE;
979
980    for(i = 0; i < cbPoints; i++) {
981        pt = pts[i];
982        if(!LPtoDP(dc->hSelf, &pt, 1))
983            return FALSE;
984        PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
985                      ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
986                       PT_LINETO));
987    }
988    return TRUE;
989 }
990
991 BOOL PATH_PolyPolygon( DC *dc, const POINT* pts, const INT* counts,
992                        UINT polygons )
993 {
994    GdiPath     *pPath = &dc->path;
995    POINT       pt, startpt;
996    INT         poly, point, i;
997
998    /* Check that path is open */
999    if(pPath->state!=PATH_Open)
1000       return FALSE;
1001
1002    for(i = 0, poly = 0; poly < polygons; poly++) {
1003        for(point = 0; point < counts[poly]; point++, i++) {
1004            pt = pts[i];
1005            if(!LPtoDP(dc->hSelf, &pt, 1))
1006                return FALSE;
1007            if(point == 0) startpt = pt;
1008            PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1009        }
1010        /* win98 adds an extra line to close the figure for some reason */
1011        PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
1012    }
1013    return TRUE;
1014 }
1015
1016 BOOL PATH_PolyPolyline( DC *dc, const POINT* pts, const DWORD* counts,
1017                         DWORD polylines )
1018 {
1019    GdiPath     *pPath = &dc->path;
1020    POINT       pt;
1021    INT         poly, point, i;
1022
1023    /* Check that path is open */
1024    if(pPath->state!=PATH_Open)
1025       return FALSE;
1026
1027    for(i = 0, poly = 0; poly < polylines; poly++) {
1028        for(point = 0; point < counts[poly]; point++, i++) {
1029            pt = pts[i];
1030            if(!LPtoDP(dc->hSelf, &pt, 1))
1031                return FALSE;
1032            PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1033        }
1034    }
1035    return TRUE;
1036 }
1037
1038 /***********************************************************************
1039  * Internal functions
1040  */
1041
1042 /* PATH_CheckCorners
1043  *
1044  * Helper function for PATH_RoundRect() and PATH_Rectangle()
1045  */
1046 static BOOL PATH_CheckCorners(DC *dc, POINT corners[], INT x1, INT y1, INT x2, INT y2)
1047 {
1048    INT temp;
1049
1050    /* Convert points to device coordinates */
1051    corners[0].x=x1;
1052    corners[0].y=y1;
1053    corners[1].x=x2;
1054    corners[1].y=y2;
1055    if(!LPtoDP(dc->hSelf, corners, 2))
1056       return FALSE;
1057
1058    /* Make sure first corner is top left and second corner is bottom right */
1059    if(corners[0].x>corners[1].x)
1060    {
1061       temp=corners[0].x;
1062       corners[0].x=corners[1].x;
1063       corners[1].x=temp;
1064    }
1065    if(corners[0].y>corners[1].y)
1066    {
1067       temp=corners[0].y;
1068       corners[0].y=corners[1].y;
1069       corners[1].y=temp;
1070    }
1071
1072    /* In GM_COMPATIBLE, don't include bottom and right edges */
1073    if(dc->GraphicsMode==GM_COMPATIBLE)
1074    {
1075       corners[1].x--;
1076       corners[1].y--;
1077    }
1078
1079    return TRUE;
1080 }
1081
1082 /* PATH_AddFlatBezier
1083  */
1084 static BOOL PATH_AddFlatBezier(GdiPath *pPath, POINT *pt, BOOL closed)
1085 {
1086     POINT *pts;
1087     INT no, i;
1088
1089     pts = GDI_Bezier( pt, 4, &no );
1090     if(!pts) return FALSE;
1091
1092     for(i = 1; i < no; i++)
1093         PATH_AddEntry(pPath, &pts[i],
1094             (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
1095     HeapFree( GetProcessHeap(), 0, pts );
1096     return TRUE;
1097 }
1098
1099 /* PATH_FlattenPath
1100  *
1101  * Replaces Beziers with line segments
1102  *
1103  */
1104 static BOOL PATH_FlattenPath(GdiPath *pPath)
1105 {
1106     GdiPath newPath;
1107     INT srcpt;
1108
1109     memset(&newPath, 0, sizeof(newPath));
1110     newPath.state = PATH_Open;
1111     for(srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++) {
1112         switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE) {
1113         case PT_MOVETO:
1114         case PT_LINETO:
1115             PATH_AddEntry(&newPath, &pPath->pPoints[srcpt],
1116                           pPath->pFlags[srcpt]);
1117             break;
1118         case PT_BEZIERTO:
1119           PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1],
1120                              pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
1121             srcpt += 2;
1122             break;
1123         }
1124     }
1125     newPath.state = PATH_Closed;
1126     PATH_AssignGdiPath(pPath, &newPath);
1127     PATH_DestroyGdiPath(&newPath);
1128     return TRUE;
1129 }
1130
1131 /* PATH_PathToRegion
1132  *
1133  * Creates a region from the specified path using the specified polygon
1134  * filling mode. The path is left unchanged. A handle to the region that
1135  * was created is stored in *pHrgn. If successful, TRUE is returned; if an
1136  * error occurs, SetLastError is called with the appropriate value and
1137  * FALSE is returned.
1138  */
1139 static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode,
1140    HRGN *pHrgn)
1141 {
1142    int    numStrokes, iStroke, i;
1143    INT  *pNumPointsInStroke;
1144    HRGN hrgn;
1145
1146    assert(pPath!=NULL);
1147    assert(pHrgn!=NULL);
1148
1149    PATH_FlattenPath(pPath);
1150
1151    /* FIXME: What happens when number of points is zero? */
1152
1153    /* First pass: Find out how many strokes there are in the path */
1154    /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
1155    numStrokes=0;
1156    for(i=0; i<pPath->numEntriesUsed; i++)
1157       if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1158          numStrokes++;
1159
1160    /* Allocate memory for number-of-points-in-stroke array */
1161    pNumPointsInStroke=(int *)HeapAlloc( GetProcessHeap(), 0,
1162                                         sizeof(int) * numStrokes );
1163    if(!pNumPointsInStroke)
1164    {
1165       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1166       return FALSE;
1167    }
1168
1169    /* Second pass: remember number of points in each polygon */
1170    iStroke=-1;  /* Will get incremented to 0 at beginning of first stroke */
1171    for(i=0; i<pPath->numEntriesUsed; i++)
1172    {
1173       /* Is this the beginning of a new stroke? */
1174       if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1175       {
1176          iStroke++;
1177          pNumPointsInStroke[iStroke]=0;
1178       }
1179
1180       pNumPointsInStroke[iStroke]++;
1181    }
1182
1183    /* Create a region from the strokes */
1184    hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
1185       numStrokes, nPolyFillMode);
1186
1187    /* Free memory for number-of-points-in-stroke array */
1188    HeapFree( GetProcessHeap(), 0, pNumPointsInStroke );
1189
1190    if(hrgn==NULL)
1191    {
1192       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1193       return FALSE;
1194    }
1195
1196    /* Success! */
1197    *pHrgn=hrgn;
1198    return TRUE;
1199 }
1200
1201 /* PATH_EmptyPath
1202  *
1203  * Removes all entries from the path and sets the path state to PATH_Null.
1204  */
1205 static void PATH_EmptyPath(GdiPath *pPath)
1206 {
1207    assert(pPath!=NULL);
1208
1209    pPath->state=PATH_Null;
1210    pPath->numEntriesUsed=0;
1211 }
1212
1213 /* PATH_AddEntry
1214  *
1215  * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
1216  * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
1217  * successful, FALSE otherwise (e.g. if not enough memory was available).
1218  */
1219 BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint, BYTE flags)
1220 {
1221    assert(pPath!=NULL);
1222
1223    /* FIXME: If newStroke is true, perhaps we want to check that we're
1224     * getting a PT_MOVETO
1225     */
1226    TRACE("(%ld,%ld) - %d\n", pPoint->x, pPoint->y, flags);
1227
1228    /* Check that path is open */
1229    if(pPath->state!=PATH_Open)
1230       return FALSE;
1231
1232    /* Reserve enough memory for an extra path entry */
1233    if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
1234       return FALSE;
1235
1236    /* Store information in path entry */
1237    pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
1238    pPath->pFlags[pPath->numEntriesUsed]=flags;
1239
1240    /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
1241    if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
1242       pPath->newStroke=TRUE;
1243
1244    /* Increment entry count */
1245    pPath->numEntriesUsed++;
1246
1247    return TRUE;
1248 }
1249
1250 /* PATH_ReserveEntries
1251  *
1252  * Ensures that at least "numEntries" entries (for points and flags) have
1253  * been allocated; allocates larger arrays and copies the existing entries
1254  * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
1255  */
1256 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries)
1257 {
1258    INT   numEntriesToAllocate;
1259    POINT *pPointsNew;
1260    BYTE    *pFlagsNew;
1261
1262    assert(pPath!=NULL);
1263    assert(numEntries>=0);
1264
1265    /* Do we have to allocate more memory? */
1266    if(numEntries > pPath->numEntriesAllocated)
1267    {
1268       /* Find number of entries to allocate. We let the size of the array
1269        * grow exponentially, since that will guarantee linear time
1270        * complexity. */
1271       if(pPath->numEntriesAllocated)
1272       {
1273          numEntriesToAllocate=pPath->numEntriesAllocated;
1274          while(numEntriesToAllocate<numEntries)
1275             numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/
1276                GROW_FACTOR_DENOM;
1277       }
1278       else
1279          numEntriesToAllocate=numEntries;
1280
1281       /* Allocate new arrays */
1282       pPointsNew=(POINT *)HeapAlloc( GetProcessHeap(), 0,
1283                                      numEntriesToAllocate * sizeof(POINT) );
1284       if(!pPointsNew)
1285          return FALSE;
1286       pFlagsNew=(BYTE *)HeapAlloc( GetProcessHeap(), 0,
1287                                    numEntriesToAllocate * sizeof(BYTE) );
1288       if(!pFlagsNew)
1289       {
1290          HeapFree( GetProcessHeap(), 0, pPointsNew );
1291          return FALSE;
1292       }
1293
1294       /* Copy old arrays to new arrays and discard old arrays */
1295       if(pPath->pPoints)
1296       {
1297          assert(pPath->pFlags);
1298
1299          memcpy(pPointsNew, pPath->pPoints,
1300              sizeof(POINT)*pPath->numEntriesUsed);
1301          memcpy(pFlagsNew, pPath->pFlags,
1302              sizeof(BYTE)*pPath->numEntriesUsed);
1303
1304          HeapFree( GetProcessHeap(), 0, pPath->pPoints );
1305          HeapFree( GetProcessHeap(), 0, pPath->pFlags );
1306       }
1307       pPath->pPoints=pPointsNew;
1308       pPath->pFlags=pFlagsNew;
1309       pPath->numEntriesAllocated=numEntriesToAllocate;
1310    }
1311
1312    return TRUE;
1313 }
1314
1315 /* PATH_DoArcPart
1316  *
1317  * Creates a Bezier spline that corresponds to part of an arc and appends the
1318  * corresponding points to the path. The start and end angles are passed in
1319  * "angleStart" and "angleEnd"; these angles should span a quarter circle
1320  * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
1321  * point is added to the path; otherwise, it is assumed that the current
1322  * position is equal to the first control point.
1323  */
1324 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
1325    double angleStart, double angleEnd, BOOL addMoveTo)
1326 {
1327    double  halfAngle, a;
1328    double  xNorm[4], yNorm[4];
1329    POINT point;
1330    int     i;
1331
1332    assert(fabs(angleEnd-angleStart)<=M_PI_2);
1333
1334    /* FIXME: Is there an easier way of computing this? */
1335
1336    /* Compute control points */
1337    halfAngle=(angleEnd-angleStart)/2.0;
1338    if(fabs(halfAngle)>1e-8)
1339    {
1340       a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
1341       xNorm[0]=cos(angleStart);
1342       yNorm[0]=sin(angleStart);
1343       xNorm[1]=xNorm[0] - a*yNorm[0];
1344       yNorm[1]=yNorm[0] + a*xNorm[0];
1345       xNorm[3]=cos(angleEnd);
1346       yNorm[3]=sin(angleEnd);
1347       xNorm[2]=xNorm[3] + a*yNorm[3];
1348       yNorm[2]=yNorm[3] - a*xNorm[3];
1349    }
1350    else
1351       for(i=0; i<4; i++)
1352       {
1353          xNorm[i]=cos(angleStart);
1354          yNorm[i]=sin(angleStart);
1355       }
1356
1357    /* Add starting point to path if desired */
1358    if(addMoveTo)
1359    {
1360       PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
1361       if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
1362          return FALSE;
1363    }
1364
1365    /* Add remaining control points */
1366    for(i=1; i<4; i++)
1367    {
1368       PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
1369       if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1370          return FALSE;
1371    }
1372
1373    return TRUE;
1374 }
1375
1376 /* PATH_ScaleNormalizedPoint
1377  *
1378  * Scales a normalized point (x, y) with respect to the box whose corners are
1379  * passed in "corners". The point is stored in "*pPoint". The normalized
1380  * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1381  * (1.0, 1.0) correspond to corners[1].
1382  */
1383 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
1384    double y, POINT *pPoint)
1385 {
1386    pPoint->x=GDI_ROUND( (double)corners[0].x +
1387       (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
1388    pPoint->y=GDI_ROUND( (double)corners[0].y +
1389       (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
1390 }
1391
1392 /* PATH_NormalizePoint
1393  *
1394  * Normalizes a point with respect to the box whose corners are passed in
1395  * "corners". The normalized coordinates are stored in "*pX" and "*pY".
1396  */
1397 static void PATH_NormalizePoint(FLOAT_POINT corners[],
1398    const FLOAT_POINT *pPoint,
1399    double *pX, double *pY)
1400 {
1401    *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) *
1402       2.0 - 1.0;
1403    *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) *
1404       2.0 - 1.0;
1405 }
1406
1407
1408 /*******************************************************************
1409  *      FlattenPath [GDI32.@]
1410  *
1411  *
1412  */
1413 BOOL WINAPI FlattenPath(HDC hdc)
1414 {
1415     BOOL ret = FALSE;
1416     DC *dc = DC_GetDCPtr( hdc );
1417
1418     if(!dc) return FALSE;
1419
1420     if(dc->funcs->pFlattenPath) ret = dc->funcs->pFlattenPath(dc->physDev);
1421     else
1422     {
1423         GdiPath *pPath = &dc->path;
1424         if(pPath->state != PATH_Closed)
1425             ret = PATH_FlattenPath(pPath);
1426     }
1427     GDI_ReleaseObj( hdc );
1428     return ret;
1429 }
1430
1431
1432 static BOOL PATH_StrokePath(DC *dc, GdiPath *pPath)
1433 {
1434     INT i;
1435     POINT ptLastMove = {0,0};
1436     POINT ptViewportOrg, ptWindowOrg;
1437     SIZE szViewportExt, szWindowExt;
1438     DWORD mapMode, graphicsMode;
1439     XFORM xform;
1440     BOOL ret = TRUE;
1441
1442     if(dc->funcs->pStrokePath)
1443         return dc->funcs->pStrokePath(dc->physDev);
1444
1445     if(pPath->state != PATH_Closed)
1446         return FALSE;
1447
1448     /* Save the mapping mode info */
1449     mapMode=GetMapMode(dc->hSelf);
1450     GetViewportExtEx(dc->hSelf, &szViewportExt);
1451     GetViewportOrgEx(dc->hSelf, &ptViewportOrg);
1452     GetWindowExtEx(dc->hSelf, &szWindowExt);
1453     GetWindowOrgEx(dc->hSelf, &ptWindowOrg);
1454     GetWorldTransform(dc->hSelf, &xform);
1455
1456     /* Set MM_TEXT */
1457     SetMapMode(dc->hSelf, MM_TEXT);
1458     SetViewportOrgEx(dc->hSelf, 0, 0, NULL);
1459     SetWindowOrgEx(dc->hSelf, 0, 0, NULL);
1460     graphicsMode=GetGraphicsMode(dc->hSelf);
1461     SetGraphicsMode(dc->hSelf, GM_ADVANCED);
1462     ModifyWorldTransform(dc->hSelf, &xform, MWT_IDENTITY);
1463     SetGraphicsMode(dc->hSelf, graphicsMode);
1464
1465     for(i = 0; i < pPath->numEntriesUsed; i++) {
1466         switch(pPath->pFlags[i]) {
1467         case PT_MOVETO:
1468             TRACE("Got PT_MOVETO (%ld, %ld)\n",
1469                   pPath->pPoints[i].x, pPath->pPoints[i].y);
1470             MoveToEx(dc->hSelf, pPath->pPoints[i].x, pPath->pPoints[i].y, NULL);
1471             ptLastMove = pPath->pPoints[i];
1472             break;
1473         case PT_LINETO:
1474         case (PT_LINETO | PT_CLOSEFIGURE):
1475             TRACE("Got PT_LINETO (%ld, %ld)\n",
1476                   pPath->pPoints[i].x, pPath->pPoints[i].y);
1477             LineTo(dc->hSelf, pPath->pPoints[i].x, pPath->pPoints[i].y);
1478             break;
1479         case PT_BEZIERTO:
1480             TRACE("Got PT_BEZIERTO\n");
1481             if(pPath->pFlags[i+1] != PT_BEZIERTO ||
1482                (pPath->pFlags[i+2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) {
1483                 ERR("Path didn't contain 3 successive PT_BEZIERTOs\n");
1484                 ret = FALSE;
1485                 goto end;
1486             }
1487             PolyBezierTo(dc->hSelf, &pPath->pPoints[i], 3);
1488             i += 2;
1489             break;
1490         default:
1491             ERR("Got path flag %d\n", (INT)pPath->pFlags[i]);
1492             ret = FALSE;
1493             goto end;
1494         }
1495         if(pPath->pFlags[i] & PT_CLOSEFIGURE)
1496             LineTo(dc->hSelf, ptLastMove.x, ptLastMove.y);
1497     }
1498
1499  end:
1500
1501     /* Restore the old mapping mode */
1502     SetMapMode(dc->hSelf, mapMode);
1503     SetViewportExtEx(dc->hSelf, szViewportExt.cx, szViewportExt.cy, NULL);
1504     SetViewportOrgEx(dc->hSelf, ptViewportOrg.x, ptViewportOrg.y, NULL);
1505     SetWindowExtEx(dc->hSelf, szWindowExt.cx, szWindowExt.cy, NULL);
1506     SetWindowOrgEx(dc->hSelf, ptWindowOrg.x, ptWindowOrg.y, NULL);
1507
1508     /* Go to GM_ADVANCED temporarily to restore the world transform */
1509     graphicsMode=GetGraphicsMode(dc->hSelf);
1510     SetGraphicsMode(dc->hSelf, GM_ADVANCED);
1511     SetWorldTransform(dc->hSelf, &xform);
1512     SetGraphicsMode(dc->hSelf, graphicsMode);
1513
1514     /* If we've moved the current point then get its new position
1515        which will be in device (MM_TEXT) co-ords, convert it to
1516        logical co-ords and re-set it.  This basically updates
1517        dc->CurPosX|Y so that their values are in the correct mapping
1518        mode.
1519     */
1520     if(i > 0) {
1521         POINT pt;
1522         GetCurrentPositionEx(dc->hSelf, &pt);
1523         DPtoLP(dc->hSelf, &pt, 1);
1524         MoveToEx(dc->hSelf, pt.x, pt.y, NULL);
1525     }
1526     return ret;
1527 }
1528
1529
1530 /*******************************************************************
1531  *      StrokeAndFillPath [GDI32.@]
1532  *
1533  *
1534  */
1535 BOOL WINAPI StrokeAndFillPath(HDC hdc)
1536 {
1537    DC *dc = DC_GetDCPtr( hdc );
1538    BOOL bRet = FALSE;
1539
1540    if(!dc) return FALSE;
1541
1542    if(dc->funcs->pStrokeAndFillPath)
1543        bRet = dc->funcs->pStrokeAndFillPath(dc->physDev);
1544    else
1545    {
1546        bRet = PATH_FillPath(dc, &dc->path);
1547        if(bRet) bRet = PATH_StrokePath(dc, &dc->path);
1548        if(bRet) PATH_EmptyPath(&dc->path);
1549    }
1550    GDI_ReleaseObj( hdc );
1551    return bRet;
1552 }
1553
1554
1555 /*******************************************************************
1556  *      StrokePath [GDI32.@]
1557  *
1558  *
1559  */
1560 BOOL WINAPI StrokePath(HDC hdc)
1561 {
1562     DC *dc = DC_GetDCPtr( hdc );
1563     GdiPath *pPath;
1564     BOOL bRet = FALSE;
1565
1566     TRACE("(%p)\n", hdc);
1567     if(!dc) return FALSE;
1568
1569     if(dc->funcs->pStrokePath)
1570         bRet = dc->funcs->pStrokePath(dc->physDev);
1571     else
1572     {
1573         pPath = &dc->path;
1574         bRet = PATH_StrokePath(dc, pPath);
1575         PATH_EmptyPath(pPath);
1576     }
1577     GDI_ReleaseObj( hdc );
1578     return bRet;
1579 }
1580
1581
1582 /*******************************************************************
1583  *      WidenPath [GDI32.@]
1584  *
1585  *
1586  */
1587 BOOL WINAPI WidenPath(HDC hdc)
1588 {
1589    DC *dc = DC_GetDCPtr( hdc );
1590    BOOL ret = FALSE;
1591
1592    if(!dc) return FALSE;
1593
1594    if(dc->funcs->pWidenPath)
1595      ret = dc->funcs->pWidenPath(dc->physDev);
1596
1597    FIXME("stub\n");
1598    GDI_ReleaseObj( hdc );
1599    return ret;
1600 }