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