Fix some memory leaks found by smatch.
[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
380       /* Paint the region */
381       PaintRgn(dc->hSelf, hrgn);
382       DeleteObject(hrgn);
383       /* Restore the old mapping mode */
384       SetMapMode(dc->hSelf, mapMode);
385       SetViewportExtEx(dc->hSelf, ptViewportExt.cx, ptViewportExt.cy, NULL);
386       SetViewportOrgEx(dc->hSelf, ptViewportOrg.x, ptViewportOrg.y, NULL);
387       SetWindowExtEx(dc->hSelf, ptWindowExt.cx, ptWindowExt.cy, NULL);
388       SetWindowOrgEx(dc->hSelf, ptWindowOrg.x, ptWindowOrg.y, NULL);
389
390       /* Go to GM_ADVANCED temporarily to restore the world transform */
391       graphicsMode=GetGraphicsMode(dc->hSelf);
392       SetGraphicsMode(dc->hSelf, GM_ADVANCED);
393       SetWorldTransform(dc->hSelf, &xform);
394       SetGraphicsMode(dc->hSelf, graphicsMode);
395       return TRUE;
396    }
397    return FALSE;
398 }
399
400
401 /***********************************************************************
402  *           FillPath    (GDI32.@)
403  *
404  * FIXME
405  *    Check that SetLastError is being called correctly
406  */
407 BOOL WINAPI FillPath(HDC hdc)
408 {
409     DC *dc = DC_GetDCPtr( hdc );
410     BOOL bRet = FALSE;
411
412     if(!dc) return FALSE;
413
414     if(dc->funcs->pFillPath)
415         bRet = dc->funcs->pFillPath(dc->physDev);
416     else
417     {
418         bRet = PATH_FillPath(dc, &dc->path);
419         if(bRet)
420         {
421             /* FIXME: Should the path be emptied even if conversion
422                failed? */
423             PATH_EmptyPath(&dc->path);
424         }
425     }
426     GDI_ReleaseObj( hdc );
427     return bRet;
428 }
429
430
431 /***********************************************************************
432  *           SelectClipPath    (GDI32.@)
433  * FIXME
434  *  Check that SetLastError is being called correctly
435  */
436 BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
437 {
438    GdiPath *pPath;
439    HRGN  hrgnPath;
440    BOOL  success = FALSE;
441    DC *dc = DC_GetDCPtr( hdc );
442
443    if(!dc) return FALSE;
444
445    if(dc->funcs->pSelectClipPath)
446      success = dc->funcs->pSelectClipPath(dc->physDev, iMode);
447    else
448    {
449        pPath = &dc->path;
450
451        /* Check that path is closed */
452        if(pPath->state!=PATH_Closed)
453            SetLastError(ERROR_CAN_NOT_COMPLETE);
454        /* Construct a region from the path */
455        else if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnPath))
456        {
457            success = ExtSelectClipRgn( hdc, hrgnPath, iMode ) != ERROR;
458            DeleteObject(hrgnPath);
459
460            /* Empty the path */
461            if(success)
462                PATH_EmptyPath(pPath);
463            /* FIXME: Should this function delete the path even if it failed? */
464        }
465    }
466    GDI_ReleaseObj( hdc );
467    return success;
468 }
469
470
471 /***********************************************************************
472  * Exported functions
473  */
474
475 /* PATH_InitGdiPath
476  *
477  * Initializes the GdiPath structure.
478  */
479 void PATH_InitGdiPath(GdiPath *pPath)
480 {
481    assert(pPath!=NULL);
482
483    pPath->state=PATH_Null;
484    pPath->pPoints=NULL;
485    pPath->pFlags=NULL;
486    pPath->numEntriesUsed=0;
487    pPath->numEntriesAllocated=0;
488 }
489
490 /* PATH_DestroyGdiPath
491  *
492  * Destroys a GdiPath structure (frees the memory in the arrays).
493  */
494 void PATH_DestroyGdiPath(GdiPath *pPath)
495 {
496    assert(pPath!=NULL);
497
498    if (pPath->pPoints) HeapFree( GetProcessHeap(), 0, pPath->pPoints );
499    if (pPath->pFlags) HeapFree( GetProcessHeap(), 0, pPath->pFlags );
500 }
501
502 /* PATH_AssignGdiPath
503  *
504  * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
505  * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
506  * not just the pointers. Since this means that the arrays in pPathDest may
507  * need to be resized, pPathDest should have been initialized using
508  * PATH_InitGdiPath (in C++, this function would be an assignment operator,
509  * not a copy constructor).
510  * Returns TRUE if successful, else FALSE.
511  */
512 BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
513 {
514    assert(pPathDest!=NULL && pPathSrc!=NULL);
515
516    /* Make sure destination arrays are big enough */
517    if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
518       return FALSE;
519
520    /* Perform the copy operation */
521    memcpy(pPathDest->pPoints, pPathSrc->pPoints,
522       sizeof(POINT)*pPathSrc->numEntriesUsed);
523    memcpy(pPathDest->pFlags, pPathSrc->pFlags,
524       sizeof(BYTE)*pPathSrc->numEntriesUsed);
525
526    pPathDest->state=pPathSrc->state;
527    pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
528    pPathDest->newStroke=pPathSrc->newStroke;
529
530    return TRUE;
531 }
532
533 /* PATH_MoveTo
534  *
535  * Should be called when a MoveTo is performed on a DC that has an
536  * open path. This starts a new stroke. Returns TRUE if successful, else
537  * FALSE.
538  */
539 BOOL PATH_MoveTo(DC *dc)
540 {
541    GdiPath *pPath = &dc->path;
542
543    /* Check that path is open */
544    if(pPath->state!=PATH_Open)
545       /* FIXME: Do we have to call SetLastError? */
546       return FALSE;
547
548    /* Start a new stroke */
549    pPath->newStroke=TRUE;
550
551    return TRUE;
552 }
553
554 /* PATH_LineTo
555  *
556  * Should be called when a LineTo is performed on a DC that has an
557  * open path. This adds a PT_LINETO entry to the path (and possibly
558  * a PT_MOVETO entry, if this is the first LineTo in a stroke).
559  * Returns TRUE if successful, else FALSE.
560  */
561 BOOL PATH_LineTo(DC *dc, INT x, INT y)
562 {
563    GdiPath *pPath = &dc->path;
564    POINT point, pointCurPos;
565
566    /* Check that path is open */
567    if(pPath->state!=PATH_Open)
568       return FALSE;
569
570    /* Convert point to device coordinates */
571    point.x=x;
572    point.y=y;
573    if(!LPtoDP(dc->hSelf, &point, 1))
574       return FALSE;
575
576    /* Add a PT_MOVETO if necessary */
577    if(pPath->newStroke)
578    {
579       pPath->newStroke=FALSE;
580       pointCurPos.x = dc->CursPosX;
581       pointCurPos.y = dc->CursPosY;
582       if(!LPtoDP(dc->hSelf, &pointCurPos, 1))
583          return FALSE;
584       if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
585          return FALSE;
586    }
587
588    /* Add a PT_LINETO entry */
589    return PATH_AddEntry(pPath, &point, PT_LINETO);
590 }
591
592 /* PATH_RoundRect
593  *
594  * Should be called when a call to RoundRect is performed on a DC that has
595  * an open path. Returns TRUE if successful, else FALSE.
596  *
597  * FIXME: it adds the same entries to the path as windows does, but there
598  * is an error in the bezier drawing code so that there are small pixel-size
599  * gaps when the resulting path is drawn by StrokePath()
600  */
601 BOOL PATH_RoundRect(DC *dc, INT x1, INT y1, INT x2, INT y2, INT ell_width, INT ell_height)
602 {
603    GdiPath *pPath = &dc->path;
604    POINT corners[2], pointTemp;
605    FLOAT_POINT ellCorners[2];
606
607    /* Check that path is open */
608    if(pPath->state!=PATH_Open)
609       return FALSE;
610
611    if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
612       return FALSE;
613
614    /* Add points to the roundrect path */
615    ellCorners[0].x = corners[1].x-ell_width;
616    ellCorners[0].y = corners[0].y;
617    ellCorners[1].x = corners[1].x;
618    ellCorners[1].y = corners[0].y+ell_height;
619    if(!PATH_DoArcPart(pPath, ellCorners, 0, -M_PI_2, TRUE))
620       return FALSE;
621    pointTemp.x = corners[0].x+ell_width/2;
622    pointTemp.y = corners[0].y;
623    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
624       return FALSE;
625    ellCorners[0].x = corners[0].x;
626    ellCorners[1].x = corners[0].x+ell_width;
627    if(!PATH_DoArcPart(pPath, ellCorners, -M_PI_2, -M_PI, FALSE))
628       return FALSE;
629    pointTemp.x = corners[0].x;
630    pointTemp.y = corners[1].y-ell_height/2;
631    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
632       return FALSE;
633    ellCorners[0].y = corners[1].y-ell_height;
634    ellCorners[1].y = corners[1].y;
635    if(!PATH_DoArcPart(pPath, ellCorners, M_PI, M_PI_2, FALSE))
636       return FALSE;
637    pointTemp.x = corners[1].x-ell_width/2;
638    pointTemp.y = corners[1].y;
639    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
640       return FALSE;
641    ellCorners[0].x = corners[1].x-ell_width;
642    ellCorners[1].x = corners[1].x;
643    if(!PATH_DoArcPart(pPath, ellCorners, M_PI_2, 0, FALSE))
644       return FALSE;
645
646    /* Close the roundrect figure */
647    if(!CloseFigure(dc->hSelf))
648       return FALSE;
649
650    return TRUE;
651 }
652
653 /* PATH_Rectangle
654  *
655  * Should be called when a call to Rectangle is performed on a DC that has
656  * an open path. Returns TRUE if successful, else FALSE.
657  */
658 BOOL PATH_Rectangle(DC *dc, INT x1, INT y1, INT x2, INT y2)
659 {
660    GdiPath *pPath = &dc->path;
661    POINT corners[2], pointTemp;
662
663    /* Check that path is open */
664    if(pPath->state!=PATH_Open)
665       return FALSE;
666
667    if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
668       return FALSE;
669
670    /* Close any previous figure */
671    if(!CloseFigure(dc->hSelf))
672    {
673       /* The CloseFigure call shouldn't have failed */
674       assert(FALSE);
675       return FALSE;
676    }
677
678    /* Add four points to the path */
679    pointTemp.x=corners[1].x;
680    pointTemp.y=corners[0].y;
681    if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
682       return FALSE;
683    if(!PATH_AddEntry(pPath, corners, PT_LINETO))
684       return FALSE;
685    pointTemp.x=corners[0].x;
686    pointTemp.y=corners[1].y;
687    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
688       return FALSE;
689    if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
690       return FALSE;
691
692    /* Close the rectangle figure */
693    if(!CloseFigure(dc->hSelf))
694    {
695       /* The CloseFigure call shouldn't have failed */
696       assert(FALSE);
697       return FALSE;
698    }
699
700    return TRUE;
701 }
702
703 /* PATH_Ellipse
704  *
705  * Should be called when a call to Ellipse is performed on a DC that has
706  * an open path. This adds four Bezier splines representing the ellipse
707  * to the path. Returns TRUE if successful, else FALSE.
708  */
709 BOOL PATH_Ellipse(DC *dc, INT x1, INT y1, INT x2, INT y2)
710 {
711    return( PATH_Arc(dc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2,0) &&
712            CloseFigure(dc->hSelf) );
713 }
714
715 /* PATH_Arc
716  *
717  * Should be called when a call to Arc is performed on a DC that has
718  * an open path. This adds up to five Bezier splines representing the arc
719  * to the path. When 'lines' is 1, we add 1 extra line to get a chord,
720  * and when 'lines' is 2, we add 2 extra lines to get a pie.
721  * Returns TRUE if successful, else FALSE.
722  */
723 BOOL PATH_Arc(DC *dc, INT x1, INT y1, INT x2, INT y2,
724    INT xStart, INT yStart, INT xEnd, INT yEnd, INT lines)
725 {
726    GdiPath     *pPath = &dc->path;
727    double      angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
728                /* Initialize angleEndQuadrant to silence gcc's warning */
729    double      x, y;
730    FLOAT_POINT corners[2], pointStart, pointEnd;
731    POINT       centre;
732    BOOL      start, end;
733    INT       temp;
734
735    /* FIXME: This function should check for all possible error returns */
736    /* FIXME: Do we have to respect newStroke? */
737
738    /* Check that path is open */
739    if(pPath->state!=PATH_Open)
740       return FALSE;
741
742    /* Check for zero height / width */
743    /* FIXME: Only in GM_COMPATIBLE? */
744    if(x1==x2 || y1==y2)
745       return TRUE;
746
747    /* Convert points to device coordinates */
748    corners[0].x=(FLOAT)x1;
749    corners[0].y=(FLOAT)y1;
750    corners[1].x=(FLOAT)x2;
751    corners[1].y=(FLOAT)y2;
752    pointStart.x=(FLOAT)xStart;
753    pointStart.y=(FLOAT)yStart;
754    pointEnd.x=(FLOAT)xEnd;
755    pointEnd.y=(FLOAT)yEnd;
756    INTERNAL_LPTODP_FLOAT(dc, corners);
757    INTERNAL_LPTODP_FLOAT(dc, corners+1);
758    INTERNAL_LPTODP_FLOAT(dc, &pointStart);
759    INTERNAL_LPTODP_FLOAT(dc, &pointEnd);
760
761    /* Make sure first corner is top left and second corner is bottom right */
762    if(corners[0].x>corners[1].x)
763    {
764       temp=corners[0].x;
765       corners[0].x=corners[1].x;
766       corners[1].x=temp;
767    }
768    if(corners[0].y>corners[1].y)
769    {
770       temp=corners[0].y;
771       corners[0].y=corners[1].y;
772       corners[1].y=temp;
773    }
774
775    /* Compute start and end angle */
776    PATH_NormalizePoint(corners, &pointStart, &x, &y);
777    angleStart=atan2(y, x);
778    PATH_NormalizePoint(corners, &pointEnd, &x, &y);
779    angleEnd=atan2(y, x);
780
781    /* Make sure the end angle is "on the right side" of the start angle */
782    if(dc->ArcDirection==AD_CLOCKWISE)
783    {
784       if(angleEnd<=angleStart)
785       {
786          angleEnd+=2*M_PI;
787          assert(angleEnd>=angleStart);
788       }
789    }
790    else
791    {
792       if(angleEnd>=angleStart)
793       {
794          angleEnd-=2*M_PI;
795          assert(angleEnd<=angleStart);
796       }
797    }
798
799    /* In GM_COMPATIBLE, don't include bottom and right edges */
800    if(dc->GraphicsMode==GM_COMPATIBLE)
801    {
802       corners[1].x--;
803       corners[1].y--;
804    }
805
806    /* Add the arc to the path with one Bezier spline per quadrant that the
807     * arc spans */
808    start=TRUE;
809    end=FALSE;
810    do
811    {
812       /* Determine the start and end angles for this quadrant */
813       if(start)
814       {
815          angleStartQuadrant=angleStart;
816          if(dc->ArcDirection==AD_CLOCKWISE)
817             angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
818          else
819             angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
820       }
821       else
822       {
823          angleStartQuadrant=angleEndQuadrant;
824          if(dc->ArcDirection==AD_CLOCKWISE)
825             angleEndQuadrant+=M_PI_2;
826          else
827             angleEndQuadrant-=M_PI_2;
828       }
829
830       /* Have we reached the last part of the arc? */
831       if((dc->ArcDirection==AD_CLOCKWISE &&
832          angleEnd<angleEndQuadrant) ||
833          (dc->ArcDirection==AD_COUNTERCLOCKWISE &&
834          angleEnd>angleEndQuadrant))
835       {
836          /* Adjust the end angle for this quadrant */
837          angleEndQuadrant=angleEnd;
838          end=TRUE;
839       }
840
841       /* Add the Bezier spline to the path */
842       PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant,
843          start);
844       start=FALSE;
845    }  while(!end);
846
847    /* chord: close figure. pie: add line and close figure */
848    if(lines==1)
849    {
850       if(!CloseFigure(dc->hSelf))
851          return FALSE;
852    }
853    else if(lines==2)
854    {
855       centre.x = (corners[0].x+corners[1].x)/2;
856       centre.y = (corners[0].y+corners[1].y)/2;
857       if(!PATH_AddEntry(pPath, &centre, PT_LINETO | PT_CLOSEFIGURE))
858          return FALSE;
859    }
860
861    return TRUE;
862 }
863
864 BOOL PATH_PolyBezierTo(DC *dc, const POINT *pts, DWORD cbPoints)
865 {
866    GdiPath     *pPath = &dc->path;
867    POINT       pt;
868    INT         i;
869
870    /* Check that path is open */
871    if(pPath->state!=PATH_Open)
872       return FALSE;
873
874    /* Add a PT_MOVETO if necessary */
875    if(pPath->newStroke)
876    {
877       pPath->newStroke=FALSE;
878       pt.x = dc->CursPosX;
879       pt.y = dc->CursPosY;
880       if(!LPtoDP(dc->hSelf, &pt, 1))
881          return FALSE;
882       if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
883          return FALSE;
884    }
885
886    for(i = 0; i < cbPoints; i++) {
887        pt = pts[i];
888        if(!LPtoDP(dc->hSelf, &pt, 1))
889            return FALSE;
890        PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
891    }
892    return TRUE;
893 }
894
895 BOOL PATH_PolyBezier(DC *dc, const POINT *pts, DWORD cbPoints)
896 {
897    GdiPath     *pPath = &dc->path;
898    POINT       pt;
899    INT         i;
900
901    /* Check that path is open */
902    if(pPath->state!=PATH_Open)
903       return FALSE;
904
905    for(i = 0; i < cbPoints; i++) {
906        pt = pts[i];
907        if(!LPtoDP(dc->hSelf, &pt, 1))
908            return FALSE;
909        PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO);
910    }
911    return TRUE;
912 }
913
914 BOOL PATH_Polyline(DC *dc, const POINT *pts, DWORD cbPoints)
915 {
916    GdiPath     *pPath = &dc->path;
917    POINT       pt;
918    INT         i;
919
920    /* Check that path is open */
921    if(pPath->state!=PATH_Open)
922       return FALSE;
923
924    for(i = 0; i < cbPoints; i++) {
925        pt = pts[i];
926        if(!LPtoDP(dc->hSelf, &pt, 1))
927            return FALSE;
928        PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
929    }
930    return TRUE;
931 }
932
933 BOOL PATH_PolylineTo(DC *dc, const POINT *pts, DWORD cbPoints)
934 {
935    GdiPath     *pPath = &dc->path;
936    POINT       pt;
937    INT         i;
938
939    /* Check that path is open */
940    if(pPath->state!=PATH_Open)
941       return FALSE;
942
943    /* Add a PT_MOVETO if necessary */
944    if(pPath->newStroke)
945    {
946       pPath->newStroke=FALSE;
947       pt.x = dc->CursPosX;
948       pt.y = dc->CursPosY;
949       if(!LPtoDP(dc->hSelf, &pt, 1))
950          return FALSE;
951       if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
952          return FALSE;
953    }
954
955    for(i = 0; i < cbPoints; i++) {
956        pt = pts[i];
957        if(!LPtoDP(dc->hSelf, &pt, 1))
958            return FALSE;
959        PATH_AddEntry(pPath, &pt, PT_LINETO);
960    }
961
962    return TRUE;
963 }
964
965
966 BOOL PATH_Polygon(DC *dc, const POINT *pts, DWORD cbPoints)
967 {
968    GdiPath     *pPath = &dc->path;
969    POINT       pt;
970    INT         i;
971
972    /* Check that path is open */
973    if(pPath->state!=PATH_Open)
974       return FALSE;
975
976    for(i = 0; i < cbPoints; i++) {
977        pt = pts[i];
978        if(!LPtoDP(dc->hSelf, &pt, 1))
979            return FALSE;
980        PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
981                      ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
982                       PT_LINETO));
983    }
984    return TRUE;
985 }
986
987 BOOL PATH_PolyPolygon( DC *dc, const POINT* pts, const INT* counts,
988                        UINT polygons )
989 {
990    GdiPath     *pPath = &dc->path;
991    POINT       pt, startpt;
992    INT         poly, point, i;
993
994    /* Check that path is open */
995    if(pPath->state!=PATH_Open)
996       return FALSE;
997
998    for(i = 0, poly = 0; poly < polygons; poly++) {
999        for(point = 0; point < counts[poly]; point++, i++) {
1000            pt = pts[i];
1001            if(!LPtoDP(dc->hSelf, &pt, 1))
1002                return FALSE;
1003            if(point == 0) startpt = pt;
1004            PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1005        }
1006        /* win98 adds an extra line to close the figure for some reason */
1007        PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
1008    }
1009    return TRUE;
1010 }
1011
1012 BOOL PATH_PolyPolyline( DC *dc, const POINT* pts, const DWORD* counts,
1013                         DWORD polylines )
1014 {
1015    GdiPath     *pPath = &dc->path;
1016    POINT       pt;
1017    INT         poly, point, i;
1018
1019    /* Check that path is open */
1020    if(pPath->state!=PATH_Open)
1021       return FALSE;
1022
1023    for(i = 0, poly = 0; poly < polylines; poly++) {
1024        for(point = 0; point < counts[poly]; point++, i++) {
1025            pt = pts[i];
1026            if(!LPtoDP(dc->hSelf, &pt, 1))
1027                return FALSE;
1028            PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1029        }
1030    }
1031    return TRUE;
1032 }
1033
1034 /***********************************************************************
1035  * Internal functions
1036  */
1037
1038 /* PATH_CheckCorners
1039  *
1040  * Helper function for PATH_RoundRect() and PATH_Rectangle()
1041  */
1042 static BOOL PATH_CheckCorners(DC *dc, POINT corners[], INT x1, INT y1, INT x2, INT y2)
1043 {
1044    INT temp;
1045
1046    /* Convert points to device coordinates */
1047    corners[0].x=x1;
1048    corners[0].y=y1;
1049    corners[1].x=x2;
1050    corners[1].y=y2;
1051    if(!LPtoDP(dc->hSelf, corners, 2))
1052       return FALSE;
1053
1054    /* Make sure first corner is top left and second corner is bottom right */
1055    if(corners[0].x>corners[1].x)
1056    {
1057       temp=corners[0].x;
1058       corners[0].x=corners[1].x;
1059       corners[1].x=temp;
1060    }
1061    if(corners[0].y>corners[1].y)
1062    {
1063       temp=corners[0].y;
1064       corners[0].y=corners[1].y;
1065       corners[1].y=temp;
1066    }
1067
1068    /* In GM_COMPATIBLE, don't include bottom and right edges */
1069    if(dc->GraphicsMode==GM_COMPATIBLE)
1070    {
1071       corners[1].x--;
1072       corners[1].y--;
1073    }
1074
1075    return TRUE;
1076 }
1077
1078 /* PATH_AddFlatBezier
1079  */
1080 static BOOL PATH_AddFlatBezier(GdiPath *pPath, POINT *pt, BOOL closed)
1081 {
1082     POINT *pts;
1083     INT no, i;
1084
1085     pts = GDI_Bezier( pt, 4, &no );
1086     if(!pts) return FALSE;
1087
1088     for(i = 1; i < no; i++)
1089         PATH_AddEntry(pPath, &pts[i],
1090             (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
1091     HeapFree( GetProcessHeap(), 0, pts );
1092     return TRUE;
1093 }
1094
1095 /* PATH_FlattenPath
1096  *
1097  * Replaces Beziers with line segments
1098  *
1099  */
1100 static BOOL PATH_FlattenPath(GdiPath *pPath)
1101 {
1102     GdiPath newPath;
1103     INT srcpt;
1104
1105     memset(&newPath, 0, sizeof(newPath));
1106     newPath.state = PATH_Open;
1107     for(srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++) {
1108         switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE) {
1109         case PT_MOVETO:
1110         case PT_LINETO:
1111             PATH_AddEntry(&newPath, &pPath->pPoints[srcpt],
1112                           pPath->pFlags[srcpt]);
1113             break;
1114         case PT_BEZIERTO:
1115           PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1],
1116                              pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
1117             srcpt += 2;
1118             break;
1119         }
1120     }
1121     newPath.state = PATH_Closed;
1122     PATH_AssignGdiPath(pPath, &newPath);
1123     PATH_EmptyPath(&newPath);
1124     return TRUE;
1125 }
1126
1127 /* PATH_PathToRegion
1128  *
1129  * Creates a region from the specified path using the specified polygon
1130  * filling mode. The path is left unchanged. A handle to the region that
1131  * was created is stored in *pHrgn. If successful, TRUE is returned; if an
1132  * error occurs, SetLastError is called with the appropriate value and
1133  * FALSE is returned.
1134  */
1135 static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode,
1136    HRGN *pHrgn)
1137 {
1138    int    numStrokes, iStroke, i;
1139    INT  *pNumPointsInStroke;
1140    HRGN hrgn;
1141
1142    assert(pPath!=NULL);
1143    assert(pHrgn!=NULL);
1144
1145    PATH_FlattenPath(pPath);
1146
1147    /* FIXME: What happens when number of points is zero? */
1148
1149    /* First pass: Find out how many strokes there are in the path */
1150    /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
1151    numStrokes=0;
1152    for(i=0; i<pPath->numEntriesUsed; i++)
1153       if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1154          numStrokes++;
1155
1156    /* Allocate memory for number-of-points-in-stroke array */
1157    pNumPointsInStroke=(int *)HeapAlloc( GetProcessHeap(), 0,
1158                                         sizeof(int) * numStrokes );
1159    if(!pNumPointsInStroke)
1160    {
1161       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1162       return FALSE;
1163    }
1164
1165    /* Second pass: remember number of points in each polygon */
1166    iStroke=-1;  /* Will get incremented to 0 at beginning of first stroke */
1167    for(i=0; i<pPath->numEntriesUsed; i++)
1168    {
1169       /* Is this the beginning of a new stroke? */
1170       if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1171       {
1172          iStroke++;
1173          pNumPointsInStroke[iStroke]=0;
1174       }
1175
1176       pNumPointsInStroke[iStroke]++;
1177    }
1178
1179    /* Create a region from the strokes */
1180    hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
1181       numStrokes, nPolyFillMode);
1182
1183    /* Free memory for number-of-points-in-stroke array */
1184    HeapFree( GetProcessHeap(), 0, pNumPointsInStroke );
1185
1186    if(hrgn==NULL)
1187    {
1188       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1189       return FALSE;
1190    }
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_DoArcPart
1312  *
1313  * Creates a Bezier spline that corresponds to part of an arc and appends the
1314  * corresponding points to the path. The start and end angles are passed in
1315  * "angleStart" and "angleEnd"; these angles should span a quarter circle
1316  * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
1317  * point is added to the path; otherwise, it is assumed that the current
1318  * position is equal to the first control point.
1319  */
1320 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
1321    double angleStart, double angleEnd, BOOL addMoveTo)
1322 {
1323    double  halfAngle, a;
1324    double  xNorm[4], yNorm[4];
1325    POINT point;
1326    int     i;
1327
1328    assert(fabs(angleEnd-angleStart)<=M_PI_2);
1329
1330    /* FIXME: Is there an easier way of computing this? */
1331
1332    /* Compute control points */
1333    halfAngle=(angleEnd-angleStart)/2.0;
1334    if(fabs(halfAngle)>1e-8)
1335    {
1336       a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
1337       xNorm[0]=cos(angleStart);
1338       yNorm[0]=sin(angleStart);
1339       xNorm[1]=xNorm[0] - a*yNorm[0];
1340       yNorm[1]=yNorm[0] + a*xNorm[0];
1341       xNorm[3]=cos(angleEnd);
1342       yNorm[3]=sin(angleEnd);
1343       xNorm[2]=xNorm[3] + a*yNorm[3];
1344       yNorm[2]=yNorm[3] - a*xNorm[3];
1345    }
1346    else
1347       for(i=0; i<4; i++)
1348       {
1349          xNorm[i]=cos(angleStart);
1350          yNorm[i]=sin(angleStart);
1351       }
1352
1353    /* Add starting point to path if desired */
1354    if(addMoveTo)
1355    {
1356       PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
1357       if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
1358          return FALSE;
1359    }
1360
1361    /* Add remaining control points */
1362    for(i=1; i<4; i++)
1363    {
1364       PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
1365       if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1366          return FALSE;
1367    }
1368
1369    return TRUE;
1370 }
1371
1372 /* PATH_ScaleNormalizedPoint
1373  *
1374  * Scales a normalized point (x, y) with respect to the box whose corners are
1375  * passed in "corners". The point is stored in "*pPoint". The normalized
1376  * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1377  * (1.0, 1.0) correspond to corners[1].
1378  */
1379 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
1380    double y, POINT *pPoint)
1381 {
1382    pPoint->x=GDI_ROUND( (double)corners[0].x +
1383       (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
1384    pPoint->y=GDI_ROUND( (double)corners[0].y +
1385       (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
1386 }
1387
1388 /* PATH_NormalizePoint
1389  *
1390  * Normalizes a point with respect to the box whose corners are passed in
1391  * "corners". The normalized coordinates are stored in "*pX" and "*pY".
1392  */
1393 static void PATH_NormalizePoint(FLOAT_POINT corners[],
1394    const FLOAT_POINT *pPoint,
1395    double *pX, double *pY)
1396 {
1397    *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) *
1398       2.0 - 1.0;
1399    *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) *
1400       2.0 - 1.0;
1401 }
1402
1403
1404 /*******************************************************************
1405  *      FlattenPath [GDI32.@]
1406  *
1407  *
1408  */
1409 BOOL WINAPI FlattenPath(HDC hdc)
1410 {
1411     BOOL ret = FALSE;
1412     DC *dc = DC_GetDCPtr( hdc );
1413
1414     if(!dc) return FALSE;
1415
1416     if(dc->funcs->pFlattenPath) ret = dc->funcs->pFlattenPath(dc->physDev);
1417     else
1418     {
1419         GdiPath *pPath = &dc->path;
1420         if(pPath->state != PATH_Closed)
1421             ret = PATH_FlattenPath(pPath);
1422     }
1423     GDI_ReleaseObj( hdc );
1424     return ret;
1425 }
1426
1427
1428 static BOOL PATH_StrokePath(DC *dc, GdiPath *pPath)
1429 {
1430     INT i;
1431     POINT ptLastMove = {0,0};
1432     POINT ptViewportOrg, ptWindowOrg;
1433     SIZE szViewportExt, szWindowExt;
1434     DWORD mapMode, graphicsMode;
1435     XFORM xform;
1436     BOOL ret = TRUE;
1437
1438     if(dc->funcs->pStrokePath)
1439         return dc->funcs->pStrokePath(dc->physDev);
1440
1441     if(pPath->state != PATH_Closed)
1442         return FALSE;
1443
1444     /* Save the mapping mode info */
1445     mapMode=GetMapMode(dc->hSelf);
1446     GetViewportExtEx(dc->hSelf, &szViewportExt);
1447     GetViewportOrgEx(dc->hSelf, &ptViewportOrg);
1448     GetWindowExtEx(dc->hSelf, &szWindowExt);
1449     GetWindowOrgEx(dc->hSelf, &ptWindowOrg);
1450     GetWorldTransform(dc->hSelf, &xform);
1451
1452     /* Set MM_TEXT */
1453     SetMapMode(dc->hSelf, MM_TEXT);
1454     SetViewportOrgEx(dc->hSelf, 0, 0, NULL);
1455     SetWindowOrgEx(dc->hSelf, 0, 0, NULL);
1456
1457
1458     for(i = 0; i < pPath->numEntriesUsed; i++) {
1459         switch(pPath->pFlags[i]) {
1460         case PT_MOVETO:
1461             TRACE("Got PT_MOVETO (%ld, %ld)\n",
1462                   pPath->pPoints[i].x, pPath->pPoints[i].y);
1463             MoveToEx(dc->hSelf, pPath->pPoints[i].x, pPath->pPoints[i].y, NULL);
1464             ptLastMove = pPath->pPoints[i];
1465             break;
1466         case PT_LINETO:
1467         case (PT_LINETO | PT_CLOSEFIGURE):
1468             TRACE("Got PT_LINETO (%ld, %ld)\n",
1469                   pPath->pPoints[i].x, pPath->pPoints[i].y);
1470             LineTo(dc->hSelf, pPath->pPoints[i].x, pPath->pPoints[i].y);
1471             break;
1472         case PT_BEZIERTO:
1473             TRACE("Got PT_BEZIERTO\n");
1474             if(pPath->pFlags[i+1] != PT_BEZIERTO ||
1475                (pPath->pFlags[i+2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) {
1476                 ERR("Path didn't contain 3 successive PT_BEZIERTOs\n");
1477                 ret = FALSE;
1478                 goto end;
1479             }
1480             PolyBezierTo(dc->hSelf, &pPath->pPoints[i], 3);
1481             i += 2;
1482             break;
1483         default:
1484             ERR("Got path flag %d\n", (INT)pPath->pFlags[i]);
1485             ret = FALSE;
1486             goto end;
1487         }
1488         if(pPath->pFlags[i] & PT_CLOSEFIGURE)
1489             LineTo(dc->hSelf, ptLastMove.x, ptLastMove.y);
1490     }
1491
1492  end:
1493
1494     /* Restore the old mapping mode */
1495     SetMapMode(dc->hSelf, mapMode);
1496     SetViewportExtEx(dc->hSelf, szViewportExt.cx, szViewportExt.cy, NULL);
1497     SetViewportOrgEx(dc->hSelf, ptViewportOrg.x, ptViewportOrg.y, NULL);
1498     SetWindowExtEx(dc->hSelf, szWindowExt.cx, szWindowExt.cy, NULL);
1499     SetWindowOrgEx(dc->hSelf, ptWindowOrg.x, ptWindowOrg.y, NULL);
1500
1501     /* Go to GM_ADVANCED temporarily to restore the world transform */
1502     graphicsMode=GetGraphicsMode(dc->hSelf);
1503     SetGraphicsMode(dc->hSelf, GM_ADVANCED);
1504     SetWorldTransform(dc->hSelf, &xform);
1505     SetGraphicsMode(dc->hSelf, graphicsMode);
1506
1507     /* If we've moved the current point then get its new position
1508        which will be in device (MM_TEXT) co-ords, convert it to
1509        logical co-ords and re-set it.  This basically updates
1510        dc->CurPosX|Y so that their values are in the correct mapping
1511        mode.
1512     */
1513     if(i > 0) {
1514         POINT pt;
1515         GetCurrentPositionEx(dc->hSelf, &pt);
1516         DPtoLP(dc->hSelf, &pt, 1);
1517         MoveToEx(dc->hSelf, pt.x, pt.y, NULL);
1518     }
1519     return ret;
1520 }
1521
1522
1523 /*******************************************************************
1524  *      StrokeAndFillPath [GDI32.@]
1525  *
1526  *
1527  */
1528 BOOL WINAPI StrokeAndFillPath(HDC hdc)
1529 {
1530    DC *dc = DC_GetDCPtr( hdc );
1531    BOOL bRet = FALSE;
1532
1533    if(!dc) return FALSE;
1534
1535    if(dc->funcs->pStrokeAndFillPath)
1536        bRet = dc->funcs->pStrokeAndFillPath(dc->physDev);
1537    else
1538    {
1539        bRet = PATH_FillPath(dc, &dc->path);
1540        if(bRet) bRet = PATH_StrokePath(dc, &dc->path);
1541        if(bRet) PATH_EmptyPath(&dc->path);
1542    }
1543    GDI_ReleaseObj( hdc );
1544    return bRet;
1545 }
1546
1547
1548 /*******************************************************************
1549  *      StrokePath [GDI32.@]
1550  *
1551  *
1552  */
1553 BOOL WINAPI StrokePath(HDC hdc)
1554 {
1555     DC *dc = DC_GetDCPtr( hdc );
1556     GdiPath *pPath;
1557     BOOL bRet = FALSE;
1558
1559     TRACE("(%p)\n", hdc);
1560     if(!dc) return FALSE;
1561
1562     if(dc->funcs->pStrokePath)
1563         bRet = dc->funcs->pStrokePath(dc->physDev);
1564     else
1565     {
1566         pPath = &dc->path;
1567         bRet = PATH_StrokePath(dc, pPath);
1568         PATH_EmptyPath(pPath);
1569     }
1570     GDI_ReleaseObj( hdc );
1571     return bRet;
1572 }
1573
1574
1575 /*******************************************************************
1576  *      WidenPath [GDI32.@]
1577  *
1578  *
1579  */
1580 BOOL WINAPI WidenPath(HDC hdc)
1581 {
1582    DC *dc = DC_GetDCPtr( hdc );
1583    BOOL ret = FALSE;
1584
1585    if(!dc) return FALSE;
1586
1587    if(dc->funcs->pWidenPath)
1588      ret = dc->funcs->pWidenPath(dc->physDev);
1589
1590    FIXME("stub\n");
1591    GDI_ReleaseObj( hdc );
1592    return ret;
1593 }