Use a per-thread buffer and write(2) for debug traces.
[wine.git] / graphics / path.c
blobd875f476cc0dc25272513ad21ff2782ede92c8cf
1 /*
2 * Graphics paths (BeginPath, EndPath etc.)
4 * Copyright 1997, 1998 Martin Boehme
5 * 1999 Huw D M Davies
6 */
8 #include <assert.h>
9 #include <math.h>
10 #include <string.h>
11 #include "config.h"
12 #if defined(HAVE_FLOAT_H)
13 #include <float.h>
14 #endif
16 #include "winbase.h"
17 #include "wingdi.h"
18 #include "winerror.h"
20 #include "dc.h"
21 #include "debugtools.h"
22 #include "path.h"
24 DEFAULT_DEBUG_CHANNEL(gdi)
26 /* Notes on the implementation
28 * The implementation is based on dynamically resizable arrays of points and
29 * flags. I dithered for a bit before deciding on this implementation, and
30 * I had even done a bit of work on a linked list version before switching
31 * to arrays. It's a bit of a tradeoff. When you use linked lists, the
32 * implementation of FlattenPath is easier, because you can rip the
33 * PT_BEZIERTO entries out of the middle of the list and link the
34 * corresponding PT_LINETO entries in. However, when you use arrays,
35 * PathToRegion becomes easier, since you can essentially just pass your array
36 * of points to CreatePolyPolygonRgn. Also, if I'd used linked lists, I would
37 * have had the extra effort of creating a chunk-based allocation scheme
38 * in order to use memory effectively. That's why I finally decided to use
39 * arrays. Note by the way that the array based implementation has the same
40 * linear time complexity that linked lists would have since the arrays grow
41 * exponentially.
43 * The points are stored in the path in device coordinates. This is
44 * consistent with the way Windows does things (for instance, see the Win32
45 * SDK documentation for GetPath).
47 * The word "stroke" appears in several places (e.g. in the flag
48 * GdiPath.newStroke). A stroke consists of a PT_MOVETO followed by one or
49 * more PT_LINETOs or PT_BEZIERTOs, up to, but not including, the next
50 * PT_MOVETO. Note that this is not the same as the definition of a figure;
51 * a figure can contain several strokes.
53 * I modified the drawing functions (MoveTo, LineTo etc.) to test whether
54 * the path is open and to call the corresponding function in path.c if this
55 * is the case. A more elegant approach would be to modify the function
56 * pointers in the DC_FUNCTIONS structure; however, this would be a lot more
57 * complex. Also, the performance degradation caused by my approach in the
58 * case where no path is open is so small that it cannot be measured.
60 * Martin Boehme
63 /* FIXME: A lot of stuff isn't implemented yet. There is much more to come. */
65 #define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
66 #define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
67 #define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
70 static BOOL PATH_PathToRegion(const GdiPath *pPath, INT nPolyFillMode,
71 HRGN *pHrgn);
72 static void PATH_EmptyPath(GdiPath *pPath);
73 static BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint,
74 BYTE flags);
75 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries);
76 static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath);
77 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
78 double angleStart, double angleEnd, BOOL addMoveTo);
79 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
80 double y, POINT *pPoint);
81 static void PATH_NormalizePoint(FLOAT_POINT corners[], const FLOAT_POINT
82 *pPoint, double *pX, double *pY);
85 /***********************************************************************
86 * BeginPath16 (GDI.512)
88 BOOL16 WINAPI BeginPath16(HDC16 hdc)
90 return (BOOL16)BeginPath((HDC)hdc);
94 /***********************************************************************
95 * BeginPath (GDI32.9)
97 BOOL WINAPI BeginPath(HDC hdc)
99 DC *dc = DC_GetDCPtr( hdc );
100 GdiPath *pPath;
102 if(!dc) {
103 SetLastError(ERROR_INVALID_HANDLE);
104 return FALSE;
107 if(dc->funcs->pBeginPath)
108 return dc->funcs->pBeginPath(dc);
110 pPath = &dc->w.path;
112 /* If path is already open, do nothing */
113 if(pPath->state==PATH_Open)
114 return TRUE;
116 /* Make sure that path is empty */
117 PATH_EmptyPath(pPath);
119 /* Initialize variables for new path */
120 pPath->newStroke=TRUE;
121 pPath->state=PATH_Open;
123 return TRUE;
127 /***********************************************************************
128 * EndPath16 (GDI.514)
130 BOOL16 WINAPI EndPath16(HDC16 hdc)
132 return (BOOL16)EndPath((HDC)hdc);
136 /***********************************************************************
137 * EndPath (GDI32.78)
139 BOOL WINAPI EndPath(HDC hdc)
141 DC *dc = DC_GetDCPtr( hdc );
142 GdiPath *pPath;
144 if(!dc) {
145 SetLastError(ERROR_INVALID_HANDLE);
146 return FALSE;
149 if(dc->funcs->pEndPath)
150 return dc->funcs->pEndPath(dc);
152 pPath = &dc->w.path;
154 /* Check that path is currently being constructed */
155 if(pPath->state!=PATH_Open)
157 SetLastError(ERROR_CAN_NOT_COMPLETE);
158 return FALSE;
161 /* Set flag to indicate that path is finished */
162 pPath->state=PATH_Closed;
164 return TRUE;
168 /***********************************************************************
169 * AbortPath16 (GDI.511)
171 BOOL16 WINAPI AbortPath16(HDC16 hdc)
173 return (BOOL16)AbortPath((HDC)hdc);
177 /******************************************************************************
178 * AbortPath [GDI32.1]
179 * Closes and discards paths from device context
181 * NOTES
182 * Check that SetLastError is being called correctly
184 * PARAMS
185 * hdc [I] Handle to device context
187 * RETURNS STD
189 BOOL WINAPI AbortPath( HDC hdc )
191 DC *dc = DC_GetDCPtr( hdc );
192 GdiPath *pPath;
194 if(!dc) {
195 SetLastError(ERROR_INVALID_HANDLE);
196 return FALSE;
199 if(dc->funcs->pAbortPath)
200 return dc->funcs->pAbortPath(dc);
202 pPath = &dc->w.path;
204 /* Remove all entries from the path */
205 PATH_EmptyPath(pPath);
207 return TRUE;
211 /***********************************************************************
212 * CloseFigure16 (GDI.513)
214 BOOL16 WINAPI CloseFigure16(HDC16 hdc)
216 return (BOOL16)CloseFigure((HDC)hdc);
220 /***********************************************************************
221 * CloseFigure (GDI32.16)
223 * FIXME: Check that SetLastError is being called correctly
225 BOOL WINAPI CloseFigure(HDC hdc)
227 DC *dc = DC_GetDCPtr( hdc );
228 GdiPath *pPath;
230 if(!dc) {
231 SetLastError(ERROR_INVALID_HANDLE);
232 return FALSE;
235 if(dc->funcs->pCloseFigure)
236 return dc->funcs->pCloseFigure(dc);
238 pPath = &dc->w.path;
240 /* Check that path is open */
241 if(pPath->state!=PATH_Open)
243 SetLastError(ERROR_CAN_NOT_COMPLETE);
244 return FALSE;
247 /* FIXME: Shouldn't we draw a line to the beginning of the figure? */
249 /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
250 if(pPath->numEntriesUsed)
252 pPath->pFlags[pPath->numEntriesUsed-1]|=PT_CLOSEFIGURE;
253 pPath->newStroke=TRUE;
256 return TRUE;
260 /***********************************************************************
261 * GetPath16 (GDI.517)
263 INT16 WINAPI GetPath16(HDC16 hdc, LPPOINT16 pPoints, LPBYTE pTypes,
264 INT16 nSize)
266 FIXME("(%d,%p,%p): stub\n",hdc,pPoints,pTypes);
268 return 0;
272 /***********************************************************************
273 * GetPath (GDI32.210)
275 INT WINAPI GetPath(HDC hdc, LPPOINT pPoints, LPBYTE pTypes,
276 INT nSize)
278 GdiPath *pPath;
280 /* Get pointer to path */
281 if(!PATH_GetPathFromHDC(hdc, &pPath))
283 SetLastError(ERROR_INVALID_PARAMETER);
284 return -1;
287 /* Check that path is closed */
288 if(pPath->state!=PATH_Closed)
290 SetLastError(ERROR_CAN_NOT_COMPLETE);
291 return -1;
294 if(nSize==0)
295 return pPath->numEntriesUsed;
296 else if(nSize<pPath->numEntriesUsed)
298 SetLastError(ERROR_INVALID_PARAMETER);
299 return -1;
301 else
303 memcpy(pPoints, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
304 memcpy(pTypes, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
306 /* Convert the points to logical coordinates */
307 if(!DPtoLP(hdc, pPoints, pPath->numEntriesUsed))
309 /* FIXME: Is this the correct value? */
310 SetLastError(ERROR_CAN_NOT_COMPLETE);
311 return -1;
314 return pPath->numEntriesUsed;
318 /***********************************************************************
319 * PathToRegion16 (GDI.518)
321 HRGN16 WINAPI PathToRegion16(HDC16 hdc)
323 return (HRGN16) PathToRegion((HDC) hdc);
326 /***********************************************************************
327 * PathToRegion (GDI32.261)
329 * FIXME
330 * Check that SetLastError is being called correctly
332 * The documentation does not state this explicitly, but a test under Windows
333 * shows that the region which is returned should be in device coordinates.
335 HRGN WINAPI PathToRegion(HDC hdc)
337 GdiPath *pPath;
338 HRGN hrgnRval;
340 /* Get pointer to path */
341 if(!PATH_GetPathFromHDC(hdc, &pPath))
343 SetLastError(ERROR_INVALID_PARAMETER);
344 return 0;
347 /* Check that path is closed */
348 if(pPath->state!=PATH_Closed)
350 SetLastError(ERROR_CAN_NOT_COMPLETE);
351 return 0;
354 /* FIXME: Should we empty the path even if conversion failed? */
355 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnRval))
356 PATH_EmptyPath(pPath);
357 else
358 hrgnRval=0;
360 return hrgnRval;
363 /***********************************************************************
364 * FillPath16 (GDI.515)
366 BOOL16 WINAPI FillPath16(HDC16 hdc)
368 return (BOOL16) FillPath((HDC) hdc);
371 /***********************************************************************
372 * FillPath (GDI32.100)
374 * FIXME
375 * Check that SetLastError is being called correctly
377 BOOL WINAPI FillPath(HDC hdc)
379 GdiPath *pPath;
380 INT mapMode, graphicsMode;
381 SIZE ptViewportExt, ptWindowExt;
382 POINT ptViewportOrg, ptWindowOrg;
383 XFORM xform;
384 HRGN hrgn;
385 DC *dc = DC_GetDCPtr( hdc );
387 if(!dc) {
388 SetLastError(ERROR_INVALID_HANDLE);
389 return FALSE;
392 if(dc->funcs->pFillPath)
393 return dc->funcs->pFillPath(dc);
395 pPath = &dc->w.path;
397 /* Check that path is closed */
398 if(pPath->state!=PATH_Closed)
400 SetLastError(ERROR_CAN_NOT_COMPLETE);
401 return FALSE;
404 /* Construct a region from the path and fill it */
405 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgn))
407 /* Since PaintRgn interprets the region as being in logical coordinates
408 * but the points we store for the path are already in device
409 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
410 * Using SaveDC to save information about the mapping mode / world
411 * transform would be easier but would require more overhead, especially
412 * now that SaveDC saves the current path.
415 /* Save the information about the old mapping mode */
416 mapMode=GetMapMode(hdc);
417 GetViewportExtEx(hdc, &ptViewportExt);
418 GetViewportOrgEx(hdc, &ptViewportOrg);
419 GetWindowExtEx(hdc, &ptWindowExt);
420 GetWindowOrgEx(hdc, &ptWindowOrg);
422 /* Save world transform
423 * NB: The Windows documentation on world transforms would lead one to
424 * believe that this has to be done only in GM_ADVANCED; however, my
425 * tests show that resetting the graphics mode to GM_COMPATIBLE does
426 * not reset the world transform.
428 GetWorldTransform(hdc, &xform);
430 /* Set MM_TEXT */
431 SetMapMode(hdc, MM_TEXT);
432 SetViewportOrgEx(hdc, 0, 0, NULL);
433 SetWindowOrgEx(hdc, 0, 0, NULL);
435 /* Paint the region */
436 PaintRgn(hdc, hrgn);
438 /* Restore the old mapping mode */
439 SetMapMode(hdc, mapMode);
440 SetViewportExtEx(hdc, ptViewportExt.cx, ptViewportExt.cy, NULL);
441 SetViewportOrgEx(hdc, ptViewportOrg.x, ptViewportOrg.y, NULL);
442 SetWindowExtEx(hdc, ptWindowExt.cx, ptWindowExt.cy, NULL);
443 SetWindowOrgEx(hdc, ptWindowOrg.x, ptWindowOrg.y, NULL);
445 /* Go to GM_ADVANCED temporarily to restore the world transform */
446 graphicsMode=GetGraphicsMode(hdc);
447 SetGraphicsMode(hdc, GM_ADVANCED);
448 SetWorldTransform(hdc, &xform);
449 SetGraphicsMode(hdc, graphicsMode);
451 /* Empty the path */
452 PATH_EmptyPath(pPath);
453 return TRUE;
455 else
457 /* FIXME: Should the path be emptied even if conversion failed? */
458 /* PATH_EmptyPath(pPath); */
459 return FALSE;
463 /***********************************************************************
464 * SelectClipPath16 (GDI.519)
466 BOOL16 WINAPI SelectClipPath16(HDC16 hdc, INT16 iMode)
468 return (BOOL16) SelectClipPath((HDC) hdc, iMode);
471 /***********************************************************************
472 * SelectClipPath (GDI32.296)
473 * FIXME
474 * Check that SetLastError is being called correctly
476 BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
478 GdiPath *pPath;
479 HRGN hrgnPath;
480 BOOL success;
481 DC *dc = DC_GetDCPtr( hdc );
483 if(!dc) {
484 SetLastError(ERROR_INVALID_HANDLE);
485 return FALSE;
488 if(dc->funcs->pSelectClipPath)
489 return dc->funcs->pSelectClipPath(dc, iMode);
491 pPath = &dc->w.path;
493 /* Check that path is closed */
494 if(pPath->state!=PATH_Closed)
496 SetLastError(ERROR_CAN_NOT_COMPLETE);
497 return FALSE;
500 /* Construct a region from the path */
501 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnPath))
503 success = ExtSelectClipRgn( hdc, hrgnPath, iMode ) != ERROR;
504 DeleteObject(hrgnPath);
506 /* Empty the path */
507 if(success)
508 PATH_EmptyPath(pPath);
509 /* FIXME: Should this function delete the path even if it failed? */
511 return success;
513 else
514 return FALSE;
518 /***********************************************************************
519 * Exported functions
522 /* PATH_InitGdiPath
524 * Initializes the GdiPath structure.
526 void PATH_InitGdiPath(GdiPath *pPath)
528 assert(pPath!=NULL);
530 pPath->state=PATH_Null;
531 pPath->pPoints=NULL;
532 pPath->pFlags=NULL;
533 pPath->numEntriesUsed=0;
534 pPath->numEntriesAllocated=0;
537 /* PATH_DestroyGdiPath
539 * Destroys a GdiPath structure (frees the memory in the arrays).
541 void PATH_DestroyGdiPath(GdiPath *pPath)
543 assert(pPath!=NULL);
545 HeapFree( GetProcessHeap(), 0, pPath->pPoints );
546 HeapFree( GetProcessHeap(), 0, pPath->pFlags );
549 /* PATH_AssignGdiPath
551 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
552 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
553 * not just the pointers. Since this means that the arrays in pPathDest may
554 * need to be resized, pPathDest should have been initialized using
555 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
556 * not a copy constructor).
557 * Returns TRUE if successful, else FALSE.
559 BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
561 assert(pPathDest!=NULL && pPathSrc!=NULL);
563 /* Make sure destination arrays are big enough */
564 if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
565 return FALSE;
567 /* Perform the copy operation */
568 memcpy(pPathDest->pPoints, pPathSrc->pPoints,
569 sizeof(POINT)*pPathSrc->numEntriesUsed);
570 memcpy(pPathDest->pFlags, pPathSrc->pFlags,
571 sizeof(BYTE)*pPathSrc->numEntriesUsed);
573 pPathDest->state=pPathSrc->state;
574 pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
575 pPathDest->newStroke=pPathSrc->newStroke;
577 return TRUE;
580 /* PATH_MoveTo
582 * Should be called when a MoveTo is performed on a DC that has an
583 * open path. This starts a new stroke. Returns TRUE if successful, else
584 * FALSE.
586 BOOL PATH_MoveTo(HDC hdc)
588 GdiPath *pPath;
590 /* Get pointer to path */
591 if(!PATH_GetPathFromHDC(hdc, &pPath))
592 return FALSE;
594 /* Check that path is open */
595 if(pPath->state!=PATH_Open)
596 /* FIXME: Do we have to call SetLastError? */
597 return FALSE;
599 /* Start a new stroke */
600 pPath->newStroke=TRUE;
602 return TRUE;
605 /* PATH_LineTo
607 * Should be called when a LineTo is performed on a DC that has an
608 * open path. This adds a PT_LINETO entry to the path (and possibly
609 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
610 * Returns TRUE if successful, else FALSE.
612 BOOL PATH_LineTo(HDC hdc, INT x, INT y)
614 GdiPath *pPath;
615 POINT point, pointCurPos;
617 /* Get pointer to path */
618 if(!PATH_GetPathFromHDC(hdc, &pPath))
619 return FALSE;
621 /* Check that path is open */
622 if(pPath->state!=PATH_Open)
623 return FALSE;
625 /* Convert point to device coordinates */
626 point.x=x;
627 point.y=y;
628 if(!LPtoDP(hdc, &point, 1))
629 return FALSE;
631 /* Add a PT_MOVETO if necessary */
632 if(pPath->newStroke)
634 pPath->newStroke=FALSE;
635 if(!GetCurrentPositionEx(hdc, &pointCurPos) ||
636 !LPtoDP(hdc, &pointCurPos, 1))
637 return FALSE;
638 if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
639 return FALSE;
642 /* Add a PT_LINETO entry */
643 return PATH_AddEntry(pPath, &point, PT_LINETO);
646 /* PATH_Rectangle
648 * Should be called when a call to Rectangle is performed on a DC that has
649 * an open path. Returns TRUE if successful, else FALSE.
651 BOOL PATH_Rectangle(HDC hdc, INT x1, INT y1, INT x2, INT y2)
653 GdiPath *pPath;
654 POINT corners[2], pointTemp;
655 INT temp;
657 /* Get pointer to path */
658 if(!PATH_GetPathFromHDC(hdc, &pPath))
659 return FALSE;
661 /* Check that path is open */
662 if(pPath->state!=PATH_Open)
663 return FALSE;
665 /* Convert points to device coordinates */
666 corners[0].x=x1;
667 corners[0].y=y1;
668 corners[1].x=x2;
669 corners[1].y=y2;
670 if(!LPtoDP(hdc, corners, 2))
671 return FALSE;
673 /* Make sure first corner is top left and second corner is bottom right */
674 if(corners[0].x>corners[1].x)
676 temp=corners[0].x;
677 corners[0].x=corners[1].x;
678 corners[1].x=temp;
680 if(corners[0].y>corners[1].y)
682 temp=corners[0].y;
683 corners[0].y=corners[1].y;
684 corners[1].y=temp;
687 /* In GM_COMPATIBLE, don't include bottom and right edges */
688 if(GetGraphicsMode(hdc)==GM_COMPATIBLE)
690 corners[1].x--;
691 corners[1].y--;
694 /* Close any previous figure */
695 if(!CloseFigure(hdc))
697 /* The CloseFigure call shouldn't have failed */
698 assert(FALSE);
699 return FALSE;
702 /* Add four points to the path */
703 pointTemp.x=corners[1].x;
704 pointTemp.y=corners[0].y;
705 if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
706 return FALSE;
707 if(!PATH_AddEntry(pPath, corners, PT_LINETO))
708 return FALSE;
709 pointTemp.x=corners[0].x;
710 pointTemp.y=corners[1].y;
711 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
712 return FALSE;
713 if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
714 return FALSE;
716 /* Close the rectangle figure */
717 if(!CloseFigure(hdc))
719 /* The CloseFigure call shouldn't have failed */
720 assert(FALSE);
721 return FALSE;
724 return TRUE;
727 /* PATH_Ellipse
729 * Should be called when a call to Ellipse is performed on a DC that has
730 * an open path. This adds four Bezier splines representing the ellipse
731 * to the path. Returns TRUE if successful, else FALSE.
733 BOOL PATH_Ellipse(HDC hdc, INT x1, INT y1, INT x2, INT y2)
735 /* TODO: This should probably be revised to call PATH_AngleArc */
736 /* (once it exists) */
737 return PATH_Arc(hdc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2);
740 /* PATH_Arc
742 * Should be called when a call to Arc is performed on a DC that has
743 * an open path. This adds up to five Bezier splines representing the arc
744 * to the path. Returns TRUE if successful, else FALSE.
746 BOOL PATH_Arc(HDC hdc, INT x1, INT y1, INT x2, INT y2,
747 INT xStart, INT yStart, INT xEnd, INT yEnd)
749 GdiPath *pPath;
750 DC *pDC;
751 double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
752 /* Initialize angleEndQuadrant to silence gcc's warning */
753 double x, y;
754 FLOAT_POINT corners[2], pointStart, pointEnd;
755 BOOL start, end;
756 INT temp;
758 /* FIXME: This function should check for all possible error returns */
759 /* FIXME: Do we have to respect newStroke? */
761 /* Get pointer to DC */
762 pDC=DC_GetDCPtr(hdc);
763 if(pDC==NULL)
764 return FALSE;
766 /* Get pointer to path */
767 if(!PATH_GetPathFromHDC(hdc, &pPath))
768 return FALSE;
770 /* Check that path is open */
771 if(pPath->state!=PATH_Open)
772 return FALSE;
774 /* FIXME: Do we have to close the current figure? */
776 /* Check for zero height / width */
777 /* FIXME: Only in GM_COMPATIBLE? */
778 if(x1==x2 || y1==y2)
779 return TRUE;
781 /* Convert points to device coordinates */
782 corners[0].x=(FLOAT)x1;
783 corners[0].y=(FLOAT)y1;
784 corners[1].x=(FLOAT)x2;
785 corners[1].y=(FLOAT)y2;
786 pointStart.x=(FLOAT)xStart;
787 pointStart.y=(FLOAT)yStart;
788 pointEnd.x=(FLOAT)xEnd;
789 pointEnd.y=(FLOAT)yEnd;
790 INTERNAL_LPTODP_FLOAT(pDC, corners);
791 INTERNAL_LPTODP_FLOAT(pDC, corners+1);
792 INTERNAL_LPTODP_FLOAT(pDC, &pointStart);
793 INTERNAL_LPTODP_FLOAT(pDC, &pointEnd);
795 /* Make sure first corner is top left and second corner is bottom right */
796 if(corners[0].x>corners[1].x)
798 temp=corners[0].x;
799 corners[0].x=corners[1].x;
800 corners[1].x=temp;
802 if(corners[0].y>corners[1].y)
804 temp=corners[0].y;
805 corners[0].y=corners[1].y;
806 corners[1].y=temp;
809 /* Compute start and end angle */
810 PATH_NormalizePoint(corners, &pointStart, &x, &y);
811 angleStart=atan2(y, x);
812 PATH_NormalizePoint(corners, &pointEnd, &x, &y);
813 angleEnd=atan2(y, x);
815 /* Make sure the end angle is "on the right side" of the start angle */
816 if(GetArcDirection(hdc)==AD_CLOCKWISE)
818 if(angleEnd<=angleStart)
820 angleEnd+=2*M_PI;
821 assert(angleEnd>=angleStart);
824 else
826 if(angleEnd>=angleStart)
828 angleEnd-=2*M_PI;
829 assert(angleEnd<=angleStart);
833 /* In GM_COMPATIBLE, don't include bottom and right edges */
834 if(GetGraphicsMode(hdc)==GM_COMPATIBLE)
836 corners[1].x--;
837 corners[1].y--;
840 /* Add the arc to the path with one Bezier spline per quadrant that the
841 * arc spans */
842 start=TRUE;
843 end=FALSE;
846 /* Determine the start and end angles for this quadrant */
847 if(start)
849 angleStartQuadrant=angleStart;
850 if(GetArcDirection(hdc)==AD_CLOCKWISE)
851 angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
852 else
853 angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
855 else
857 angleStartQuadrant=angleEndQuadrant;
858 if(GetArcDirection(hdc)==AD_CLOCKWISE)
859 angleEndQuadrant+=M_PI_2;
860 else
861 angleEndQuadrant-=M_PI_2;
864 /* Have we reached the last part of the arc? */
865 if((GetArcDirection(hdc)==AD_CLOCKWISE &&
866 angleEnd<angleEndQuadrant) ||
867 (GetArcDirection(hdc)==AD_COUNTERCLOCKWISE &&
868 angleEnd>angleEndQuadrant))
870 /* Adjust the end angle for this quadrant */
871 angleEndQuadrant=angleEnd;
872 end=TRUE;
875 /* Add the Bezier spline to the path */
876 PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant,
877 start);
878 start=FALSE;
879 } while(!end);
881 return TRUE;
884 BOOL PATH_PolyBezierTo(HDC hdc, const POINT *pts, DWORD cbPoints)
886 GdiPath *pPath;
887 POINT pt;
888 INT i;
890 if(!PATH_GetPathFromHDC(hdc, &pPath))
891 return FALSE;
893 /* Check that path is open */
894 if(pPath->state!=PATH_Open)
895 return FALSE;
897 /* Add a PT_MOVETO if necessary */
898 if(pPath->newStroke)
900 pPath->newStroke=FALSE;
901 if(!GetCurrentPositionEx(hdc, &pt) ||
902 !LPtoDP(hdc, &pt, 1))
903 return FALSE;
904 if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
905 return FALSE;
908 for(i = 0; i < cbPoints; i++) {
909 pt = pts[i];
910 if(!LPtoDP(hdc, &pt, 1))
911 return FALSE;
912 PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
914 return TRUE;
917 BOOL PATH_PolyBezier(HDC hdc, const POINT *pts, DWORD cbPoints)
919 GdiPath *pPath;
920 POINT pt;
921 INT i;
923 if(!PATH_GetPathFromHDC(hdc, &pPath))
924 return FALSE;
926 /* Check that path is open */
927 if(pPath->state!=PATH_Open)
928 return FALSE;
930 for(i = 0; i < cbPoints; i++) {
931 pt = pts[i];
932 if(!LPtoDP(hdc, &pt, 1))
933 return FALSE;
934 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO);
936 return TRUE;
939 BOOL PATH_Polyline(HDC hdc, const POINT *pts, DWORD cbPoints)
941 GdiPath *pPath;
942 POINT pt;
943 INT i;
945 if(!PATH_GetPathFromHDC(hdc, &pPath))
946 return FALSE;
948 /* Check that path is open */
949 if(pPath->state!=PATH_Open)
950 return FALSE;
952 for(i = 0; i < cbPoints; i++) {
953 pt = pts[i];
954 if(!LPtoDP(hdc, &pt, 1))
955 return FALSE;
956 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
958 return TRUE;
961 BOOL PATH_PolylineTo(HDC hdc, const POINT *pts, DWORD cbPoints)
963 GdiPath *pPath;
964 POINT pt;
965 INT i;
967 if(!PATH_GetPathFromHDC(hdc, &pPath))
968 return FALSE;
970 /* Check that path is open */
971 if(pPath->state!=PATH_Open)
972 return FALSE;
974 /* Add a PT_MOVETO if necessary */
975 if(pPath->newStroke)
977 pPath->newStroke=FALSE;
978 if(!GetCurrentPositionEx(hdc, &pt) ||
979 !LPtoDP(hdc, &pt, 1))
980 return FALSE;
981 if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
982 return FALSE;
985 for(i = 0; i < cbPoints; i++) {
986 pt = pts[i];
987 if(!LPtoDP(hdc, &pt, 1))
988 return FALSE;
989 PATH_AddEntry(pPath, &pt, PT_LINETO);
992 return TRUE;
996 BOOL PATH_Polygon(HDC hdc, const POINT *pts, DWORD cbPoints)
998 GdiPath *pPath;
999 POINT pt;
1000 INT i;
1002 if(!PATH_GetPathFromHDC(hdc, &pPath))
1003 return FALSE;
1005 /* Check that path is open */
1006 if(pPath->state!=PATH_Open)
1007 return FALSE;
1009 for(i = 0; i < cbPoints; i++) {
1010 pt = pts[i];
1011 if(!LPtoDP(hdc, &pt, 1))
1012 return FALSE;
1013 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
1014 ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
1015 PT_LINETO));
1017 return TRUE;
1020 BOOL PATH_PolyPolygon( HDC hdc, const POINT* pts, const INT* counts,
1021 UINT polygons )
1023 GdiPath *pPath;
1024 POINT pt, startpt;
1025 INT poly, point, i;
1027 if(!PATH_GetPathFromHDC(hdc, &pPath))
1028 return FALSE;
1030 /* Check that path is open */
1031 if(pPath->state!=PATH_Open)
1032 return FALSE;
1034 for(i = 0, poly = 0; poly < polygons; poly++) {
1035 for(point = 0; point < counts[poly]; point++, i++) {
1036 pt = pts[i];
1037 if(!LPtoDP(hdc, &pt, 1))
1038 return FALSE;
1039 if(point == 0) startpt = pt;
1040 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1042 /* win98 adds an extra line to close the figure for some reason */
1043 PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
1045 return TRUE;
1048 BOOL PATH_PolyPolyline( HDC hdc, const POINT* pts, const DWORD* counts,
1049 DWORD polylines )
1051 GdiPath *pPath;
1052 POINT pt;
1053 INT poly, point, i;
1055 if(!PATH_GetPathFromHDC(hdc, &pPath))
1056 return FALSE;
1058 /* Check that path is open */
1059 if(pPath->state!=PATH_Open)
1060 return FALSE;
1062 for(i = 0, poly = 0; poly < polylines; poly++) {
1063 for(point = 0; point < counts[poly]; point++, i++) {
1064 pt = pts[i];
1065 if(!LPtoDP(hdc, &pt, 1))
1066 return FALSE;
1067 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1070 return TRUE;
1073 /***********************************************************************
1074 * Internal functions
1078 /* PATH_AddFlatBezier
1081 static BOOL PATH_AddFlatBezier(GdiPath *pPath, POINT *pt, BOOL closed)
1083 POINT *pts;
1084 INT no, i;
1086 pts = GDI_Bezier( pt, 4, &no );
1087 if(!pts) return FALSE;
1089 for(i = 1; i < no; i++)
1090 PATH_AddEntry(pPath, &pts[i],
1091 (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
1092 HeapFree( GetProcessHeap(), 0, pts );
1093 return TRUE;
1096 /* PATH_FlattenPath
1098 * Replaces Beziers with line segments
1101 static BOOL PATH_FlattenPath(GdiPath *pPath)
1103 GdiPath newPath;
1104 INT srcpt;
1106 memset(&newPath, 0, sizeof(newPath));
1107 newPath.state = PATH_Open;
1108 for(srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++) {
1109 switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE) {
1110 case PT_MOVETO:
1111 case PT_LINETO:
1112 PATH_AddEntry(&newPath, &pPath->pPoints[srcpt],
1113 pPath->pFlags[srcpt]);
1114 break;
1115 case PT_BEZIERTO:
1116 PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1],
1117 pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
1118 srcpt += 2;
1119 break;
1122 newPath.state = PATH_Closed;
1123 PATH_AssignGdiPath(pPath, &newPath);
1124 PATH_EmptyPath(&newPath);
1125 return TRUE;
1128 /* PATH_PathToRegion
1130 * Creates a region from the specified path using the specified polygon
1131 * filling mode. The path is left unchanged. A handle to the region that
1132 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
1133 * error occurs, SetLastError is called with the appropriate value and
1134 * FALSE is returned.
1136 static BOOL PATH_PathToRegion(const GdiPath *pPath, INT nPolyFillMode,
1137 HRGN *pHrgn)
1139 int numStrokes, iStroke, i;
1140 INT *pNumPointsInStroke;
1141 HRGN hrgn;
1143 assert(pPath!=NULL);
1144 assert(pHrgn!=NULL);
1146 PATH_FlattenPath(pPath);
1148 /* FIXME: What happens when number of points is zero? */
1150 /* First pass: Find out how many strokes there are in the path */
1151 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
1152 numStrokes=0;
1153 for(i=0; i<pPath->numEntriesUsed; i++)
1154 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1155 numStrokes++;
1157 /* Allocate memory for number-of-points-in-stroke array */
1158 pNumPointsInStroke=(int *)HeapAlloc( GetProcessHeap(), 0,
1159 sizeof(int) * numStrokes );
1160 if(!pNumPointsInStroke)
1162 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1163 return FALSE;
1166 /* Second pass: remember number of points in each polygon */
1167 iStroke=-1; /* Will get incremented to 0 at beginning of first stroke */
1168 for(i=0; i<pPath->numEntriesUsed; i++)
1170 /* Is this the beginning of a new stroke? */
1171 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1173 iStroke++;
1174 pNumPointsInStroke[iStroke]=0;
1177 pNumPointsInStroke[iStroke]++;
1180 /* Create a region from the strokes */
1181 hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
1182 numStrokes, nPolyFillMode);
1183 if(hrgn==(HRGN)0)
1185 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1186 return FALSE;
1189 /* Free memory for number-of-points-in-stroke array */
1190 HeapFree( GetProcessHeap(), 0, pNumPointsInStroke );
1192 /* Success! */
1193 *pHrgn=hrgn;
1194 return TRUE;
1197 /* PATH_EmptyPath
1199 * Removes all entries from the path and sets the path state to PATH_Null.
1201 static void PATH_EmptyPath(GdiPath *pPath)
1203 assert(pPath!=NULL);
1205 pPath->state=PATH_Null;
1206 pPath->numEntriesUsed=0;
1209 /* PATH_AddEntry
1211 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
1212 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
1213 * successful, FALSE otherwise (e.g. if not enough memory was available).
1215 BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint, BYTE flags)
1217 assert(pPath!=NULL);
1219 /* FIXME: If newStroke is true, perhaps we want to check that we're
1220 * getting a PT_MOVETO
1222 TRACE("(%ld,%ld) - %d\n", pPoint->x, pPoint->y, flags);
1224 /* Check that path is open */
1225 if(pPath->state!=PATH_Open)
1226 return FALSE;
1228 /* Reserve enough memory for an extra path entry */
1229 if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
1230 return FALSE;
1232 /* Store information in path entry */
1233 pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
1234 pPath->pFlags[pPath->numEntriesUsed]=flags;
1236 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
1237 if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
1238 pPath->newStroke=TRUE;
1240 /* Increment entry count */
1241 pPath->numEntriesUsed++;
1243 return TRUE;
1246 /* PATH_ReserveEntries
1248 * Ensures that at least "numEntries" entries (for points and flags) have
1249 * been allocated; allocates larger arrays and copies the existing entries
1250 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
1252 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries)
1254 INT numEntriesToAllocate;
1255 POINT *pPointsNew;
1256 BYTE *pFlagsNew;
1258 assert(pPath!=NULL);
1259 assert(numEntries>=0);
1261 /* Do we have to allocate more memory? */
1262 if(numEntries > pPath->numEntriesAllocated)
1264 /* Find number of entries to allocate. We let the size of the array
1265 * grow exponentially, since that will guarantee linear time
1266 * complexity. */
1267 if(pPath->numEntriesAllocated)
1269 numEntriesToAllocate=pPath->numEntriesAllocated;
1270 while(numEntriesToAllocate<numEntries)
1271 numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/
1272 GROW_FACTOR_DENOM;
1274 else
1275 numEntriesToAllocate=numEntries;
1277 /* Allocate new arrays */
1278 pPointsNew=(POINT *)HeapAlloc( GetProcessHeap(), 0,
1279 numEntriesToAllocate * sizeof(POINT) );
1280 if(!pPointsNew)
1281 return FALSE;
1282 pFlagsNew=(BYTE *)HeapAlloc( GetProcessHeap(), 0,
1283 numEntriesToAllocate * sizeof(BYTE) );
1284 if(!pFlagsNew)
1286 HeapFree( GetProcessHeap(), 0, pPointsNew );
1287 return FALSE;
1290 /* Copy old arrays to new arrays and discard old arrays */
1291 if(pPath->pPoints)
1293 assert(pPath->pFlags);
1295 memcpy(pPointsNew, pPath->pPoints,
1296 sizeof(POINT)*pPath->numEntriesUsed);
1297 memcpy(pFlagsNew, pPath->pFlags,
1298 sizeof(BYTE)*pPath->numEntriesUsed);
1300 HeapFree( GetProcessHeap(), 0, pPath->pPoints );
1301 HeapFree( GetProcessHeap(), 0, pPath->pFlags );
1303 pPath->pPoints=pPointsNew;
1304 pPath->pFlags=pFlagsNew;
1305 pPath->numEntriesAllocated=numEntriesToAllocate;
1308 return TRUE;
1311 /* PATH_GetPathFromHDC
1313 * Retrieves a pointer to the GdiPath structure contained in an HDC and
1314 * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
1316 static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath)
1318 DC *pDC;
1320 pDC=DC_GetDCPtr(hdc);
1321 if(pDC)
1323 *ppPath=&pDC->w.path;
1324 return TRUE;
1326 else
1327 return FALSE;
1330 /* PATH_DoArcPart
1332 * Creates a Bezier spline that corresponds to part of an arc and appends the
1333 * corresponding points to the path. The start and end angles are passed in
1334 * "angleStart" and "angleEnd"; these angles should span a quarter circle
1335 * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
1336 * point is added to the path; otherwise, it is assumed that the current
1337 * position is equal to the first control point.
1339 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
1340 double angleStart, double angleEnd, BOOL addMoveTo)
1342 double halfAngle, a;
1343 double xNorm[4], yNorm[4];
1344 POINT point;
1345 int i;
1347 assert(fabs(angleEnd-angleStart)<=M_PI_2);
1349 /* FIXME: Is there an easier way of computing this? */
1351 /* Compute control points */
1352 halfAngle=(angleEnd-angleStart)/2.0;
1353 if(fabs(halfAngle)>1e-8)
1355 a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
1356 xNorm[0]=cos(angleStart);
1357 yNorm[0]=sin(angleStart);
1358 xNorm[1]=xNorm[0] - a*yNorm[0];
1359 yNorm[1]=yNorm[0] + a*xNorm[0];
1360 xNorm[3]=cos(angleEnd);
1361 yNorm[3]=sin(angleEnd);
1362 xNorm[2]=xNorm[3] + a*yNorm[3];
1363 yNorm[2]=yNorm[3] - a*xNorm[3];
1365 else
1366 for(i=0; i<4; i++)
1368 xNorm[i]=cos(angleStart);
1369 yNorm[i]=sin(angleStart);
1372 /* Add starting point to path if desired */
1373 if(addMoveTo)
1375 PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
1376 if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
1377 return FALSE;
1380 /* Add remaining control points */
1381 for(i=1; i<4; i++)
1383 PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
1384 if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1385 return FALSE;
1388 return TRUE;
1391 /* PATH_ScaleNormalizedPoint
1393 * Scales a normalized point (x, y) with respect to the box whose corners are
1394 * passed in "corners". The point is stored in "*pPoint". The normalized
1395 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1396 * (1.0, 1.0) correspond to corners[1].
1398 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
1399 double y, POINT *pPoint)
1401 pPoint->x=GDI_ROUND( (double)corners[0].x +
1402 (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
1403 pPoint->y=GDI_ROUND( (double)corners[0].y +
1404 (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
1407 /* PATH_NormalizePoint
1409 * Normalizes a point with respect to the box whose corners are passed in
1410 * "corners". The normalized coordinates are stored in "*pX" and "*pY".
1412 static void PATH_NormalizePoint(FLOAT_POINT corners[],
1413 const FLOAT_POINT *pPoint,
1414 double *pX, double *pY)
1416 *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) *
1417 2.0 - 1.0;
1418 *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) *
1419 2.0 - 1.0;
1422 /*******************************************************************
1423 * FlattenPath16 [GDI.516]
1427 BOOL16 WINAPI FlattenPath16(HDC16 hdc)
1429 return (BOOL16) FlattenPath((HDC) hdc);
1432 /*******************************************************************
1433 * FlattenPath [GDI32.103]
1437 BOOL WINAPI FlattenPath(HDC hdc)
1439 DC *dc = DC_GetDCPtr( hdc );
1440 GdiPath *pPath;
1441 TRACE("%08x\n", hdc);
1443 if(!dc) {
1444 SetLastError(ERROR_INVALID_HANDLE);
1445 return FALSE;
1448 if(dc->funcs->pFlattenPath)
1449 return dc->funcs->pFlattenPath(dc);
1451 pPath = &dc->w.path;
1452 if(pPath->state != PATH_Closed)
1453 return FALSE;
1454 return PATH_FlattenPath(pPath);
1457 /*******************************************************************
1458 * StrokeAndFillPath16 [GDI.520]
1462 BOOL16 WINAPI StrokeAndFillPath16(HDC16 hdc)
1464 return (BOOL16) StrokeAndFillPath((HDC) hdc);
1467 /*******************************************************************
1468 * StrokeAndFillPath [GDI32.352]
1472 BOOL WINAPI StrokeAndFillPath(HDC hdc)
1474 DC *dc = DC_GetDCPtr( hdc );
1476 if(!dc) {
1477 SetLastError(ERROR_INVALID_HANDLE);
1478 return FALSE;
1481 if(dc->funcs->pStrokeAndFillPath)
1482 return dc->funcs->pStrokeAndFillPath(dc);
1484 FIXME("stub\n");
1485 return StrokePath(hdc);
1488 /*******************************************************************
1489 * StrokePath16 [GDI.521]
1493 BOOL16 WINAPI StrokePath16(HDC16 hdc)
1495 return (BOOL16) StrokePath((HDC) hdc);
1498 /*******************************************************************
1499 * StrokePath [GDI32.353]
1503 BOOL WINAPI StrokePath(HDC hdc)
1505 DC *dc = DC_GetDCPtr( hdc );
1506 GdiPath *pPath;
1507 INT i;
1508 POINT ptLastMove = {0,0};
1510 TRACE("(%08x)\n", hdc);
1511 if(!dc) {
1512 SetLastError(ERROR_INVALID_HANDLE);
1513 return FALSE;
1516 if(dc->funcs->pStrokePath)
1517 return dc->funcs->pStrokePath(dc);
1519 pPath = &dc->w.path;
1520 if(pPath->state != PATH_Closed)
1521 return FALSE;
1523 SaveDC(hdc);
1524 SetMapMode(hdc, MM_TEXT);
1525 SetViewportOrgEx(hdc, 0, 0, NULL);
1526 SetWindowOrgEx(hdc, 0, 0, NULL);
1527 for(i = 0; i < pPath->numEntriesUsed; i++) {
1528 switch(pPath->pFlags[i]) {
1529 case PT_MOVETO:
1530 TRACE("Got PT_MOVETO (%ld, %ld)\n",
1531 pPath->pPoints[i].x, pPath->pPoints[i].y);
1532 MoveToEx(hdc, pPath->pPoints[i].x, pPath->pPoints[i].y, NULL);
1533 ptLastMove = pPath->pPoints[i];
1534 break;
1535 case PT_LINETO:
1536 case (PT_LINETO | PT_CLOSEFIGURE):
1537 TRACE("Got PT_LINETO (%ld, %ld)\n",
1538 pPath->pPoints[i].x, pPath->pPoints[i].y);
1539 LineTo(hdc, pPath->pPoints[i].x, pPath->pPoints[i].y);
1540 break;
1541 case PT_BEZIERTO:
1542 TRACE("Got PT_BEZIERTO\n");
1543 if(pPath->pFlags[i+1] != PT_BEZIERTO ||
1544 (pPath->pFlags[i+2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) {
1545 ERR("Path didn't contain 3 successive PT_BEZIERTOs\n");
1546 return FALSE;
1548 PolyBezierTo(hdc, &pPath->pPoints[i], 3);
1549 i += 2;
1550 break;
1551 default:
1552 ERR("Got path flag %d\n", (INT)pPath->pFlags[i]);
1553 return FALSE;
1555 if(pPath->pFlags[i] & PT_CLOSEFIGURE)
1556 LineTo(hdc, ptLastMove.x, ptLastMove.y);
1558 RestoreDC(hdc, -1);
1559 PATH_EmptyPath(pPath);
1560 return TRUE;
1563 /*******************************************************************
1564 * WidenPath16 [GDI.522]
1568 BOOL16 WINAPI WidenPath16(HDC16 hdc)
1570 return (BOOL16) WidenPath((HDC) hdc);
1573 /*******************************************************************
1574 * WidenPath [GDI32.360]
1578 BOOL WINAPI WidenPath(HDC hdc)
1580 DC *dc = DC_GetDCPtr( hdc );
1582 if(!dc) {
1583 SetLastError(ERROR_INVALID_HANDLE);
1584 return FALSE;
1587 if(dc->funcs->pWidenPath)
1588 return dc->funcs->pWidenPath(dc);
1590 FIXME("stub\n");
1591 return 0;