2 * Graphics paths (BeginPath, EndPath etc.)
4 * Copyright 1997, 1998 Martin Boehme
18 /* Notes on the implementation
20 * The implementation is based on dynamically resizable arrays of points and
21 * flags. I dithered for a bit before deciding on this implementation, and
22 * I had even done a bit of work on a linked list version before switching
23 * to arrays. It's a bit of a tradeoff. When you use linked lists, the
24 * implementation of FlattenPath is easier, because you can rip the
25 * PT_BEZIERTO entries out of the middle of the list and link the
26 * corresponding PT_LINETO entries in. However, when you use arrays,
27 * PathToRegion becomes easier, since you can essentially just pass your array
28 * of points to CreatePolyPolygonRgn. Also, if I'd used linked lists, I would
29 * have had the extra effort of creating a chunk-based allocation scheme
30 * in order to use memory effectively. That's why I finally decided to use
31 * arrays. Note by the way that the array based implementation has the same
32 * linear time complexity that linked lists would have since the arrays grow
35 * The points are stored in the path in device coordinates. This is
36 * consistent with the way Windows does things (for instance, see the Win32
37 * SDK documentation for GetPath).
39 * The word "stroke" appears in several places (e.g. in the flag
40 * GdiPath.newStroke). A stroke consists of a PT_MOVETO followed by one or
41 * more PT_LINETOs or PT_BEZIERTOs, up to, but not including, the next
42 * PT_MOVETO. Note that this is not the same as the definition of a figure;
43 * a figure can contain several strokes.
45 * I modified the drawing functions (MoveTo, LineTo etc.) to test whether
46 * the path is open and to call the corresponding function in path.c if this
47 * is the case. A more elegant approach would be to modify the function
48 * pointers in the DC_FUNCTIONS structure; however, this would be a lot more
49 * complex. Also, the performance degradation caused by my approach in the
50 * case where no path is open is so small that it cannot be measured.
55 /* FIXME: A lot of stuff isn't implemented yet. There is much more to come. */
57 #define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
58 #define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
59 #define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
62 static BOOL32
PATH_PathToRegion(const GdiPath
*pPath
, INT32 nPolyFillMode
,
64 static void PATH_EmptyPath(GdiPath
*pPath
);
65 static BOOL32
PATH_AddEntry(GdiPath
*pPath
, const POINT32
*pPoint
,
67 static BOOL32
PATH_ReserveEntries(GdiPath
*pPath
, INT32 numEntries
);
68 static BOOL32
PATH_GetPathFromHDC(HDC32 hdc
, GdiPath
**ppPath
);
69 static BOOL32
PATH_DoArcPart(GdiPath
*pPath
, FLOAT_POINT corners
[],
70 double angleStart
, double angleEnd
, BOOL32 addMoveTo
);
71 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners
[], double x
,
72 double y
, POINT32
*pPoint
);
73 static void PATH_NormalizePoint(FLOAT_POINT corners
[], const FLOAT_POINT
74 *pPoint
, double *pX
, double *pY
);
77 /***********************************************************************
78 * BeginPath16 (GDI.512)
80 BOOL16 WINAPI
BeginPath16(HDC16 hdc
)
82 return (BOOL16
)BeginPath32((HDC32
)hdc
);
86 /***********************************************************************
87 * BeginPath32 (GDI32.9)
89 BOOL32 WINAPI
BeginPath32(HDC32 hdc
)
93 /* Get pointer to path */
94 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
96 SetLastError(ERROR_INVALID_HANDLE
);
100 /* If path is already open, do nothing */
101 if(pPath
->state
==PATH_Open
)
104 /* Make sure that path is empty */
105 PATH_EmptyPath(pPath
);
107 /* Initialize variables for new path */
108 pPath
->newStroke
=TRUE
;
109 pPath
->state
=PATH_Open
;
115 /***********************************************************************
116 * EndPath16 (GDI.514)
118 BOOL16 WINAPI
EndPath16(HDC16 hdc
)
120 return (BOOL16
)EndPath32((HDC32
)hdc
);
124 /***********************************************************************
125 * EndPath32 (GDI32.78)
127 BOOL32 WINAPI
EndPath32(HDC32 hdc
)
131 /* Get pointer to path */
132 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
134 SetLastError(ERROR_INVALID_HANDLE
);
138 /* Check that path is currently being constructed */
139 if(pPath
->state
!=PATH_Open
)
141 SetLastError(ERROR_CAN_NOT_COMPLETE
);
145 /* Set flag to indicate that path is finished */
146 pPath
->state
=PATH_Closed
;
152 /***********************************************************************
153 * AbortPath16 (GDI.511)
155 BOOL16 WINAPI
AbortPath16(HDC16 hdc
)
157 return (BOOL16
)AbortPath32((HDC32
)hdc
);
161 /******************************************************************************
162 * AbortPath32 [GDI32.1]
163 * Closes and discards paths from device context
166 * Check that SetLastError is being called correctly
169 * hdc [I] Handle to device context
173 BOOL32 WINAPI
AbortPath32( HDC32 hdc
)
177 /* Get pointer to path */
178 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
180 SetLastError(ERROR_INVALID_PARAMETER
);
184 /* Remove all entries from the path */
185 PATH_EmptyPath(pPath
);
191 /***********************************************************************
192 * CloseFigure16 (GDI.513)
194 BOOL16 WINAPI
CloseFigure16(HDC16 hdc
)
196 return (BOOL16
)CloseFigure32((HDC32
)hdc
);
200 /***********************************************************************
201 * CloseFigure32 (GDI32.16)
203 * FIXME: Check that SetLastError is being called correctly
205 BOOL32 WINAPI
CloseFigure32(HDC32 hdc
)
209 /* Get pointer to path */
210 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
212 SetLastError(ERROR_INVALID_PARAMETER
);
216 /* Check that path is open */
217 if(pPath
->state
!=PATH_Open
)
219 SetLastError(ERROR_CAN_NOT_COMPLETE
);
223 /* FIXME: Shouldn't we draw a line to the beginning of the figure? */
225 /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
226 if(pPath
->numEntriesUsed
)
228 pPath
->pFlags
[pPath
->numEntriesUsed
-1]|=PT_CLOSEFIGURE
;
229 pPath
->newStroke
=TRUE
;
236 /***********************************************************************
237 * GetPath16 (GDI.517)
239 INT16 WINAPI
GetPath16(HDC16 hdc
, LPPOINT16 pPoints
, LPBYTE pTypes
,
242 FIXME(gdi
, "(%d,%p,%p): stub\n",hdc
,pPoints
,pTypes
);
248 /***********************************************************************
249 * GetPath32 (GDI32.210)
251 INT32 WINAPI
GetPath32(HDC32 hdc
, LPPOINT32 pPoints
, LPBYTE pTypes
,
256 /* Get pointer to path */
257 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
259 SetLastError(ERROR_INVALID_PARAMETER
);
263 /* Check that path is closed */
264 if(pPath
->state
!=PATH_Closed
)
266 SetLastError(ERROR_CAN_NOT_COMPLETE
);
271 return pPath
->numEntriesUsed
;
272 else if(nSize
<pPath
->numEntriesUsed
)
274 SetLastError(ERROR_INVALID_PARAMETER
);
279 memcpy(pPoints
, pPath
->pPoints
, sizeof(POINT32
)*pPath
->numEntriesUsed
);
280 memcpy(pTypes
, pPath
->pFlags
, sizeof(BYTE
)*pPath
->numEntriesUsed
);
282 /* Convert the points to logical coordinates */
283 if(!DPtoLP32(hdc
, pPoints
, pPath
->numEntriesUsed
))
285 /* FIXME: Is this the correct value? */
286 SetLastError(ERROR_CAN_NOT_COMPLETE
);
290 return pPath
->numEntriesUsed
;
295 /***********************************************************************
296 * PathToRegion32 (GDI32.261)
299 * Check that SetLastError is being called correctly
301 * The documentation does not state this explicitly, but a test under Windows
302 * shows that the region which is returned should be in device coordinates.
304 HRGN32 WINAPI
PathToRegion32(HDC32 hdc
)
309 /* Get pointer to path */
310 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
312 SetLastError(ERROR_INVALID_PARAMETER
);
316 /* Check that path is closed */
317 if(pPath
->state
!=PATH_Closed
)
319 SetLastError(ERROR_CAN_NOT_COMPLETE
);
323 /* FIXME: Should we empty the path even if conversion failed? */
324 if(PATH_PathToRegion(pPath
, GetPolyFillMode32(hdc
), &hrgnRval
))
325 PATH_EmptyPath(pPath
);
333 /***********************************************************************
334 * FillPath32 (GDI32.100)
337 * Check that SetLastError is being called correctly
339 BOOL32 WINAPI
FillPath32(HDC32 hdc
)
342 INT32 mapMode
, graphicsMode
;
343 POINT32 ptViewportExt
, ptViewportOrg
, ptWindowExt
, ptWindowOrg
;
347 /* Get pointer to path */
348 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
350 SetLastError(ERROR_INVALID_PARAMETER
);
354 /* Check that path is closed */
355 if(pPath
->state
!=PATH_Closed
)
357 SetLastError(ERROR_CAN_NOT_COMPLETE
);
361 /* Construct a region from the path and fill it */
362 if(PATH_PathToRegion(pPath
, GetPolyFillMode32(hdc
), &hrgn
))
364 /* Since PaintRgn interprets the region as being in logical coordinates
365 * but the points we store for the path are already in device
366 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
367 * Using SaveDC to save information about the mapping mode / world
368 * transform would be easier but would require more overhead, especially
369 * now that SaveDC saves the current path.
372 /* Save the information about the old mapping mode */
373 mapMode
=GetMapMode32(hdc
);
374 GetViewportExtEx32(hdc
, &ptViewportExt
);
375 GetViewportOrgEx32(hdc
, &ptViewportOrg
);
376 GetWindowExtEx32(hdc
, &ptWindowExt
);
377 GetWindowOrgEx32(hdc
, &ptWindowOrg
);
379 /* Save world transform
380 * NB: The Windows documentation on world transforms would lead one to
381 * believe that this has to be done only in GM_ADVANCED; however, my
382 * tests show that resetting the graphics mode to GM_COMPATIBLE does
383 * not reset the world transform.
385 GetWorldTransform(hdc
, &xform
);
388 SetMapMode32(hdc
, MM_TEXT
);
390 /* Paint the region */
391 PaintRgn32(hdc
, hrgn
);
393 /* Restore the old mapping mode */
394 SetMapMode32(hdc
, mapMode
);
395 SetViewportExtEx32(hdc
, ptViewportExt
.x
, ptViewportExt
.y
, NULL
);
396 SetViewportOrgEx32(hdc
, ptViewportOrg
.x
, ptViewportOrg
.y
, NULL
);
397 SetWindowExtEx32(hdc
, ptWindowExt
.x
, ptWindowExt
.y
, NULL
);
398 SetWindowOrgEx32(hdc
, ptWindowOrg
.x
, ptWindowOrg
.y
, NULL
);
400 /* Go to GM_ADVANCED temporarily to restore the world transform */
401 graphicsMode
=GetGraphicsMode(hdc
);
402 SetGraphicsMode(hdc
, GM_ADVANCED
);
403 SetWorldTransform(hdc
, &xform
);
404 SetGraphicsMode(hdc
, graphicsMode
);
407 PATH_EmptyPath(pPath
);
412 /* FIXME: Should the path be emptied even if conversion failed? */
413 /* PATH_EmptyPath(pPath); */
419 /***********************************************************************
420 * SelectClipPath32 (GDI32.296)
422 * Check that SetLastError is being called correctly
424 BOOL32 WINAPI
SelectClipPath32(HDC32 hdc
, INT32 iMode
)
427 HRGN32 hrgnPath
, hrgnClip
;
430 /* Get pointer to path */
431 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
433 SetLastError(ERROR_INVALID_PARAMETER
);
437 /* Check that path is closed */
438 if(pPath
->state
!=PATH_Closed
)
440 SetLastError(ERROR_CAN_NOT_COMPLETE
);
444 /* Construct a region from the path */
445 if(PATH_PathToRegion(pPath
, GetPolyFillMode32(hdc
), &hrgnPath
))
447 hrgnClip
=CreateRectRgn32(0, 0, 0, 0);
448 if(hrgnClip
==(HRGN32
)0)
452 success
=(GetClipRgn32(hdc
, hrgnClip
)!=-1) &&
453 (CombineRgn32(hrgnClip
, hrgnClip
, hrgnPath
, iMode
)!=ERROR
) &&
454 (SelectClipRgn32(hdc
, hrgnClip
)!=ERROR
);
455 DeleteObject32(hrgnClip
);
458 DeleteObject32(hrgnPath
);
462 PATH_EmptyPath(pPath
);
463 /* FIXME: Should this function delete the path even if it failed? */
472 /***********************************************************************
478 * Initializes the GdiPath structure.
480 void PATH_InitGdiPath(GdiPath
*pPath
)
484 pPath
->state
=PATH_Null
;
487 pPath
->numEntriesUsed
=0;
488 pPath
->numEntriesAllocated
=0;
491 /* PATH_DestroyGdiPath
493 * Destroys a GdiPath structure (frees the memory in the arrays).
495 void PATH_DestroyGdiPath(GdiPath
*pPath
)
499 free(pPath
->pPoints
);
503 /* PATH_AssignGdiPath
505 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
506 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
507 * not just the pointers. Since this means that the arrays in pPathDest may
508 * need to be resized, pPathDest should have been initialized using
509 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
510 * not a copy constructor).
511 * Returns TRUE if successful, else FALSE.
513 BOOL32
PATH_AssignGdiPath(GdiPath
*pPathDest
, const GdiPath
*pPathSrc
)
515 assert(pPathDest
!=NULL
&& pPathSrc
!=NULL
);
517 /* Make sure destination arrays are big enough */
518 if(!PATH_ReserveEntries(pPathDest
, pPathSrc
->numEntriesUsed
))
521 /* Perform the copy operation */
522 memcpy(pPathDest
->pPoints
, pPathSrc
->pPoints
,
523 sizeof(POINT32
)*pPathSrc
->numEntriesUsed
);
524 memcpy(pPathDest
->pFlags
, pPathSrc
->pFlags
,
525 sizeof(INT32
)*pPathSrc
->numEntriesUsed
);
526 pPathDest
->state
=pPathSrc
->state
;
527 pPathDest
->numEntriesUsed
=pPathSrc
->numEntriesUsed
;
528 pPathDest
->newStroke
=pPathSrc
->newStroke
;
535 * Should be called when a MoveTo is performed on a DC that has an
536 * open path. This starts a new stroke. Returns TRUE if successful, else
539 BOOL32
PATH_MoveTo(HDC32 hdc
)
543 /* Get pointer to path */
544 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
547 /* Check that path is open */
548 if(pPath
->state
!=PATH_Open
)
549 /* FIXME: Do we have to call SetLastError? */
552 /* Start a new stroke */
553 pPath
->newStroke
=TRUE
;
560 * Should be called when a LineTo is performed on a DC that has an
561 * open path. This adds a PT_LINETO entry to the path (and possibly
562 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
563 * Returns TRUE if successful, else FALSE.
565 BOOL32
PATH_LineTo(HDC32 hdc
, INT32 x
, INT32 y
)
568 POINT32 point
, pointCurPos
;
570 /* Get pointer to path */
571 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
574 /* Check that path is open */
575 if(pPath
->state
!=PATH_Open
)
578 /* Convert point to device coordinates */
581 if(!LPtoDP32(hdc
, &point
, 1))
584 /* Add a PT_MOVETO if necessary */
587 pPath
->newStroke
=FALSE
;
588 if(!GetCurrentPositionEx32(hdc
, &pointCurPos
) ||
589 !LPtoDP32(hdc
, &pointCurPos
, 1))
591 if(!PATH_AddEntry(pPath
, &pointCurPos
, PT_MOVETO
))
595 /* Add a PT_LINETO entry */
596 return PATH_AddEntry(pPath
, &point
, PT_LINETO
);
601 * Should be called when a call to Rectangle is performed on a DC that has
602 * an open path. Returns TRUE if successful, else FALSE.
604 BOOL32
PATH_Rectangle(HDC32 hdc
, INT32 x1
, INT32 y1
, INT32 x2
, INT32 y2
)
607 POINT32 corners
[2], pointTemp
;
610 /* Get pointer to path */
611 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
614 /* Check that path is open */
615 if(pPath
->state
!=PATH_Open
)
618 /* Convert points to device coordinates */
623 if(!LPtoDP32(hdc
, corners
, 2))
626 /* Make sure first corner is top left and second corner is bottom right */
627 if(corners
[0].x
>corners
[1].x
)
630 corners
[0].x
=corners
[1].x
;
633 if(corners
[0].y
>corners
[1].y
)
636 corners
[0].y
=corners
[1].y
;
640 /* In GM_COMPATIBLE, don't include bottom and right edges */
641 if(GetGraphicsMode(hdc
)==GM_COMPATIBLE
)
647 /* Close any previous figure */
648 if(!CloseFigure32(hdc
))
650 /* The CloseFigure call shouldn't have failed */
655 /* Add four points to the path */
656 pointTemp
.x
=corners
[1].x
;
657 pointTemp
.y
=corners
[0].y
;
658 if(!PATH_AddEntry(pPath
, &pointTemp
, PT_MOVETO
))
660 if(!PATH_AddEntry(pPath
, corners
, PT_LINETO
))
662 pointTemp
.x
=corners
[0].x
;
663 pointTemp
.y
=corners
[1].y
;
664 if(!PATH_AddEntry(pPath
, &pointTemp
, PT_LINETO
))
666 if(!PATH_AddEntry(pPath
, corners
+1, PT_LINETO
))
669 /* Close the rectangle figure */
670 if(!CloseFigure32(hdc
))
672 /* The CloseFigure call shouldn't have failed */
682 * Should be called when a call to Ellipse is performed on a DC that has
683 * an open path. This adds four Bezier splines representing the ellipse
684 * to the path. Returns TRUE if successful, else FALSE.
686 BOOL32
PATH_Ellipse(HDC32 hdc
, INT32 x1
, INT32 y1
, INT32 x2
, INT32 y2
)
688 // TODO: This should probably be revised to call PATH_AngleArc
690 return PATH_Arc(hdc
, x1
, y1
, x2
, y2
, x1
, (y1
+y2
)/2, x1
, (y1
+y2
)/2);
695 * Should be called when a call to Arc is performed on a DC that has
696 * an open path. This adds up to five Bezier splines representing the arc
697 * to the path. Returns TRUE if successful, else FALSE.
699 BOOL32
PATH_Arc(HDC32 hdc
, INT32 x1
, INT32 y1
, INT32 x2
, INT32 y2
,
700 INT32 xStart
, INT32 yStart
, INT32 xEnd
, INT32 yEnd
)
704 double angleStart
, angleEnd
, angleStartQuadrant
, angleEndQuadrant
=0.0;
705 /* Initialize angleEndQuadrant to silence gcc's warning */
707 FLOAT_POINT corners
[2], pointStart
, pointEnd
;
711 /* FIXME: This function should check for all possible error returns */
712 /* FIXME: Do we have to respect newStroke? */
714 /* Get pointer to DC */
715 pDC
=DC_GetDCPtr(hdc
);
719 /* Get pointer to path */
720 if(!PATH_GetPathFromHDC(hdc
, &pPath
))
723 /* Check that path is open */
724 if(pPath
->state
!=PATH_Open
)
727 /* FIXME: Do we have to close the current figure? */
729 /* Check for zero height / width */
730 /* FIXME: Only in GM_COMPATIBLE? */
734 /* Convert points to device coordinates */
735 corners
[0].x
=(FLOAT
)x1
;
736 corners
[0].y
=(FLOAT
)y1
;
737 corners
[1].x
=(FLOAT
)x2
;
738 corners
[1].y
=(FLOAT
)y2
;
739 pointStart
.x
=(FLOAT
)xStart
;
740 pointStart
.y
=(FLOAT
)yStart
;
741 pointEnd
.x
=(FLOAT
)xEnd
;
742 pointEnd
.y
=(FLOAT
)yEnd
;
743 INTERNAL_LPTODP_FLOAT(pDC
, corners
);
744 INTERNAL_LPTODP_FLOAT(pDC
, corners
+1);
745 INTERNAL_LPTODP_FLOAT(pDC
, &pointStart
);
746 INTERNAL_LPTODP_FLOAT(pDC
, &pointEnd
);
748 /* Make sure first corner is top left and second corner is bottom right */
749 if(corners
[0].x
>corners
[1].x
)
752 corners
[0].x
=corners
[1].x
;
755 if(corners
[0].y
>corners
[1].y
)
758 corners
[0].y
=corners
[1].y
;
762 /* Compute start and end angle */
763 PATH_NormalizePoint(corners
, &pointStart
, &x
, &y
);
764 angleStart
=atan2(y
, x
);
765 PATH_NormalizePoint(corners
, &pointEnd
, &x
, &y
);
766 angleEnd
=atan2(y
, x
);
768 /* Make sure the end angle is "on the right side" of the start angle */
769 if(GetArcDirection32(hdc
)==AD_CLOCKWISE
)
771 if(angleEnd
<=angleStart
)
774 assert(angleEnd
>=angleStart
);
779 if(angleEnd
>=angleStart
)
782 assert(angleEnd
<=angleStart
);
786 /* In GM_COMPATIBLE, don't include bottom and right edges */
787 if(GetGraphicsMode(hdc
)==GM_COMPATIBLE
)
793 /* Add the arc to the path with one Bezier spline per quadrant that the
799 /* Determine the start and end angles for this quadrant */
802 angleStartQuadrant
=angleStart
;
803 if(GetArcDirection32(hdc
)==AD_CLOCKWISE
)
804 angleEndQuadrant
=(floor(angleStart
/M_PI_2
)+1.0)*M_PI_2
;
806 angleEndQuadrant
=(ceil(angleStart
/M_PI_2
)-1.0)*M_PI_2
;
810 angleStartQuadrant
=angleEndQuadrant
;
811 if(GetArcDirection32(hdc
)==AD_CLOCKWISE
)
812 angleEndQuadrant
+=M_PI_2
;
814 angleEndQuadrant
-=M_PI_2
;
817 /* Have we reached the last part of the arc? */
818 if((GetArcDirection32(hdc
)==AD_CLOCKWISE
&&
819 angleEnd
<angleEndQuadrant
) ||
820 (GetArcDirection32(hdc
)==AD_COUNTERCLOCKWISE
&&
821 angleEnd
>angleEndQuadrant
))
823 /* Adjust the end angle for this quadrant */
824 angleEndQuadrant
=angleEnd
;
828 /* Add the Bezier spline to the path */
829 PATH_DoArcPart(pPath
, corners
, angleStartQuadrant
, angleEndQuadrant
,
837 /***********************************************************************
843 * Creates a region from the specified path using the specified polygon
844 * filling mode. The path is left unchanged. A handle to the region that
845 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
846 * error occurs, SetLastError is called with the appropriate value and
849 static BOOL32
PATH_PathToRegion(const GdiPath
*pPath
, INT32 nPolyFillMode
,
852 int numStrokes
, iStroke
, i
;
853 INT32
*pNumPointsInStroke
;
859 /* FIXME: What happens when number of points is zero? */
861 /* First pass: Find out how many strokes there are in the path */
862 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
864 for(i
=0; i
<pPath
->numEntriesUsed
; i
++)
865 if((pPath
->pFlags
[i
] & ~PT_CLOSEFIGURE
) == PT_MOVETO
)
868 /* Allocate memory for number-of-points-in-stroke array */
869 pNumPointsInStroke
=(int *)malloc(sizeof(int)*numStrokes
);
870 if(!pNumPointsInStroke
)
872 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
876 /* Second pass: remember number of points in each polygon */
877 iStroke
=-1; /* Will get incremented to 0 at beginning of first stroke */
878 for(i
=0; i
<pPath
->numEntriesUsed
; i
++)
880 /* Is this the beginning of a new stroke? */
881 if((pPath
->pFlags
[i
] & ~PT_CLOSEFIGURE
) == PT_MOVETO
)
884 pNumPointsInStroke
[iStroke
]=0;
887 pNumPointsInStroke
[iStroke
]++;
890 /* Create a region from the strokes */
891 hrgn
=CreatePolyPolygonRgn32(pPath
->pPoints
, pNumPointsInStroke
,
892 numStrokes
, nPolyFillMode
);
895 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
899 /* Free memory for number-of-points-in-stroke array */
900 free(pNumPointsInStroke
);
909 * Removes all entries from the path and sets the path state to PATH_Null.
911 static void PATH_EmptyPath(GdiPath
*pPath
)
915 pPath
->state
=PATH_Null
;
916 pPath
->numEntriesUsed
=0;
921 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
922 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
923 * successful, FALSE otherwise (e.g. if not enough memory was available).
925 BOOL32
PATH_AddEntry(GdiPath
*pPath
, const POINT32
*pPoint
, BYTE flags
)
929 /* FIXME: If newStroke is true, perhaps we want to check that we're
930 * getting a PT_MOVETO
933 /* Check that path is open */
934 if(pPath
->state
!=PATH_Open
)
937 /* Reserve enough memory for an extra path entry */
938 if(!PATH_ReserveEntries(pPath
, pPath
->numEntriesUsed
+1))
941 /* Store information in path entry */
942 pPath
->pPoints
[pPath
->numEntriesUsed
]=*pPoint
;
943 pPath
->pFlags
[pPath
->numEntriesUsed
]=flags
;
945 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
946 if((flags
& PT_CLOSEFIGURE
) == PT_CLOSEFIGURE
)
947 pPath
->newStroke
=TRUE
;
949 /* Increment entry count */
950 pPath
->numEntriesUsed
++;
955 /* PATH_ReserveEntries
957 * Ensures that at least "numEntries" entries (for points and flags) have
958 * been allocated; allocates larger arrays and copies the existing entries
959 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
961 static BOOL32
PATH_ReserveEntries(GdiPath
*pPath
, INT32 numEntries
)
963 INT32 numEntriesToAllocate
;
968 assert(numEntries
>=0);
970 /* Do we have to allocate more memory? */
971 if(numEntries
> pPath
->numEntriesAllocated
)
973 /* Find number of entries to allocate. We let the size of the array
974 * grow exponentially, since that will guarantee linear time
976 if(pPath
->numEntriesAllocated
)
978 numEntriesToAllocate
=pPath
->numEntriesAllocated
;
979 while(numEntriesToAllocate
<numEntries
)
980 numEntriesToAllocate
=numEntriesToAllocate
*GROW_FACTOR_NUMER
/
984 numEntriesToAllocate
=NUM_ENTRIES_INITIAL
;
986 /* Allocate new arrays */
987 pPointsNew
=(POINT32
*)malloc(numEntriesToAllocate
* sizeof(POINT32
));
990 pFlagsNew
=(BYTE
*)malloc(numEntriesToAllocate
* sizeof(BYTE
));
997 /* Copy old arrays to new arrays and discard old arrays */
1000 assert(pPath
->pFlags
);
1002 memcpy(pPointsNew
, pPath
->pPoints
,
1003 sizeof(POINT32
)*pPath
->numEntriesUsed
);
1004 memcpy(pFlagsNew
, pPath
->pFlags
,
1005 sizeof(BYTE
)*pPath
->numEntriesUsed
);
1007 free(pPath
->pPoints
);
1008 free(pPath
->pFlags
);
1010 pPath
->pPoints
=pPointsNew
;
1011 pPath
->pFlags
=pFlagsNew
;
1012 pPath
->numEntriesAllocated
=numEntriesToAllocate
;
1018 /* PATH_GetPathFromHDC
1020 * Retrieves a pointer to the GdiPath structure contained in an HDC and
1021 * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
1023 static BOOL32
PATH_GetPathFromHDC(HDC32 hdc
, GdiPath
**ppPath
)
1027 pDC
=DC_GetDCPtr(hdc
);
1030 *ppPath
=&pDC
->w
.path
;
1039 * Creates a Bezier spline that corresponds to part of an arc and appends the
1040 * corresponding points to the path. The start and end angles are passed in
1041 * "angleStart" and "angleEnd"; these angles should span a quarter circle
1042 * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
1043 * point is added to the path; otherwise, it is assumed that the current
1044 * position is equal to the first control point.
1046 static BOOL32
PATH_DoArcPart(GdiPath
*pPath
, FLOAT_POINT corners
[],
1047 double angleStart
, double angleEnd
, BOOL32 addMoveTo
)
1049 double halfAngle
, a
;
1050 double xNorm
[4], yNorm
[4];
1054 assert(fabs(angleEnd
-angleStart
)<=M_PI_2
);
1056 /* FIXME: Is there an easier way of computing this? */
1058 /* Compute control points */
1059 halfAngle
=(angleEnd
-angleStart
)/2.0;
1060 if(fabs(halfAngle
)>1e-8)
1062 a
=4.0/3.0*(1-cos(halfAngle
))/sin(halfAngle
);
1063 xNorm
[0]=cos(angleStart
);
1064 yNorm
[0]=sin(angleStart
);
1065 xNorm
[1]=xNorm
[0] - a
*yNorm
[0];
1066 yNorm
[1]=yNorm
[0] + a
*xNorm
[0];
1067 xNorm
[3]=cos(angleEnd
);
1068 yNorm
[3]=sin(angleEnd
);
1069 xNorm
[2]=xNorm
[3] + a
*yNorm
[3];
1070 yNorm
[2]=yNorm
[3] - a
*xNorm
[3];
1075 xNorm
[i
]=cos(angleStart
);
1076 yNorm
[i
]=sin(angleStart
);
1079 /* Add starting point to path if desired */
1082 PATH_ScaleNormalizedPoint(corners
, xNorm
[0], yNorm
[0], &point
);
1083 if(!PATH_AddEntry(pPath
, &point
, PT_MOVETO
))
1087 /* Add remaining control points */
1090 PATH_ScaleNormalizedPoint(corners
, xNorm
[i
], yNorm
[i
], &point
);
1091 if(!PATH_AddEntry(pPath
, &point
, PT_BEZIERTO
))
1098 /* PATH_ScaleNormalizedPoint
1100 * Scales a normalized point (x, y) with respect to the box whose corners are
1101 * passed in "corners". The point is stored in "*pPoint". The normalized
1102 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1103 * (1.0, 1.0) correspond to corners[1].
1105 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners
[], double x
,
1106 double y
, POINT32
*pPoint
)
1108 pPoint
->x
=GDI_ROUND( (double)corners
[0].x
+
1109 (double)(corners
[1].x
-corners
[0].x
)*0.5*(x
+1.0) );
1110 pPoint
->y
=GDI_ROUND( (double)corners
[0].y
+
1111 (double)(corners
[1].y
-corners
[0].y
)*0.5*(y
+1.0) );
1114 /* PATH_NormalizePoint
1116 * Normalizes a point with respect to the box whose corners are passed in
1117 * "corners". The normalized coordinates are stored in "*pX" and "*pY".
1119 static void PATH_NormalizePoint(FLOAT_POINT corners
[],
1120 const FLOAT_POINT
*pPoint
,
1121 double *pX
, double *pY
)
1123 *pX
=(double)(pPoint
->x
-corners
[0].x
)/(double)(corners
[1].x
-corners
[0].x
) *
1125 *pY
=(double)(pPoint
->y
-corners
[0].y
)/(double)(corners
[1].y
-corners
[0].y
) *