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