2 * Graphics paths (BeginPath, EndPath etc.)
4 * Copyright 1997, 1998 Martin Boehme
11 #if defined(HAVE_FLOAT_H)
20 #include "debugtools.h"
23 DEFAULT_DEBUG_CHANNEL(gdi
)
25 /* Notes on the implementation
27 * The implementation is based on dynamically resizable arrays of points and
28 * flags. I dithered for a bit before deciding on this implementation, and
29 * I had even done a bit of work on a linked list version before switching
30 * to arrays. It's a bit of a tradeoff. When you use linked lists, the
31 * implementation of FlattenPath is easier, because you can rip the
32 * PT_BEZIERTO entries out of the middle of the list and link the
33 * corresponding PT_LINETO entries in. However, when you use arrays,
34 * PathToRegion becomes easier, since you can essentially just pass your array
35 * of points to CreatePolyPolygonRgn. Also, if I'd used linked lists, I would
36 * have had the extra effort of creating a chunk-based allocation scheme
37 * in order to use memory effectively. That's why I finally decided to use
38 * arrays. Note by the way that the array based implementation has the same
39 * linear time complexity that linked lists would have since the arrays grow
42 * The points are stored in the path in device coordinates. This is
43 * consistent with the way Windows does things (for instance, see the Win32
44 * SDK documentation for GetPath).
46 * The word "stroke" appears in several places (e.g. in the flag
47 * GdiPath.newStroke). A stroke consists of a PT_MOVETO followed by one or
48 * more PT_LINETOs or PT_BEZIERTOs, up to, but not including, the next
49 * PT_MOVETO. Note that this is not the same as the definition of a figure;
50 * a figure can contain several strokes.
52 * I modified the drawing functions (MoveTo, LineTo etc.) to test whether
53 * the path is open and to call the corresponding function in path.c if this
54 * is the case. A more elegant approach would be to modify the function
55 * pointers in the DC_FUNCTIONS structure; however, this would be a lot more
56 * complex. Also, the performance degradation caused by my approach in the
57 * case where no path is open is so small that it cannot be measured.
62 /* FIXME: A lot of stuff isn't implemented yet. There is much more to come. */
64 #define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
65 #define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
66 #define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
69 static BOOL
PATH_PathToRegion(const GdiPath
*pPath
, INT nPolyFillMode
,
71 static void PATH_EmptyPath(GdiPath
*pPath
);
72 static BOOL
PATH_AddEntry(GdiPath
*pPath
, const POINT
*pPoint
,
74 static BOOL
PATH_ReserveEntries(GdiPath
*pPath
, INT numEntries
);
75 static BOOL
PATH_GetPathFromHDC(HDC hdc
, GdiPath
**ppPath
);
76 static BOOL
PATH_DoArcPart(GdiPath
*pPath
, FLOAT_POINT corners
[],
77 double angleStart
, double angleEnd
, BOOL addMoveTo
);
78 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners
[], double x
,
79 double y
, POINT
*pPoint
);
80 static void PATH_NormalizePoint(FLOAT_POINT corners
[], const FLOAT_POINT
81 *pPoint
, double *pX
, double *pY
);
84 /***********************************************************************
85 * BeginPath16 (GDI.512)
87 BOOL16 WINAPI
BeginPath16(HDC16 hdc
)
89 return (BOOL16
)BeginPath((HDC
)hdc
);
93 /***********************************************************************
96 BOOL WINAPI
BeginPath(HDC hdc
)
98 DC
*dc
= DC_GetDCPtr( hdc
);
102 SetLastError(ERROR_INVALID_HANDLE
);
106 if(dc
->funcs
->pBeginPath
)
107 return dc
->funcs
->pBeginPath(dc
);
111 /* If path is already open, do nothing */
112 if(pPath
->state
==PATH_Open
)
115 /* Make sure that path is empty */
116 PATH_EmptyPath(pPath
);
118 /* Initialize variables for new path */
119 pPath
->newStroke
=TRUE
;
120 pPath
->state
=PATH_Open
;
126 /***********************************************************************
127 * EndPath16 (GDI.514)
129 BOOL16 WINAPI
EndPath16(HDC16 hdc
)
131 return (BOOL16
)EndPath((HDC
)hdc
);
135 /***********************************************************************
138 BOOL WINAPI
EndPath(HDC hdc
)
140 DC
*dc
= DC_GetDCPtr( hdc
);
144 SetLastError(ERROR_INVALID_HANDLE
);
148 if(dc
->funcs
->pEndPath
)
149 return dc
->funcs
->pEndPath(dc
);
153 /* Check that path is currently being constructed */
154 if(pPath
->state
!=PATH_Open
)
156 SetLastError(ERROR_CAN_NOT_COMPLETE
);
160 /* Set flag to indicate that path is finished */
161 pPath
->state
=PATH_Closed
;
167 /***********************************************************************
168 * AbortPath16 (GDI.511)
170 BOOL16 WINAPI
AbortPath16(HDC16 hdc
)
172 return (BOOL16
)AbortPath((HDC
)hdc
);
176 /******************************************************************************
177 * AbortPath [GDI32.1]
178 * Closes and discards paths from device context
181 * Check that SetLastError is being called correctly
184 * hdc [I] Handle to device context
188 BOOL WINAPI
AbortPath( HDC hdc
)
190 DC
*dc
= DC_GetDCPtr( hdc
);
194 SetLastError(ERROR_INVALID_HANDLE
);
198 if(dc
->funcs
->pAbortPath
)
199 return dc
->funcs
->pAbortPath(dc
);
203 /* Remove all entries from the path */
204 PATH_EmptyPath(pPath
);
210 /***********************************************************************
211 * CloseFigure16 (GDI.513)
213 BOOL16 WINAPI
CloseFigure16(HDC16 hdc
)
215 return (BOOL16
)CloseFigure((HDC
)hdc
);
219 /***********************************************************************
220 * CloseFigure (GDI32.16)
222 * FIXME: Check that SetLastError is being called correctly
224 BOOL WINAPI
CloseFigure(HDC hdc
)
226 DC
*dc
= DC_GetDCPtr( hdc
);
230 SetLastError(ERROR_INVALID_HANDLE
);
234 if(dc
->funcs
->pCloseFigure
)
235 return dc
->funcs
->pCloseFigure(dc
);
239 /* Check that path is open */
240 if(pPath
->state
!=PATH_Open
)
242 SetLastError(ERROR_CAN_NOT_COMPLETE
);
246 /* FIXME: Shouldn't we draw a line to the beginning of the figure? */
248 /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
249 if(pPath
->numEntriesUsed
)
251 pPath
->pFlags
[pPath
->numEntriesUsed
-1]|=PT_CLOSEFIGURE
;
252 pPath
->newStroke
=TRUE
;
259 /***********************************************************************
260 * GetPath16 (GDI.517)
262 INT16 WINAPI
GetPath16(HDC16 hdc
, LPPOINT16 pPoints
, LPBYTE pTypes
,
265 FIXME("(%d,%p,%p): stub\n",hdc
,pPoints
,pTypes
);
271 /***********************************************************************
272 * GetPath (GDI32.210)
274 INT WINAPI
GetPath(HDC hdc
, LPPOINT pPoints
, LPBYTE pTypes
,
279 /* Get pointer to path */
280 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
282 SetLastError(ERROR_INVALID_PARAMETER
);
286 /* Check that path is closed */
287 if(pPath
->state
!=PATH_Closed
)
289 SetLastError(ERROR_CAN_NOT_COMPLETE
);
294 return pPath
->numEntriesUsed
;
295 else if(nSize
<pPath
->numEntriesUsed
)
297 SetLastError(ERROR_INVALID_PARAMETER
);
302 memcpy(pPoints
, pPath
->pPoints
, sizeof(POINT
)*pPath
->numEntriesUsed
);
303 memcpy(pTypes
, pPath
->pFlags
, sizeof(BYTE
)*pPath
->numEntriesUsed
);
305 /* Convert the points to logical coordinates */
306 if(!DPtoLP(hdc
, pPoints
, pPath
->numEntriesUsed
))
308 /* FIXME: Is this the correct value? */
309 SetLastError(ERROR_CAN_NOT_COMPLETE
);
313 return pPath
->numEntriesUsed
;
317 /***********************************************************************
318 * PathToRegion16 (GDI.518)
320 HRGN16 WINAPI
PathToRegion16(HDC16 hdc
)
322 return (HRGN16
) PathToRegion((HDC
) hdc
);
325 /***********************************************************************
326 * PathToRegion (GDI32.261)
329 * Check that SetLastError is being called correctly
331 * The documentation does not state this explicitly, but a test under Windows
332 * shows that the region which is returned should be in device coordinates.
334 HRGN WINAPI
PathToRegion(HDC hdc
)
339 /* Get pointer to path */
340 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
342 SetLastError(ERROR_INVALID_PARAMETER
);
346 /* Check that path is closed */
347 if(pPath
->state
!=PATH_Closed
)
349 SetLastError(ERROR_CAN_NOT_COMPLETE
);
353 /* FIXME: Should we empty the path even if conversion failed? */
354 if(PATH_PathToRegion(pPath
, GetPolyFillMode(hdc
), &hrgnRval
))
355 PATH_EmptyPath(pPath
);
362 /***********************************************************************
363 * FillPath16 (GDI.515)
365 BOOL16 WINAPI
FillPath16(HDC16 hdc
)
367 return (BOOL16
) FillPath((HDC
) hdc
);
370 /***********************************************************************
371 * FillPath (GDI32.100)
374 * Check that SetLastError is being called correctly
376 BOOL WINAPI
FillPath(HDC hdc
)
379 INT mapMode
, graphicsMode
;
380 SIZE ptViewportExt
, ptWindowExt
;
381 POINT ptViewportOrg
, ptWindowOrg
;
384 DC
*dc
= DC_GetDCPtr( hdc
);
387 SetLastError(ERROR_INVALID_HANDLE
);
391 if(dc
->funcs
->pFillPath
)
392 return dc
->funcs
->pFillPath(dc
);
396 /* Check that path is closed */
397 if(pPath
->state
!=PATH_Closed
)
399 SetLastError(ERROR_CAN_NOT_COMPLETE
);
403 /* Construct a region from the path and fill it */
404 if(PATH_PathToRegion(pPath
, GetPolyFillMode(hdc
), &hrgn
))
406 /* Since PaintRgn interprets the region as being in logical coordinates
407 * but the points we store for the path are already in device
408 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
409 * Using SaveDC to save information about the mapping mode / world
410 * transform would be easier but would require more overhead, especially
411 * now that SaveDC saves the current path.
414 /* Save the information about the old mapping mode */
415 mapMode
=GetMapMode(hdc
);
416 GetViewportExtEx(hdc
, &ptViewportExt
);
417 GetViewportOrgEx(hdc
, &ptViewportOrg
);
418 GetWindowExtEx(hdc
, &ptWindowExt
);
419 GetWindowOrgEx(hdc
, &ptWindowOrg
);
421 /* Save world transform
422 * NB: The Windows documentation on world transforms would lead one to
423 * believe that this has to be done only in GM_ADVANCED; however, my
424 * tests show that resetting the graphics mode to GM_COMPATIBLE does
425 * not reset the world transform.
427 GetWorldTransform(hdc
, &xform
);
430 SetMapMode(hdc
, MM_TEXT
);
431 SetViewportOrgEx(hdc
, 0, 0, NULL
);
432 SetWindowOrgEx(hdc
, 0, 0, NULL
);
434 /* Paint the region */
437 /* Restore the old mapping mode */
438 SetMapMode(hdc
, mapMode
);
439 SetViewportExtEx(hdc
, ptViewportExt
.cx
, ptViewportExt
.cy
, NULL
);
440 SetViewportOrgEx(hdc
, ptViewportOrg
.x
, ptViewportOrg
.y
, NULL
);
441 SetWindowExtEx(hdc
, ptWindowExt
.cx
, ptWindowExt
.cy
, NULL
);
442 SetWindowOrgEx(hdc
, ptWindowOrg
.x
, ptWindowOrg
.y
, NULL
);
444 /* Go to GM_ADVANCED temporarily to restore the world transform */
445 graphicsMode
=GetGraphicsMode(hdc
);
446 SetGraphicsMode(hdc
, GM_ADVANCED
);
447 SetWorldTransform(hdc
, &xform
);
448 SetGraphicsMode(hdc
, graphicsMode
);
451 PATH_EmptyPath(pPath
);
456 /* FIXME: Should the path be emptied even if conversion failed? */
457 /* PATH_EmptyPath(pPath); */
462 /***********************************************************************
463 * SelectClipPath16 (GDI.519)
465 BOOL16 WINAPI
SelectClipPath16(HDC16 hdc
, INT16 iMode
)
467 return (BOOL16
) SelectClipPath((HDC
) hdc
, iMode
);
470 /***********************************************************************
471 * SelectClipPath (GDI32.296)
473 * Check that SetLastError is being called correctly
475 BOOL WINAPI
SelectClipPath(HDC hdc
, INT iMode
)
480 DC
*dc
= DC_GetDCPtr( hdc
);
483 SetLastError(ERROR_INVALID_HANDLE
);
487 if(dc
->funcs
->pSelectClipPath
)
488 return dc
->funcs
->pSelectClipPath(dc
, iMode
);
492 /* Check that path is closed */
493 if(pPath
->state
!=PATH_Closed
)
495 SetLastError(ERROR_CAN_NOT_COMPLETE
);
499 /* Construct a region from the path */
500 if(PATH_PathToRegion(pPath
, GetPolyFillMode(hdc
), &hrgnPath
))
502 success
= ExtSelectClipRgn( hdc
, hrgnPath
, iMode
) != ERROR
;
503 DeleteObject(hrgnPath
);
507 PATH_EmptyPath(pPath
);
508 /* FIXME: Should this function delete the path even if it failed? */
517 /***********************************************************************
523 * Initializes the GdiPath structure.
525 void PATH_InitGdiPath(GdiPath
*pPath
)
529 pPath
->state
=PATH_Null
;
532 pPath
->numEntriesUsed
=0;
533 pPath
->numEntriesAllocated
=0;
536 /* PATH_DestroyGdiPath
538 * Destroys a GdiPath structure (frees the memory in the arrays).
540 void PATH_DestroyGdiPath(GdiPath
*pPath
)
544 HeapFree( GetProcessHeap(), 0, pPath
->pPoints
);
545 HeapFree( GetProcessHeap(), 0, pPath
->pFlags
);
548 /* PATH_AssignGdiPath
550 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
551 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
552 * not just the pointers. Since this means that the arrays in pPathDest may
553 * need to be resized, pPathDest should have been initialized using
554 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
555 * not a copy constructor).
556 * Returns TRUE if successful, else FALSE.
558 BOOL
PATH_AssignGdiPath(GdiPath
*pPathDest
, const GdiPath
*pPathSrc
)
560 assert(pPathDest
!=NULL
&& pPathSrc
!=NULL
);
562 /* Make sure destination arrays are big enough */
563 if(!PATH_ReserveEntries(pPathDest
, pPathSrc
->numEntriesUsed
))
566 /* Perform the copy operation */
567 memcpy(pPathDest
->pPoints
, pPathSrc
->pPoints
,
568 sizeof(POINT
)*pPathSrc
->numEntriesUsed
);
569 memcpy(pPathDest
->pFlags
, pPathSrc
->pFlags
,
570 sizeof(BYTE
)*pPathSrc
->numEntriesUsed
);
572 pPathDest
->state
=pPathSrc
->state
;
573 pPathDest
->numEntriesUsed
=pPathSrc
->numEntriesUsed
;
574 pPathDest
->newStroke
=pPathSrc
->newStroke
;
581 * Should be called when a MoveTo is performed on a DC that has an
582 * open path. This starts a new stroke. Returns TRUE if successful, else
585 BOOL
PATH_MoveTo(HDC hdc
)
589 /* Get pointer to path */
590 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
593 /* Check that path is open */
594 if(pPath
->state
!=PATH_Open
)
595 /* FIXME: Do we have to call SetLastError? */
598 /* Start a new stroke */
599 pPath
->newStroke
=TRUE
;
606 * Should be called when a LineTo is performed on a DC that has an
607 * open path. This adds a PT_LINETO entry to the path (and possibly
608 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
609 * Returns TRUE if successful, else FALSE.
611 BOOL
PATH_LineTo(HDC hdc
, INT x
, INT y
)
614 POINT point
, pointCurPos
;
616 /* Get pointer to path */
617 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
620 /* Check that path is open */
621 if(pPath
->state
!=PATH_Open
)
624 /* Convert point to device coordinates */
627 if(!LPtoDP(hdc
, &point
, 1))
630 /* Add a PT_MOVETO if necessary */
633 pPath
->newStroke
=FALSE
;
634 if(!GetCurrentPositionEx(hdc
, &pointCurPos
) ||
635 !LPtoDP(hdc
, &pointCurPos
, 1))
637 if(!PATH_AddEntry(pPath
, &pointCurPos
, PT_MOVETO
))
641 /* Add a PT_LINETO entry */
642 return PATH_AddEntry(pPath
, &point
, PT_LINETO
);
647 * Should be called when a call to Rectangle is performed on a DC that has
648 * an open path. Returns TRUE if successful, else FALSE.
650 BOOL
PATH_Rectangle(HDC hdc
, INT x1
, INT y1
, INT x2
, INT y2
)
653 POINT corners
[2], pointTemp
;
656 /* Get pointer to path */
657 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
660 /* Check that path is open */
661 if(pPath
->state
!=PATH_Open
)
664 /* Convert points to device coordinates */
669 if(!LPtoDP(hdc
, corners
, 2))
672 /* Make sure first corner is top left and second corner is bottom right */
673 if(corners
[0].x
>corners
[1].x
)
676 corners
[0].x
=corners
[1].x
;
679 if(corners
[0].y
>corners
[1].y
)
682 corners
[0].y
=corners
[1].y
;
686 /* In GM_COMPATIBLE, don't include bottom and right edges */
687 if(GetGraphicsMode(hdc
)==GM_COMPATIBLE
)
693 /* Close any previous figure */
694 if(!CloseFigure(hdc
))
696 /* The CloseFigure call shouldn't have failed */
701 /* Add four points to the path */
702 pointTemp
.x
=corners
[1].x
;
703 pointTemp
.y
=corners
[0].y
;
704 if(!PATH_AddEntry(pPath
, &pointTemp
, PT_MOVETO
))
706 if(!PATH_AddEntry(pPath
, corners
, PT_LINETO
))
708 pointTemp
.x
=corners
[0].x
;
709 pointTemp
.y
=corners
[1].y
;
710 if(!PATH_AddEntry(pPath
, &pointTemp
, PT_LINETO
))
712 if(!PATH_AddEntry(pPath
, corners
+1, PT_LINETO
))
715 /* Close the rectangle figure */
716 if(!CloseFigure(hdc
))
718 /* The CloseFigure call shouldn't have failed */
728 * Should be called when a call to Ellipse is performed on a DC that has
729 * an open path. This adds four Bezier splines representing the ellipse
730 * to the path. Returns TRUE if successful, else FALSE.
732 BOOL
PATH_Ellipse(HDC hdc
, INT x1
, INT y1
, INT x2
, INT y2
)
734 /* TODO: This should probably be revised to call PATH_AngleArc */
735 /* (once it exists) */
736 return PATH_Arc(hdc
, x1
, y1
, x2
, y2
, x1
, (y1
+y2
)/2, x1
, (y1
+y2
)/2);
741 * Should be called when a call to Arc is performed on a DC that has
742 * an open path. This adds up to five Bezier splines representing the arc
743 * to the path. Returns TRUE if successful, else FALSE.
745 BOOL
PATH_Arc(HDC hdc
, INT x1
, INT y1
, INT x2
, INT y2
,
746 INT xStart
, INT yStart
, INT xEnd
, INT yEnd
)
750 double angleStart
, angleEnd
, angleStartQuadrant
, angleEndQuadrant
=0.0;
751 /* Initialize angleEndQuadrant to silence gcc's warning */
753 FLOAT_POINT corners
[2], pointStart
, pointEnd
;
757 /* FIXME: This function should check for all possible error returns */
758 /* FIXME: Do we have to respect newStroke? */
760 /* Get pointer to DC */
761 pDC
=DC_GetDCPtr(hdc
);
765 /* Get pointer to path */
766 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
769 /* Check that path is open */
770 if(pPath
->state
!=PATH_Open
)
773 /* FIXME: Do we have to close the current figure? */
775 /* Check for zero height / width */
776 /* FIXME: Only in GM_COMPATIBLE? */
780 /* Convert points to device coordinates */
781 corners
[0].x
=(FLOAT
)x1
;
782 corners
[0].y
=(FLOAT
)y1
;
783 corners
[1].x
=(FLOAT
)x2
;
784 corners
[1].y
=(FLOAT
)y2
;
785 pointStart
.x
=(FLOAT
)xStart
;
786 pointStart
.y
=(FLOAT
)yStart
;
787 pointEnd
.x
=(FLOAT
)xEnd
;
788 pointEnd
.y
=(FLOAT
)yEnd
;
789 INTERNAL_LPTODP_FLOAT(pDC
, corners
);
790 INTERNAL_LPTODP_FLOAT(pDC
, corners
+1);
791 INTERNAL_LPTODP_FLOAT(pDC
, &pointStart
);
792 INTERNAL_LPTODP_FLOAT(pDC
, &pointEnd
);
794 /* Make sure first corner is top left and second corner is bottom right */
795 if(corners
[0].x
>corners
[1].x
)
798 corners
[0].x
=corners
[1].x
;
801 if(corners
[0].y
>corners
[1].y
)
804 corners
[0].y
=corners
[1].y
;
808 /* Compute start and end angle */
809 PATH_NormalizePoint(corners
, &pointStart
, &x
, &y
);
810 angleStart
=atan2(y
, x
);
811 PATH_NormalizePoint(corners
, &pointEnd
, &x
, &y
);
812 angleEnd
=atan2(y
, x
);
814 /* Make sure the end angle is "on the right side" of the start angle */
815 if(GetArcDirection(hdc
)==AD_CLOCKWISE
)
817 if(angleEnd
<=angleStart
)
820 assert(angleEnd
>=angleStart
);
825 if(angleEnd
>=angleStart
)
828 assert(angleEnd
<=angleStart
);
832 /* In GM_COMPATIBLE, don't include bottom and right edges */
833 if(GetGraphicsMode(hdc
)==GM_COMPATIBLE
)
839 /* Add the arc to the path with one Bezier spline per quadrant that the
845 /* Determine the start and end angles for this quadrant */
848 angleStartQuadrant
=angleStart
;
849 if(GetArcDirection(hdc
)==AD_CLOCKWISE
)
850 angleEndQuadrant
=(floor(angleStart
/M_PI_2
)+1.0)*M_PI_2
;
852 angleEndQuadrant
=(ceil(angleStart
/M_PI_2
)-1.0)*M_PI_2
;
856 angleStartQuadrant
=angleEndQuadrant
;
857 if(GetArcDirection(hdc
)==AD_CLOCKWISE
)
858 angleEndQuadrant
+=M_PI_2
;
860 angleEndQuadrant
-=M_PI_2
;
863 /* Have we reached the last part of the arc? */
864 if((GetArcDirection(hdc
)==AD_CLOCKWISE
&&
865 angleEnd
<angleEndQuadrant
) ||
866 (GetArcDirection(hdc
)==AD_COUNTERCLOCKWISE
&&
867 angleEnd
>angleEndQuadrant
))
869 /* Adjust the end angle for this quadrant */
870 angleEndQuadrant
=angleEnd
;
874 /* Add the Bezier spline to the path */
875 PATH_DoArcPart(pPath
, corners
, angleStartQuadrant
, angleEndQuadrant
,
883 BOOL
PATH_PolyBezierTo(HDC hdc
, const POINT
*pts
, DWORD cbPoints
)
889 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
892 /* Check that path is open */
893 if(pPath
->state
!=PATH_Open
)
896 /* Add a PT_MOVETO if necessary */
899 pPath
->newStroke
=FALSE
;
900 if(!GetCurrentPositionEx(hdc
, &pt
) ||
901 !LPtoDP(hdc
, &pt
, 1))
903 if(!PATH_AddEntry(pPath
, &pt
, PT_MOVETO
))
906 for(i
= 0; i
< cbPoints
; i
++) {
908 if(!LPtoDP(hdc
, &pt
, 1))
910 PATH_AddEntry(pPath
, &pt
, PT_BEZIERTO
);
915 /***********************************************************************
921 * Creates a region from the specified path using the specified polygon
922 * filling mode. The path is left unchanged. A handle to the region that
923 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
924 * error occurs, SetLastError is called with the appropriate value and
927 static BOOL
PATH_PathToRegion(const GdiPath
*pPath
, INT nPolyFillMode
,
930 int numStrokes
, iStroke
, i
;
931 INT
*pNumPointsInStroke
;
937 /* FIXME: What happens when number of points is zero? */
939 /* First pass: Find out how many strokes there are in the path */
940 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
942 for(i
=0; i
<pPath
->numEntriesUsed
; i
++)
943 if((pPath
->pFlags
[i
] & ~PT_CLOSEFIGURE
) == PT_MOVETO
)
946 /* Allocate memory for number-of-points-in-stroke array */
947 pNumPointsInStroke
=(int *)HeapAlloc( GetProcessHeap(), 0,
948 sizeof(int) * numStrokes
);
949 if(!pNumPointsInStroke
)
951 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
955 /* Second pass: remember number of points in each polygon */
956 iStroke
=-1; /* Will get incremented to 0 at beginning of first stroke */
957 for(i
=0; i
<pPath
->numEntriesUsed
; i
++)
959 /* Is this the beginning of a new stroke? */
960 if((pPath
->pFlags
[i
] & ~PT_CLOSEFIGURE
) == PT_MOVETO
)
963 pNumPointsInStroke
[iStroke
]=0;
966 pNumPointsInStroke
[iStroke
]++;
969 /* Create a region from the strokes */
970 hrgn
=CreatePolyPolygonRgn(pPath
->pPoints
, pNumPointsInStroke
,
971 numStrokes
, nPolyFillMode
);
974 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
978 /* Free memory for number-of-points-in-stroke array */
979 HeapFree( GetProcessHeap(), 0, pNumPointsInStroke
);
988 * Removes all entries from the path and sets the path state to PATH_Null.
990 static void PATH_EmptyPath(GdiPath
*pPath
)
994 pPath
->state
=PATH_Null
;
995 pPath
->numEntriesUsed
=0;
1000 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
1001 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
1002 * successful, FALSE otherwise (e.g. if not enough memory was available).
1004 BOOL
PATH_AddEntry(GdiPath
*pPath
, const POINT
*pPoint
, BYTE flags
)
1006 assert(pPath
!=NULL
);
1008 /* FIXME: If newStroke is true, perhaps we want to check that we're
1009 * getting a PT_MOVETO
1012 /* Check that path is open */
1013 if(pPath
->state
!=PATH_Open
)
1016 /* Reserve enough memory for an extra path entry */
1017 if(!PATH_ReserveEntries(pPath
, pPath
->numEntriesUsed
+1))
1020 /* Store information in path entry */
1021 pPath
->pPoints
[pPath
->numEntriesUsed
]=*pPoint
;
1022 pPath
->pFlags
[pPath
->numEntriesUsed
]=flags
;
1024 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
1025 if((flags
& PT_CLOSEFIGURE
) == PT_CLOSEFIGURE
)
1026 pPath
->newStroke
=TRUE
;
1028 /* Increment entry count */
1029 pPath
->numEntriesUsed
++;
1034 /* PATH_ReserveEntries
1036 * Ensures that at least "numEntries" entries (for points and flags) have
1037 * been allocated; allocates larger arrays and copies the existing entries
1038 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
1040 static BOOL
PATH_ReserveEntries(GdiPath
*pPath
, INT numEntries
)
1042 INT numEntriesToAllocate
;
1046 assert(pPath
!=NULL
);
1047 assert(numEntries
>=0);
1049 /* Do we have to allocate more memory? */
1050 if(numEntries
> pPath
->numEntriesAllocated
)
1052 /* Find number of entries to allocate. We let the size of the array
1053 * grow exponentially, since that will guarantee linear time
1055 if(pPath
->numEntriesAllocated
)
1057 numEntriesToAllocate
=pPath
->numEntriesAllocated
;
1058 while(numEntriesToAllocate
<numEntries
)
1059 numEntriesToAllocate
=numEntriesToAllocate
*GROW_FACTOR_NUMER
/
1063 numEntriesToAllocate
=numEntries
;
1065 /* Allocate new arrays */
1066 pPointsNew
=(POINT
*)HeapAlloc( GetProcessHeap(), 0,
1067 numEntriesToAllocate
* sizeof(POINT
) );
1070 pFlagsNew
=(BYTE
*)HeapAlloc( GetProcessHeap(), 0,
1071 numEntriesToAllocate
* sizeof(BYTE
) );
1074 HeapFree( GetProcessHeap(), 0, pPointsNew
);
1078 /* Copy old arrays to new arrays and discard old arrays */
1081 assert(pPath
->pFlags
);
1083 memcpy(pPointsNew
, pPath
->pPoints
,
1084 sizeof(POINT
)*pPath
->numEntriesUsed
);
1085 memcpy(pFlagsNew
, pPath
->pFlags
,
1086 sizeof(BYTE
)*pPath
->numEntriesUsed
);
1088 HeapFree( GetProcessHeap(), 0, pPath
->pPoints
);
1089 HeapFree( GetProcessHeap(), 0, pPath
->pFlags
);
1091 pPath
->pPoints
=pPointsNew
;
1092 pPath
->pFlags
=pFlagsNew
;
1093 pPath
->numEntriesAllocated
=numEntriesToAllocate
;
1099 /* PATH_GetPathFromHDC
1101 * Retrieves a pointer to the GdiPath structure contained in an HDC and
1102 * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
1104 static BOOL
PATH_GetPathFromHDC(HDC hdc
, GdiPath
**ppPath
)
1108 pDC
=DC_GetDCPtr(hdc
);
1111 *ppPath
=&pDC
->w
.path
;
1120 * Creates a Bezier spline that corresponds to part of an arc and appends the
1121 * corresponding points to the path. The start and end angles are passed in
1122 * "angleStart" and "angleEnd"; these angles should span a quarter circle
1123 * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
1124 * point is added to the path; otherwise, it is assumed that the current
1125 * position is equal to the first control point.
1127 static BOOL
PATH_DoArcPart(GdiPath
*pPath
, FLOAT_POINT corners
[],
1128 double angleStart
, double angleEnd
, BOOL addMoveTo
)
1130 double halfAngle
, a
;
1131 double xNorm
[4], yNorm
[4];
1135 assert(fabs(angleEnd
-angleStart
)<=M_PI_2
);
1137 /* FIXME: Is there an easier way of computing this? */
1139 /* Compute control points */
1140 halfAngle
=(angleEnd
-angleStart
)/2.0;
1141 if(fabs(halfAngle
)>1e-8)
1143 a
=4.0/3.0*(1-cos(halfAngle
))/sin(halfAngle
);
1144 xNorm
[0]=cos(angleStart
);
1145 yNorm
[0]=sin(angleStart
);
1146 xNorm
[1]=xNorm
[0] - a
*yNorm
[0];
1147 yNorm
[1]=yNorm
[0] + a
*xNorm
[0];
1148 xNorm
[3]=cos(angleEnd
);
1149 yNorm
[3]=sin(angleEnd
);
1150 xNorm
[2]=xNorm
[3] + a
*yNorm
[3];
1151 yNorm
[2]=yNorm
[3] - a
*xNorm
[3];
1156 xNorm
[i
]=cos(angleStart
);
1157 yNorm
[i
]=sin(angleStart
);
1160 /* Add starting point to path if desired */
1163 PATH_ScaleNormalizedPoint(corners
, xNorm
[0], yNorm
[0], &point
);
1164 if(!PATH_AddEntry(pPath
, &point
, PT_MOVETO
))
1168 /* Add remaining control points */
1171 PATH_ScaleNormalizedPoint(corners
, xNorm
[i
], yNorm
[i
], &point
);
1172 if(!PATH_AddEntry(pPath
, &point
, PT_BEZIERTO
))
1179 /* PATH_ScaleNormalizedPoint
1181 * Scales a normalized point (x, y) with respect to the box whose corners are
1182 * passed in "corners". The point is stored in "*pPoint". The normalized
1183 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1184 * (1.0, 1.0) correspond to corners[1].
1186 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners
[], double x
,
1187 double y
, POINT
*pPoint
)
1189 pPoint
->x
=GDI_ROUND( (double)corners
[0].x
+
1190 (double)(corners
[1].x
-corners
[0].x
)*0.5*(x
+1.0) );
1191 pPoint
->y
=GDI_ROUND( (double)corners
[0].y
+
1192 (double)(corners
[1].y
-corners
[0].y
)*0.5*(y
+1.0) );
1195 /* PATH_NormalizePoint
1197 * Normalizes a point with respect to the box whose corners are passed in
1198 * "corners". The normalized coordinates are stored in "*pX" and "*pY".
1200 static void PATH_NormalizePoint(FLOAT_POINT corners
[],
1201 const FLOAT_POINT
*pPoint
,
1202 double *pX
, double *pY
)
1204 *pX
=(double)(pPoint
->x
-corners
[0].x
)/(double)(corners
[1].x
-corners
[0].x
) *
1206 *pY
=(double)(pPoint
->y
-corners
[0].y
)/(double)(corners
[1].y
-corners
[0].y
) *
1210 /*******************************************************************
1211 * FlattenPath16 [GDI.516]
1215 BOOL16 WINAPI
FlattenPath16(HDC16 hdc
)
1217 return (BOOL16
) FlattenPath((HDC
) hdc
);
1220 /*******************************************************************
1221 * FlattenPath [GDI32.103]
1225 BOOL WINAPI
FlattenPath(HDC hdc
)
1227 DC
*dc
= DC_GetDCPtr( hdc
);
1230 SetLastError(ERROR_INVALID_HANDLE
);
1234 if(dc
->funcs
->pFlattenPath
)
1235 return dc
->funcs
->pFlattenPath(dc
);
1241 /*******************************************************************
1242 * StrokeAndFillPath16 [GDI.520]
1246 BOOL16 WINAPI
StrokeAndFillPath16(HDC16 hdc
)
1248 return (BOOL16
) StrokeAndFillPath((HDC
) hdc
);
1251 /*******************************************************************
1252 * StrokeAndFillPath [GDI32.352]
1256 BOOL WINAPI
StrokeAndFillPath(HDC hdc
)
1258 DC
*dc
= DC_GetDCPtr( hdc
);
1261 SetLastError(ERROR_INVALID_HANDLE
);
1265 if(dc
->funcs
->pStrokeAndFillPath
)
1266 return dc
->funcs
->pStrokeAndFillPath(dc
);
1269 return StrokePath(hdc
);
1272 /*******************************************************************
1273 * StrokePath16 [GDI.521]
1277 BOOL16 WINAPI
StrokePath16(HDC16 hdc
)
1279 return (BOOL16
) StrokePath((HDC
) hdc
);
1282 /*******************************************************************
1283 * StrokePath [GDI32.353]
1287 BOOL WINAPI
StrokePath(HDC hdc
)
1289 DC
*dc
= DC_GetDCPtr( hdc
);
1292 POINT ptLastMove
= {0,0};
1294 TRACE("(%08x)\n", hdc
);
1296 SetLastError(ERROR_INVALID_HANDLE
);
1300 if(dc
->funcs
->pStrokePath
)
1301 return dc
->funcs
->pStrokePath(dc
);
1303 pPath
= &dc
->w
.path
;
1304 if(pPath
->state
!= PATH_Closed
)
1308 SetMapMode(hdc
, MM_TEXT
);
1309 SetViewportOrgEx(hdc
, 0, 0, NULL
);
1310 SetWindowOrgEx(hdc
, 0, 0, NULL
);
1311 for(i
= 0; i
< pPath
->numEntriesUsed
; i
++) {
1312 switch(pPath
->pFlags
[i
]) {
1314 TRACE("Got PT_MOVETO (%ld, %ld)\n",
1315 pPath
->pPoints
[i
].x
, pPath
->pPoints
[i
].y
);
1316 MoveToEx(hdc
, pPath
->pPoints
[i
].x
, pPath
->pPoints
[i
].y
, NULL
);
1317 ptLastMove
= pPath
->pPoints
[i
];
1320 case (PT_LINETO
| PT_CLOSEFIGURE
):
1321 TRACE("Got PT_LINETO (%ld, %ld)\n",
1322 pPath
->pPoints
[i
].x
, pPath
->pPoints
[i
].y
);
1323 LineTo(hdc
, pPath
->pPoints
[i
].x
, pPath
->pPoints
[i
].y
);
1326 TRACE("Got PT_BEZIERTO\n");
1327 if(pPath
->pFlags
[i
+1] != PT_BEZIERTO
||
1328 (pPath
->pFlags
[i
+2] & ~PT_CLOSEFIGURE
) != PT_BEZIERTO
) {
1329 ERR("Path didn't contain 3 successive PT_BEZIERTOs\n");
1332 PolyBezierTo(hdc
, &pPath
->pPoints
[i
], 3);
1336 ERR("Got path flag %d\n", (INT
)pPath
->pFlags
[i
]);
1339 if(pPath
->pFlags
[i
] & PT_CLOSEFIGURE
)
1340 LineTo(hdc
, ptLastMove
.x
, ptLastMove
.y
);
1343 PATH_EmptyPath(pPath
);
1347 /*******************************************************************
1348 * WidenPath16 [GDI.522]
1352 BOOL16 WINAPI
WidenPath16(HDC16 hdc
)
1354 return (BOOL16
) WidenPath((HDC
) hdc
);
1357 /*******************************************************************
1358 * WidenPath [GDI32.360]
1362 BOOL WINAPI
WidenPath(HDC hdc
)
1364 DC
*dc
= DC_GetDCPtr( hdc
);
1367 SetLastError(ERROR_INVALID_HANDLE
);
1371 if(dc
->funcs
->pWidenPath
)
1372 return dc
->funcs
->pWidenPath(dc
);