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