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