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