~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/gdi32/path.c

Version: ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  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, BYTE startEntryType);
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 
126 /***********************************************************************
127  *           BeginPath    (GDI32.@)
128  */
129 BOOL WINAPI BeginPath(HDC hdc)
130 {
131     BOOL ret = TRUE;
132     DC *dc = get_dc_ptr( hdc );
133 
134     if(!dc) return FALSE;
135 
136     if(dc->funcs->pBeginPath)
137         ret = dc->funcs->pBeginPath(dc->physDev);
138     else
139     {
140         /* If path is already open, do nothing */
141         if(dc->path.state != PATH_Open)
142         {
143             /* Make sure that path is empty */
144             PATH_EmptyPath(&dc->path);
145 
146             /* Initialize variables for new path */
147             dc->path.newStroke=TRUE;
148             dc->path.state=PATH_Open;
149         }
150     }
151     release_dc_ptr( dc );
152     return ret;
153 }
154 
155 
156 /***********************************************************************
157  *           EndPath    (GDI32.@)
158  */
159 BOOL WINAPI EndPath(HDC hdc)
160 {
161     BOOL ret = TRUE;
162     DC *dc = get_dc_ptr( hdc );
163 
164     if(!dc) return FALSE;
165 
166     if(dc->funcs->pEndPath)
167         ret = dc->funcs->pEndPath(dc->physDev);
168     else
169     {
170         /* Check that path is currently being constructed */
171         if(dc->path.state!=PATH_Open)
172         {
173             SetLastError(ERROR_CAN_NOT_COMPLETE);
174             ret = FALSE;
175         }
176         /* Set flag to indicate that path is finished */
177         else dc->path.state=PATH_Closed;
178     }
179     release_dc_ptr( dc );
180     return ret;
181 }
182 
183 
184 /******************************************************************************
185  * AbortPath [GDI32.@]
186  * Closes and discards paths from device context
187  *
188  * NOTES
189  *    Check that SetLastError is being called correctly
190  *
191  * PARAMS
192  *    hdc [I] Handle to device context
193  *
194  * RETURNS
195  *    Success: TRUE
196  *    Failure: FALSE
197  */
198 BOOL WINAPI AbortPath( HDC hdc )
199 {
200     BOOL ret = TRUE;
201     DC *dc = get_dc_ptr( hdc );
202 
203     if(!dc) return FALSE;
204 
205     if(dc->funcs->pAbortPath)
206         ret = dc->funcs->pAbortPath(dc->physDev);
207     else /* Remove all entries from the path */
208         PATH_EmptyPath( &dc->path );
209     release_dc_ptr( dc );
210     return ret;
211 }
212 
213 
214 /***********************************************************************
215  *           CloseFigure    (GDI32.@)
216  *
217  * FIXME: Check that SetLastError is being called correctly
218  */
219 BOOL WINAPI CloseFigure(HDC hdc)
220 {
221     BOOL ret = TRUE;
222     DC *dc = get_dc_ptr( hdc );
223 
224     if(!dc) return FALSE;
225 
226     if(dc->funcs->pCloseFigure)
227         ret = dc->funcs->pCloseFigure(dc->physDev);
228     else
229     {
230         /* Check that path is open */
231         if(dc->path.state!=PATH_Open)
232         {
233             SetLastError(ERROR_CAN_NOT_COMPLETE);
234             ret = FALSE;
235         }
236         else
237         {
238             /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
239             /* It is not necessary to draw a line, PT_CLOSEFIGURE is a virtual closing line itself */
240             if(dc->path.numEntriesUsed)
241             {
242                 dc->path.pFlags[dc->path.numEntriesUsed-1]|=PT_CLOSEFIGURE;
243                 dc->path.newStroke=TRUE;
244             }
245         }
246     }
247     release_dc_ptr( dc );
248     return ret;
249 }
250 
251 
252 /***********************************************************************
253  *           GetPath    (GDI32.@)
254  */
255 INT WINAPI GetPath(HDC hdc, LPPOINT pPoints, LPBYTE pTypes,
256    INT nSize)
257 {
258    INT ret = -1;
259    GdiPath *pPath;
260    DC *dc = get_dc_ptr( hdc );
261 
262    if(!dc) return -1;
263 
264    pPath = &dc->path;
265 
266    /* Check that path is closed */
267    if(pPath->state!=PATH_Closed)
268    {
269       SetLastError(ERROR_CAN_NOT_COMPLETE);
270       goto done;
271    }
272 
273    if(nSize==0)
274       ret = pPath->numEntriesUsed;
275    else if(nSize<pPath->numEntriesUsed)
276    {
277       SetLastError(ERROR_INVALID_PARAMETER);
278       goto done;
279    }
280    else
281    {
282       memcpy(pPoints, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
283       memcpy(pTypes, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
284 
285       /* Convert the points to logical coordinates */
286       if(!DPtoLP(hdc, pPoints, pPath->numEntriesUsed))
287       {
288          /* FIXME: Is this the correct value? */
289          SetLastError(ERROR_CAN_NOT_COMPLETE);
290         goto done;
291       }
292      else ret = pPath->numEntriesUsed;
293    }
294  done:
295    release_dc_ptr( dc );
296    return ret;
297 }
298 
299 
300 /***********************************************************************
301  *           PathToRegion    (GDI32.@)
302  *
303  * FIXME
304  *   Check that SetLastError is being called correctly
305  *
306  * The documentation does not state this explicitly, but a test under Windows
307  * shows that the region which is returned should be in device coordinates.
308  */
309 HRGN WINAPI PathToRegion(HDC hdc)
310 {
311    GdiPath *pPath;
312    HRGN  hrgnRval = 0;
313    DC *dc = get_dc_ptr( hdc );
314 
315    /* Get pointer to path */
316    if(!dc) return 0;
317 
318     pPath = &dc->path;
319 
320    /* Check that path is closed */
321    if(pPath->state!=PATH_Closed) SetLastError(ERROR_CAN_NOT_COMPLETE);
322    else
323    {
324        /* FIXME: Should we empty the path even if conversion failed? */
325        if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnRval))
326            PATH_EmptyPath(pPath);
327        else
328            hrgnRval=0;
329    }
330    release_dc_ptr( dc );
331    return hrgnRval;
332 }
333 
334 static BOOL PATH_FillPath(DC *dc, GdiPath *pPath)
335 {
336    INT   mapMode, graphicsMode;
337    SIZE  ptViewportExt, ptWindowExt;
338    POINT ptViewportOrg, ptWindowOrg;
339    XFORM xform;
340    HRGN  hrgn;
341 
342    if(dc->funcs->pFillPath)
343        return dc->funcs->pFillPath(dc->physDev);
344 
345    /* Check that path is closed */
346    if(pPath->state!=PATH_Closed)
347    {
348       SetLastError(ERROR_CAN_NOT_COMPLETE);
349       return FALSE;
350    }
351 
352    /* Construct a region from the path and fill it */
353    if(PATH_PathToRegion(pPath, dc->polyFillMode, &hrgn))
354    {
355       /* Since PaintRgn interprets the region as being in logical coordinates
356        * but the points we store for the path are already in device
357        * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
358        * Using SaveDC to save information about the mapping mode / world
359        * transform would be easier but would require more overhead, especially
360        * now that SaveDC saves the current path.
361        */
362 
363       /* Save the information about the old mapping mode */
364       mapMode=GetMapMode(dc->hSelf);
365       GetViewportExtEx(dc->hSelf, &ptViewportExt);
366       GetViewportOrgEx(dc->hSelf, &ptViewportOrg);
367       GetWindowExtEx(dc->hSelf, &ptWindowExt);
368       GetWindowOrgEx(dc->hSelf, &ptWindowOrg);
369 
370       /* Save world transform
371        * NB: The Windows documentation on world transforms would lead one to
372        * believe that this has to be done only in GM_ADVANCED; however, my
373        * tests show that resetting the graphics mode to GM_COMPATIBLE does
374        * not reset the world transform.
375        */
376       GetWorldTransform(dc->hSelf, &xform);
377 
378       /* Set MM_TEXT */
379       SetMapMode(dc->hSelf, MM_TEXT);
380       SetViewportOrgEx(dc->hSelf, 0, 0, NULL);
381       SetWindowOrgEx(dc->hSelf, 0, 0, NULL);
382       graphicsMode=GetGraphicsMode(dc->hSelf);
383       SetGraphicsMode(dc->hSelf, GM_ADVANCED);
384       ModifyWorldTransform(dc->hSelf, &xform, MWT_IDENTITY);
385       SetGraphicsMode(dc->hSelf, graphicsMode);
386 
387       /* Paint the region */
388       PaintRgn(dc->hSelf, hrgn);
389       DeleteObject(hrgn);
390       /* Restore the old mapping mode */
391       SetMapMode(dc->hSelf, mapMode);
392       SetViewportExtEx(dc->hSelf, ptViewportExt.cx, ptViewportExt.cy, NULL);
393       SetViewportOrgEx(dc->hSelf, ptViewportOrg.x, ptViewportOrg.y, NULL);
394       SetWindowExtEx(dc->hSelf, ptWindowExt.cx, ptWindowExt.cy, NULL);
395       SetWindowOrgEx(dc->hSelf, ptWindowOrg.x, ptWindowOrg.y, NULL);
396 
397       /* Go to GM_ADVANCED temporarily to restore the world transform */
398       graphicsMode=GetGraphicsMode(dc->hSelf);
399       SetGraphicsMode(dc->hSelf, GM_ADVANCED);
400       SetWorldTransform(dc->hSelf, &xform);
401       SetGraphicsMode(dc->hSelf, graphicsMode);
402       return TRUE;
403    }
404    return FALSE;
405 }
406 
407 
408 /***********************************************************************
409  *           FillPath    (GDI32.@)
410  *
411  * FIXME
412  *    Check that SetLastError is being called correctly
413  */
414 BOOL WINAPI FillPath(HDC hdc)
415 {
416     DC *dc = get_dc_ptr( hdc );
417     BOOL bRet = FALSE;
418 
419     if(!dc) return FALSE;
420 
421     if(dc->funcs->pFillPath)
422         bRet = dc->funcs->pFillPath(dc->physDev);
423     else
424     {
425         bRet = PATH_FillPath(dc, &dc->path);
426         if(bRet)
427         {
428             /* FIXME: Should the path be emptied even if conversion
429                failed? */
430             PATH_EmptyPath(&dc->path);
431         }
432     }
433     release_dc_ptr( dc );
434     return bRet;
435 }
436 
437 
438 /***********************************************************************
439  *           SelectClipPath    (GDI32.@)
440  * FIXME
441  *  Check that SetLastError is being called correctly
442  */
443 BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
444 {
445    GdiPath *pPath;
446    HRGN  hrgnPath;
447    BOOL  success = FALSE;
448    DC *dc = get_dc_ptr( hdc );
449 
450    if(!dc) return FALSE;
451 
452    if(dc->funcs->pSelectClipPath)
453      success = dc->funcs->pSelectClipPath(dc->physDev, iMode);
454    else
455    {
456        pPath = &dc->path;
457 
458        /* Check that path is closed */
459        if(pPath->state!=PATH_Closed)
460            SetLastError(ERROR_CAN_NOT_COMPLETE);
461        /* Construct a region from the path */
462        else if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnPath))
463        {
464            success = ExtSelectClipRgn( hdc, hrgnPath, iMode ) != ERROR;
465            DeleteObject(hrgnPath);
466 
467            /* Empty the path */
468            if(success)
469                PATH_EmptyPath(pPath);
470            /* FIXME: Should this function delete the path even if it failed? */
471        }
472    }
473    release_dc_ptr( dc );
474    return success;
475 }
476 
477 
478 /***********************************************************************
479  * Exported functions
480  */
481 
482 /* PATH_InitGdiPath
483  *
484  * Initializes the GdiPath structure.
485  */
486 void PATH_InitGdiPath(GdiPath *pPath)
487 {
488    assert(pPath!=NULL);
489 
490    pPath->state=PATH_Null;
491    pPath->pPoints=NULL;
492    pPath->pFlags=NULL;
493    pPath->numEntriesUsed=0;
494    pPath->numEntriesAllocated=0;
495 }
496 
497 /* PATH_DestroyGdiPath
498  *
499  * Destroys a GdiPath structure (frees the memory in the arrays).
500  */
501 void PATH_DestroyGdiPath(GdiPath *pPath)
502 {
503    assert(pPath!=NULL);
504 
505    HeapFree( GetProcessHeap(), 0, pPath->pPoints );
506    HeapFree( GetProcessHeap(), 0, pPath->pFlags );
507 }
508 
509 /* PATH_AssignGdiPath
510  *
511  * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
512  * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
513  * not just the pointers. Since this means that the arrays in pPathDest may
514  * need to be resized, pPathDest should have been initialized using
515  * PATH_InitGdiPath (in C++, this function would be an assignment operator,
516  * not a copy constructor).
517  * Returns TRUE if successful, else FALSE.
518  */
519 BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
520 {
521    assert(pPathDest!=NULL && pPathSrc!=NULL);
522 
523    /* Make sure destination arrays are big enough */
524    if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
525       return FALSE;
526 
527    /* Perform the copy operation */
528    memcpy(pPathDest->pPoints, pPathSrc->pPoints,
529       sizeof(POINT)*pPathSrc->numEntriesUsed);
530    memcpy(pPathDest->pFlags, pPathSrc->pFlags,
531       sizeof(BYTE)*pPathSrc->numEntriesUsed);
532 
533    pPathDest->state=pPathSrc->state;
534    pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
535    pPathDest->newStroke=pPathSrc->newStroke;
536 
537    return TRUE;
538 }
539 
540 /* PATH_MoveTo
541  *
542  * Should be called when a MoveTo is performed on a DC that has an
543  * open path. This starts a new stroke. Returns TRUE if successful, else
544  * FALSE.
545  */
546 BOOL PATH_MoveTo(DC *dc)
547 {
548    GdiPath *pPath = &dc->path;
549 
550    /* Check that path is open */
551    if(pPath->state!=PATH_Open)
552       /* FIXME: Do we have to call SetLastError? */
553       return FALSE;
554 
555    /* Start a new stroke */
556    pPath->newStroke=TRUE;
557 
558    return TRUE;
559 }
560 
561 /* PATH_LineTo
562  *
563  * Should be called when a LineTo is performed on a DC that has an
564  * open path. This adds a PT_LINETO entry to the path (and possibly
565  * a PT_MOVETO entry, if this is the first LineTo in a stroke).
566  * Returns TRUE if successful, else FALSE.
567  */
568 BOOL PATH_LineTo(DC *dc, INT x, INT y)
569 {
570    GdiPath *pPath = &dc->path;
571    POINT point, pointCurPos;
572 
573    /* Check that path is open */
574    if(pPath->state!=PATH_Open)
575       return FALSE;
576 
577    /* Convert point to device coordinates */
578    point.x=x;
579    point.y=y;
580    if(!LPtoDP(dc->hSelf, &point, 1))
581       return FALSE;
582 
583    /* Add a PT_MOVETO if necessary */
584    if(pPath->newStroke)
585    {
586       pPath->newStroke=FALSE;
587       pointCurPos.x = dc->CursPosX;
588       pointCurPos.y = dc->CursPosY;
589       if(!LPtoDP(dc->hSelf, &pointCurPos, 1))
590          return FALSE;
591       if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
592          return FALSE;
593    }
594 
595    /* Add a PT_LINETO entry */
596    return PATH_AddEntry(pPath, &point, PT_LINETO);
597 }
598 
599 /* PATH_RoundRect
600  *
601  * Should be called when a call to RoundRect is performed on a DC that has
602  * an open path. Returns TRUE if successful, else FALSE.
603  *
604  * FIXME: it adds the same entries to the path as windows does, but there
605  * is an error in the bezier drawing code so that there are small pixel-size
606  * gaps when the resulting path is drawn by StrokePath()
607  */
608 BOOL PATH_RoundRect(DC *dc, INT x1, INT y1, INT x2, INT y2, INT ell_width, INT ell_height)
609 {
610    GdiPath *pPath = &dc->path;
611    POINT corners[2], pointTemp;
612    FLOAT_POINT ellCorners[2];
613 
614    /* Check that path is open */
615    if(pPath->state!=PATH_Open)
616       return FALSE;
617 
618    if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
619       return FALSE;
620 
621    /* Add points to the roundrect path */
622    ellCorners[0].x = corners[1].x-ell_width;
623    ellCorners[0].y = corners[0].y;
624    ellCorners[1].x = corners[1].x;
625    ellCorners[1].y = corners[0].y+ell_height;
626    if(!PATH_DoArcPart(pPath, ellCorners, 0, -M_PI_2, PT_MOVETO))
627       return FALSE;
628    pointTemp.x = corners[0].x+ell_width/2;
629    pointTemp.y = corners[0].y;
630    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
631       return FALSE;
632    ellCorners[0].x = corners[0].x;
633    ellCorners[1].x = corners[0].x+ell_width;
634    if(!PATH_DoArcPart(pPath, ellCorners, -M_PI_2, -M_PI, FALSE))
635       return FALSE;
636    pointTemp.x = corners[0].x;
637    pointTemp.y = corners[1].y-ell_height/2;
638    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
639       return FALSE;
640    ellCorners[0].y = corners[1].y-ell_height;
641    ellCorners[1].y = corners[1].y;
642    if(!PATH_DoArcPart(pPath, ellCorners, M_PI, M_PI_2, FALSE))
643       return FALSE;
644    pointTemp.x = corners[1].x-ell_width/2;
645    pointTemp.y = corners[1].y;
646    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
647       return FALSE;
648    ellCorners[0].x = corners[1].x-ell_width;
649    ellCorners[1].x = corners[1].x;
650    if(!PATH_DoArcPart(pPath, ellCorners, M_PI_2, 0, FALSE))
651       return FALSE;
652 
653    /* Close the roundrect figure */
654    if(!CloseFigure(dc->hSelf))
655       return FALSE;
656 
657    return TRUE;
658 }
659 
660 /* PATH_Rectangle
661  *
662  * Should be called when a call to Rectangle is performed on a DC that has
663  * an open path. Returns TRUE if successful, else FALSE.
664  */
665 BOOL PATH_Rectangle(DC *dc, INT x1, INT y1, INT x2, INT y2)
666 {
667    GdiPath *pPath = &dc->path;
668    POINT corners[2], pointTemp;
669 
670    /* Check that path is open */
671    if(pPath->state!=PATH_Open)
672       return FALSE;
673 
674    if(!PATH_CheckCorners(dc,corners,x1,y1,x2,y2))
675       return FALSE;
676 
677    /* Close any previous figure */
678    if(!CloseFigure(dc->hSelf))
679    {
680       /* The CloseFigure call shouldn't have failed */
681       assert(FALSE);
682       return FALSE;
683    }
684 
685    /* Add four points to the path */
686    pointTemp.x=corners[1].x;
687    pointTemp.y=corners[0].y;
688    if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
689       return FALSE;
690    if(!PATH_AddEntry(pPath, corners, PT_LINETO))
691       return FALSE;
692    pointTemp.x=corners[0].x;
693    pointTemp.y=corners[1].y;
694    if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
695       return FALSE;
696    if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
697       return FALSE;
698 
699    /* Close the rectangle figure */
700    if(!CloseFigure(dc->hSelf))
701    {
702       /* The CloseFigure call shouldn't have failed */
703       assert(FALSE);
704       return FALSE;
705    }
706 
707    return TRUE;
708 }
709 
710 /* PATH_Ellipse
711  *
712  * Should be called when a call to Ellipse is performed on a DC that has
713  * an open path. This adds four Bezier splines representing the ellipse
714  * to the path. Returns TRUE if successful, else FALSE.
715  */
716 BOOL PATH_Ellipse(DC *dc, INT x1, INT y1, INT x2, INT y2)
717 {
718    return( PATH_Arc(dc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2,0) &&
719            CloseFigure(dc->hSelf) );
720 }
721 
722 /* PATH_Arc
723  *
724  * Should be called when a call to Arc is performed on a DC that has
725  * an open path. This adds up to five Bezier splines representing the arc
726  * to the path. When 'lines' is 1, we add 1 extra line to get a chord,
727  * when 'lines' is 2, we add 2 extra lines to get a pie, and when 'lines' is
728  * -1 we add 1 extra line from the current DC position to the starting position
729  * of the arc before drawing the arc itself (arcto). Returns TRUE if successful,
730  * else FALSE.
731  */
732 BOOL PATH_Arc(DC *dc, INT x1, INT y1, INT x2, INT y2,
733    INT xStart, INT yStart, INT xEnd, INT yEnd, INT lines)
734 {
735    GdiPath     *pPath = &dc->path;
736    double      angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
737                /* Initialize angleEndQuadrant to silence gcc's warning */
738    double      x, y;
739    FLOAT_POINT corners[2], pointStart, pointEnd;
740    POINT       centre, pointCurPos;
741    BOOL      start, end;
742    INT       temp;
743 
744    /* FIXME: This function should check for all possible error returns */
745    /* FIXME: Do we have to respect newStroke? */
746 
747    /* Check that path is open */
748    if(pPath->state!=PATH_Open)
749       return FALSE;
750 
751    /* Check for zero height / width */
752    /* FIXME: Only in GM_COMPATIBLE? */
753    if(x1==x2 || y1==y2)
754       return TRUE;
755 
756    /* Convert points to device coordinates */
757    corners[0].x=(FLOAT)x1;
758    corners[0].y=(FLOAT)y1;
759    corners[1].x=(FLOAT)x2;
760    corners[1].y=(FLOAT)y2;
761    pointStart.x=(FLOAT)xStart;
762    pointStart.y=(FLOAT)yStart;
763    pointEnd.x=(FLOAT)xEnd;
764    pointEnd.y=(FLOAT)yEnd;
765    INTERNAL_LPTODP_FLOAT(dc, corners);
766    INTERNAL_LPTODP_FLOAT(dc, corners+1);
767    INTERNAL_LPTODP_FLOAT(dc, &pointStart);
768    INTERNAL_LPTODP_FLOAT(dc, &pointEnd);
769 
770    /* Make sure first corner is top left and second corner is bottom right */
771    if(corners[0].x>corners[1].x)
772    {
773       temp=corners[0].x;
774       corners[0].x=corners[1].x;
775       corners[1].x=temp;
776    }
777    if(corners[0].y>corners[1].y)
778    {
779       temp=corners[0].y;
780       corners[0].y=corners[1].y;
781       corners[1].y=temp;
782    }
783 
784    /* Compute start and end angle */
785    PATH_NormalizePoint(corners, &pointStart, &x, &y);
786    angleStart=atan2(y, x);
787    PATH_NormalizePoint(corners, &pointEnd, &x, &y);
788    angleEnd=atan2(y, x);
789 
790    /* Make sure the end angle is "on the right side" of the start angle */
791    if(dc->ArcDirection==AD_CLOCKWISE)
792    {
793       if(angleEnd<=angleStart)
794       {
795          angleEnd+=2*M_PI;
796          assert(angleEnd>=angleStart);
797       }
798    }
799    else
800    {
801       if(angleEnd>=angleStart)
802       {
803          angleEnd-=2*M_PI;
804          assert(angleEnd<=angleStart);
805       }
806    }
807 
808    /* In GM_COMPATIBLE, don't include bottom and right edges */
809    if(dc->GraphicsMode==GM_COMPATIBLE)
810    {
811       corners[1].x--;
812       corners[1].y--;
813    }
814 
815    /* arcto: Add a PT_MOVETO only if this is the first entry in a stroke */
816    if(lines==-1 && pPath->newStroke)
817    {
818       pPath->newStroke=FALSE;
819       pointCurPos.x = dc->CursPosX;
820       pointCurPos.y = dc->CursPosY;
821       if(!LPtoDP(dc->hSelf, &pointCurPos, 1))
822          return FALSE;
823       if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
824          return FALSE;
825    }
826 
827    /* Add the arc to the path with one Bezier spline per quadrant that the
828     * arc spans */
829    start=TRUE;
830    end=FALSE;
831    do
832    {
833       /* Determine the start and end angles for this quadrant */
834       if(start)
835       {
836          angleStartQuadrant=angleStart;
837          if(dc->ArcDirection==AD_CLOCKWISE)
838             angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
839          else
840             angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
841       }
842       else
843       {
844          angleStartQuadrant=angleEndQuadrant;
845          if(dc->ArcDirection==AD_CLOCKWISE)
846             angleEndQuadrant+=M_PI_2;
847          else
848             angleEndQuadrant-=M_PI_2;
849       }
850 
851       /* Have we reached the last part of the arc? */
852       if((dc->ArcDirection==AD_CLOCKWISE &&
853          angleEnd<angleEndQuadrant) ||
854          (dc->ArcDirection==AD_COUNTERCLOCKWISE &&
855          angleEnd>angleEndQuadrant))
856       {
857          /* Adjust the end angle for this quadrant */
858          angleEndQuadrant=angleEnd;
859          end=TRUE;
860       }
861 
862       /* Add the Bezier spline to the path */
863       PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant,
864          start ? (lines==-1 ? PT_LINETO : PT_MOVETO) : FALSE);
865       start=FALSE;
866    }  while(!end);
867 
868    /* chord: close figure. pie: add line and close figure */
869    if(lines==1)
870    {
871       if(!CloseFigure(dc->hSelf))
872          return FALSE;
873    }
874    else if(lines==2)
875    {
876       centre.x = (corners[0].x+corners[1].x)/2;
877       centre.y = (corners[0].y+corners[1].y)/2;
878       if(!PATH_AddEntry(pPath, &centre, PT_LINETO | PT_CLOSEFIGURE))
879          return FALSE;
880    }
881 
882    return TRUE;
883 }
884 
885 BOOL PATH_PolyBezierTo(DC *dc, const POINT *pts, DWORD cbPoints)
886 {
887    GdiPath     *pPath = &dc->path;
888    POINT       pt;
889    UINT        i;
890 
891    /* Check that path is open */
892    if(pPath->state!=PATH_Open)
893       return FALSE;
894 
895    /* Add a PT_MOVETO if necessary */
896    if(pPath->newStroke)
897    {
898       pPath->newStroke=FALSE;
899       pt.x = dc->CursPosX;
900       pt.y = dc->CursPosY;
901       if(!LPtoDP(dc->hSelf, &pt, 1))
902          return FALSE;
903       if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
904          return FALSE;
905    }
906 
907    for(i = 0; i < cbPoints; i++) {
908        pt = pts[i];
909        if(!LPtoDP(dc->hSelf, &pt, 1))
910            return FALSE;
911        PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
912    }
913    return TRUE;
914 }
915 
916 BOOL PATH_PolyBezier(DC *dc, const POINT *pts, DWORD cbPoints)
917 {
918    GdiPath     *pPath = &dc->path;
919    POINT       pt;
920    UINT        i;
921 
922    /* Check that path is open */
923    if(pPath->state!=PATH_Open)
924       return FALSE;
925 
926    for(i = 0; i < cbPoints; i++) {
927        pt = pts[i];
928        if(!LPtoDP(dc->hSelf, &pt, 1))
929            return FALSE;
930        PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO);
931    }
932    return TRUE;
933 }
934 
935 /* PATH_PolyDraw
936  *
937  * Should be called when a call to PolyDraw is performed on a DC that has
938  * an open path. Returns TRUE if successful, else FALSE.
939  */
940 BOOL PATH_PolyDraw(DC *dc, const POINT *pts, const BYTE *types,
941     DWORD cbPoints)
942 {
943         GdiPath     *pPath = &dc->path;
944         POINT       lastmove, orig_pos;
945         INT         i;
946 
947         lastmove.x = orig_pos.x = dc->CursPosX;
948         lastmove.y = orig_pos.y = dc->CursPosY;
949 
950         for(i = pPath->numEntriesUsed - 1; i >= 0; i--){
951             if(pPath->pFlags[i] == PT_MOVETO){
952                 lastmove.x = pPath->pPoints[i].x;
953                 lastmove.y = pPath->pPoints[i].y;
954                 if(!DPtoLP(dc->hSelf, &lastmove, 1))
955                     return FALSE;
956                 break;
957             }
958         }
959 
960         for(i = 0; i < cbPoints; i++){
961             if(types[i] == PT_MOVETO){
962                 pPath->newStroke = TRUE;
963                 lastmove.x = pts[i].x;
964                 lastmove.y = pts[i].y;
965             }
966             else if((types[i] & ~PT_CLOSEFIGURE) == PT_LINETO){
967                 PATH_LineTo(dc, pts[i].x, pts[i].y);
968             }
969             else if(types[i] == PT_BEZIERTO){
970                 if(!((i + 2 < cbPoints) && (types[i + 1] == PT_BEZIERTO)
971                     && ((types[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)))
972                     goto err;
973                 PATH_PolyBezierTo(dc, &(pts[i]), 3);
974                 i += 2;
975             }
976             else
977                 goto err;
978 
979             dc->CursPosX = pts[i].x;
980             dc->CursPosY = pts[i].y;
981 
982             if(types[i] & PT_CLOSEFIGURE){
983                 pPath->pFlags[pPath->numEntriesUsed-1] |= PT_CLOSEFIGURE;
984                 pPath->newStroke = TRUE;
985                 dc->CursPosX = lastmove.x;
986                 dc->CursPosY = lastmove.y;
987             }
988         }
989 
990         return TRUE;
991 
992 err:
993         if((dc->CursPosX != orig_pos.x) || (dc->CursPosY != orig_pos.y)){
994             pPath->newStroke = TRUE;
995             dc->CursPosX = orig_pos.x;
996             dc->CursPosY = orig_pos.y;
997         }
998 
999         return FALSE;
1000 }
1001 
1002 BOOL PATH_Polyline(DC *dc, const POINT *pts, DWORD cbPoints)
1003 {
1004    GdiPath     *pPath = &dc->path;
1005    POINT       pt;
1006    UINT        i;
1007 
1008    /* Check that path is open */
1009    if(pPath->state!=PATH_Open)
1010       return FALSE;
1011 
1012    for(i = 0; i < cbPoints; i++) {
1013        pt = pts[i];
1014        if(!LPtoDP(dc->hSelf, &pt, 1))
1015            return FALSE;
1016        PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
1017    }
1018    return TRUE;
1019 }
1020 
1021 BOOL PATH_PolylineTo(DC *dc, const POINT *pts, DWORD cbPoints)
1022 {
1023    GdiPath     *pPath = &dc->path;
1024    POINT       pt;
1025    UINT        i;
1026 
1027    /* Check that path is open */
1028    if(pPath->state!=PATH_Open)
1029       return FALSE;
1030 
1031    /* Add a PT_MOVETO if necessary */
1032    if(pPath->newStroke)
1033    {
1034       pPath->newStroke=FALSE;
1035       pt.x = dc->CursPosX;
1036       pt.y = dc->CursPosY;
1037       if(!LPtoDP(dc->hSelf, &pt, 1))
1038          return FALSE;
1039       if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
1040          return FALSE;
1041    }
1042 
1043    for(i = 0; i < cbPoints; i++) {
1044        pt = pts[i];
1045        if(!LPtoDP(dc->hSelf, &pt, 1))
1046            return FALSE;
1047        PATH_AddEntry(pPath, &pt, PT_LINETO