Corrected operand sizes for the "enter" instruction.
[wine.git] / graphics / path.c
blobb7023bfc503068adf0f4b7c857886d597c0a47c4
1 /*
2 * Graphics paths (BeginPath, EndPath etc.)
4 * Copyright 1997, 1998 Martin Boehme
5 */
7 #include <assert.h>
8 #include <malloc.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 * BeginPath32 (GDI32.9)
97 BOOL WINAPI BeginPath(HDC hdc)
99 GdiPath *pPath;
101 /* Get pointer to path */
102 if(!PATH_GetPathFromHDC(hdc, &pPath))
104 SetLastError(ERROR_INVALID_HANDLE);
105 return FALSE;
108 /* If path is already open, do nothing */
109 if(pPath->state==PATH_Open)
110 return TRUE;
112 /* Make sure that path is empty */
113 PATH_EmptyPath(pPath);
115 /* Initialize variables for new path */
116 pPath->newStroke=TRUE;
117 pPath->state=PATH_Open;
119 return TRUE;
123 /***********************************************************************
124 * EndPath16 (GDI.514)
126 BOOL16 WINAPI EndPath16(HDC16 hdc)
128 return (BOOL16)EndPath((HDC)hdc);
132 /***********************************************************************
133 * EndPath32 (GDI32.78)
135 BOOL WINAPI EndPath(HDC hdc)
137 GdiPath *pPath;
139 /* Get pointer to path */
140 if(!PATH_GetPathFromHDC(hdc, &pPath))
142 SetLastError(ERROR_INVALID_HANDLE);
143 return FALSE;
146 /* Check that path is currently being constructed */
147 if(pPath->state!=PATH_Open)
149 SetLastError(ERROR_CAN_NOT_COMPLETE);
150 return FALSE;
153 /* Set flag to indicate that path is finished */
154 pPath->state=PATH_Closed;
156 return TRUE;
160 /***********************************************************************
161 * AbortPath16 (GDI.511)
163 BOOL16 WINAPI AbortPath16(HDC16 hdc)
165 return (BOOL16)AbortPath((HDC)hdc);
169 /******************************************************************************
170 * AbortPath32 [GDI32.1]
171 * Closes and discards paths from device context
173 * NOTES
174 * Check that SetLastError is being called correctly
176 * PARAMS
177 * hdc [I] Handle to device context
179 * RETURNS STD
181 BOOL WINAPI AbortPath( HDC hdc )
183 GdiPath *pPath;
185 /* Get pointer to path */
186 if(!PATH_GetPathFromHDC(hdc, &pPath))
188 SetLastError(ERROR_INVALID_PARAMETER);
189 return FALSE;
192 /* Remove all entries from the path */
193 PATH_EmptyPath(pPath);
195 return TRUE;
199 /***********************************************************************
200 * CloseFigure16 (GDI.513)
202 BOOL16 WINAPI CloseFigure16(HDC16 hdc)
204 return (BOOL16)CloseFigure((HDC)hdc);
208 /***********************************************************************
209 * CloseFigure32 (GDI32.16)
211 * FIXME: Check that SetLastError is being called correctly
213 BOOL WINAPI CloseFigure(HDC hdc)
215 GdiPath *pPath;
217 /* Get pointer to path */
218 if(!PATH_GetPathFromHDC(hdc, &pPath))
220 SetLastError(ERROR_INVALID_PARAMETER);
221 return FALSE;
224 /* Check that path is open */
225 if(pPath->state!=PATH_Open)
227 SetLastError(ERROR_CAN_NOT_COMPLETE);
228 return FALSE;
231 /* FIXME: Shouldn't we draw a line to the beginning of the figure? */
233 /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
234 if(pPath->numEntriesUsed)
236 pPath->pFlags[pPath->numEntriesUsed-1]|=PT_CLOSEFIGURE;
237 pPath->newStroke=TRUE;
240 return TRUE;
244 /***********************************************************************
245 * GetPath16 (GDI.517)
247 INT16 WINAPI GetPath16(HDC16 hdc, LPPOINT16 pPoints, LPBYTE pTypes,
248 INT16 nSize)
250 FIXME("(%d,%p,%p): stub\n",hdc,pPoints,pTypes);
252 return 0;
256 /***********************************************************************
257 * GetPath32 (GDI32.210)
259 INT WINAPI GetPath(HDC hdc, LPPOINT pPoints, LPBYTE pTypes,
260 INT nSize)
262 GdiPath *pPath;
264 /* Get pointer to path */
265 if(!PATH_GetPathFromHDC(hdc, &pPath))
267 SetLastError(ERROR_INVALID_PARAMETER);
268 return -1;
271 /* Check that path is closed */
272 if(pPath->state!=PATH_Closed)
274 SetLastError(ERROR_CAN_NOT_COMPLETE);
275 return -1;
278 if(nSize==0)
279 return pPath->numEntriesUsed;
280 else if(nSize<pPath->numEntriesUsed)
282 SetLastError(ERROR_INVALID_PARAMETER);
283 return -1;
285 else
287 memcpy(pPoints, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
288 memcpy(pTypes, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
290 /* Convert the points to logical coordinates */
291 if(!DPtoLP(hdc, pPoints, pPath->numEntriesUsed))
293 /* FIXME: Is this the correct value? */
294 SetLastError(ERROR_CAN_NOT_COMPLETE);
295 return -1;
298 return pPath->numEntriesUsed;
302 /***********************************************************************
303 * PathToRegion16 (GDI.518)
305 HRGN16 WINAPI PathToRegion16(HDC16 hdc)
307 return (HRGN16) PathToRegion((HDC) hdc);
310 /***********************************************************************
311 * PathToRegion32 (GDI32.261)
313 * FIXME
314 * Check that SetLastError is being called correctly
316 * The documentation does not state this explicitly, but a test under Windows
317 * shows that the region which is returned should be in device coordinates.
319 HRGN WINAPI PathToRegion(HDC hdc)
321 GdiPath *pPath;
322 HRGN hrgnRval;
324 /* Get pointer to path */
325 if(!PATH_GetPathFromHDC(hdc, &pPath))
327 SetLastError(ERROR_INVALID_PARAMETER);
328 return 0;
331 /* Check that path is closed */
332 if(pPath->state!=PATH_Closed)
334 SetLastError(ERROR_CAN_NOT_COMPLETE);
335 return 0;
338 /* FIXME: Should we empty the path even if conversion failed? */
339 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnRval))
340 PATH_EmptyPath(pPath);
341 else
342 hrgnRval=0;
344 return hrgnRval;
347 /***********************************************************************
348 * FillPath16 (GDI.515)
350 BOOL16 WINAPI FillPath16(HDC16 hdc)
352 return (BOOL16) FillPath((HDC) hdc);
355 /***********************************************************************
356 * FillPath32 (GDI32.100)
358 * FIXME
359 * Check that SetLastError is being called correctly
361 BOOL WINAPI FillPath(HDC hdc)
363 GdiPath *pPath;
364 INT mapMode, graphicsMode;
365 SIZE ptViewportExt, ptWindowExt;
366 POINT ptViewportOrg, ptWindowOrg;
367 XFORM xform;
368 HRGN hrgn;
370 /* Get pointer to path */
371 if(!PATH_GetPathFromHDC(hdc, &pPath))
373 SetLastError(ERROR_INVALID_PARAMETER);
374 return FALSE;
377 /* Check that path is closed */
378 if(pPath->state!=PATH_Closed)
380 SetLastError(ERROR_CAN_NOT_COMPLETE);
381 return FALSE;
384 /* Construct a region from the path and fill it */
385 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgn))
387 /* Since PaintRgn interprets the region as being in logical coordinates
388 * but the points we store for the path are already in device
389 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
390 * Using SaveDC to save information about the mapping mode / world
391 * transform would be easier but would require more overhead, especially
392 * now that SaveDC saves the current path.
395 /* Save the information about the old mapping mode */
396 mapMode=GetMapMode(hdc);
397 GetViewportExtEx(hdc, &ptViewportExt);
398 GetViewportOrgEx(hdc, &ptViewportOrg);
399 GetWindowExtEx(hdc, &ptWindowExt);
400 GetWindowOrgEx(hdc, &ptWindowOrg);
402 /* Save world transform
403 * NB: The Windows documentation on world transforms would lead one to
404 * believe that this has to be done only in GM_ADVANCED; however, my
405 * tests show that resetting the graphics mode to GM_COMPATIBLE does
406 * not reset the world transform.
408 GetWorldTransform(hdc, &xform);
410 /* Set MM_TEXT */
411 SetMapMode(hdc, MM_TEXT);
413 /* Paint the region */
414 PaintRgn(hdc, hrgn);
416 /* Restore the old mapping mode */
417 SetMapMode(hdc, mapMode);
418 SetViewportExtEx(hdc, ptViewportExt.cx, ptViewportExt.cy, NULL);
419 SetViewportOrgEx(hdc, ptViewportOrg.x, ptViewportOrg.y, NULL);
420 SetWindowExtEx(hdc, ptWindowExt.cx, ptWindowExt.cy, NULL);
421 SetWindowOrgEx(hdc, ptWindowOrg.x, ptWindowOrg.y, NULL);
423 /* Go to GM_ADVANCED temporarily to restore the world transform */
424 graphicsMode=GetGraphicsMode(hdc);
425 SetGraphicsMode(hdc, GM_ADVANCED);
426 SetWorldTransform(hdc, &xform);
427 SetGraphicsMode(hdc, graphicsMode);
429 /* Empty the path */
430 PATH_EmptyPath(pPath);
431 return TRUE;
433 else
435 /* FIXME: Should the path be emptied even if conversion failed? */
436 /* PATH_EmptyPath(pPath); */
437 return FALSE;
441 /***********************************************************************
442 * SelectClipPath16 (GDI.519)
444 BOOL16 WINAPI SelectClipPath16(HDC16 hdc, INT16 iMode)
446 return (BOOL16) SelectClipPath((HDC) hdc, iMode);
449 /***********************************************************************
450 * SelectClipPath32 (GDI32.296)
451 * FIXME
452 * Check that SetLastError is being called correctly
454 BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
456 GdiPath *pPath;
457 HRGN hrgnPath;
458 BOOL success;
460 /* Get pointer to path */
461 if(!PATH_GetPathFromHDC(hdc, &pPath))
463 SetLastError(ERROR_INVALID_PARAMETER);
464 return FALSE;
467 /* Check that path is closed */
468 if(pPath->state!=PATH_Closed)
470 SetLastError(ERROR_CAN_NOT_COMPLETE);
471 return FALSE;
474 /* Construct a region from the path */
475 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnPath))
477 success = ExtSelectClipRgn( hdc, hrgnPath, iMode ) != ERROR;
478 DeleteObject(hrgnPath);
480 /* Empty the path */
481 if(success)
482 PATH_EmptyPath(pPath);
483 /* FIXME: Should this function delete the path even if it failed? */
485 return success;
487 else
488 return FALSE;
492 /***********************************************************************
493 * Exported functions
496 /* PATH_InitGdiPath
498 * Initializes the GdiPath structure.
500 void PATH_InitGdiPath(GdiPath *pPath)
502 assert(pPath!=NULL);
504 pPath->state=PATH_Null;
505 pPath->pPoints=NULL;
506 pPath->pFlags=NULL;
507 pPath->numEntriesUsed=0;
508 pPath->numEntriesAllocated=0;
511 /* PATH_DestroyGdiPath
513 * Destroys a GdiPath structure (frees the memory in the arrays).
515 void PATH_DestroyGdiPath(GdiPath *pPath)
517 assert(pPath!=NULL);
519 free(pPath->pPoints);
520 free(pPath->pFlags);
523 /* PATH_AssignGdiPath
525 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
526 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
527 * not just the pointers. Since this means that the arrays in pPathDest may
528 * need to be resized, pPathDest should have been initialized using
529 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
530 * not a copy constructor).
531 * Returns TRUE if successful, else FALSE.
533 BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
535 assert(pPathDest!=NULL && pPathSrc!=NULL);
537 /* Make sure destination arrays are big enough */
538 if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
539 return FALSE;
541 /* Perform the copy operation */
542 memcpy(pPathDest->pPoints, pPathSrc->pPoints,
543 sizeof(POINT)*pPathSrc->numEntriesUsed);
544 memcpy(pPathDest->pFlags, pPathSrc->pFlags,
545 sizeof(INT)*pPathSrc->numEntriesUsed);
546 pPathDest->state=pPathSrc->state;
547 pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
548 pPathDest->newStroke=pPathSrc->newStroke;
550 return TRUE;
553 /* PATH_MoveTo
555 * Should be called when a MoveTo is performed on a DC that has an
556 * open path. This starts a new stroke. Returns TRUE if successful, else
557 * FALSE.
559 BOOL PATH_MoveTo(HDC hdc)
561 GdiPath *pPath;
563 /* Get pointer to path */
564 if(!PATH_GetPathFromHDC(hdc, &pPath))
565 return FALSE;
567 /* Check that path is open */
568 if(pPath->state!=PATH_Open)
569 /* FIXME: Do we have to call SetLastError? */
570 return FALSE;
572 /* Start a new stroke */
573 pPath->newStroke=TRUE;
575 return TRUE;
578 /* PATH_LineTo
580 * Should be called when a LineTo is performed on a DC that has an
581 * open path. This adds a PT_LINETO entry to the path (and possibly
582 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
583 * Returns TRUE if successful, else FALSE.
585 BOOL PATH_LineTo(HDC hdc, INT x, INT y)
587 GdiPath *pPath;
588 POINT point, pointCurPos;
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 return FALSE;
598 /* Convert point to device coordinates */
599 point.x=x;
600 point.y=y;
601 if(!LPtoDP(hdc, &point, 1))
602 return FALSE;
604 /* Add a PT_MOVETO if necessary */
605 if(pPath->newStroke)
607 pPath->newStroke=FALSE;
608 if(!GetCurrentPositionEx(hdc, &pointCurPos) ||
609 !LPtoDP(hdc, &pointCurPos, 1))
610 return FALSE;
611 if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
612 return FALSE;
615 /* Add a PT_LINETO entry */
616 return PATH_AddEntry(pPath, &point, PT_LINETO);
619 /* PATH_Rectangle
621 * Should be called when a call to Rectangle is performed on a DC that has
622 * an open path. Returns TRUE if successful, else FALSE.
624 BOOL PATH_Rectangle(HDC hdc, INT x1, INT y1, INT x2, INT y2)
626 GdiPath *pPath;
627 POINT corners[2], pointTemp;
628 INT temp;
630 /* Get pointer to path */
631 if(!PATH_GetPathFromHDC(hdc, &pPath))
632 return FALSE;
634 /* Check that path is open */
635 if(pPath->state!=PATH_Open)
636 return FALSE;
638 /* Convert points to device coordinates */
639 corners[0].x=x1;
640 corners[0].y=y1;
641 corners[1].x=x2;
642 corners[1].y=y2;
643 if(!LPtoDP(hdc, corners, 2))
644 return FALSE;
646 /* Make sure first corner is top left and second corner is bottom right */
647 if(corners[0].x>corners[1].x)
649 temp=corners[0].x;
650 corners[0].x=corners[1].x;
651 corners[1].x=temp;
653 if(corners[0].y>corners[1].y)
655 temp=corners[0].y;
656 corners[0].y=corners[1].y;
657 corners[1].y=temp;
660 /* In GM_COMPATIBLE, don't include bottom and right edges */
661 if(GetGraphicsMode(hdc)==GM_COMPATIBLE)
663 corners[1].x--;
664 corners[1].y--;
667 /* Close any previous figure */
668 if(!CloseFigure(hdc))
670 /* The CloseFigure call shouldn't have failed */
671 assert(FALSE);
672 return FALSE;
675 /* Add four points to the path */
676 pointTemp.x=corners[1].x;
677 pointTemp.y=corners[0].y;
678 if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
679 return FALSE;
680 if(!PATH_AddEntry(pPath, corners, PT_LINETO))
681 return FALSE;
682 pointTemp.x=corners[0].x;
683 pointTemp.y=corners[1].y;
684 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
685 return FALSE;
686 if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
687 return FALSE;
689 /* Close the rectangle figure */
690 if(!CloseFigure(hdc))
692 /* The CloseFigure call shouldn't have failed */
693 assert(FALSE);
694 return FALSE;
697 return TRUE;
700 /* PATH_Ellipse
702 * Should be called when a call to Ellipse is performed on a DC that has
703 * an open path. This adds four Bezier splines representing the ellipse
704 * to the path. Returns TRUE if successful, else FALSE.
706 BOOL PATH_Ellipse(HDC hdc, INT x1, INT y1, INT x2, INT y2)
708 /* TODO: This should probably be revised to call PATH_AngleArc */
709 /* (once it exists) */
710 return PATH_Arc(hdc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2);
713 /* PATH_Arc
715 * Should be called when a call to Arc is performed on a DC that has
716 * an open path. This adds up to five Bezier splines representing the arc
717 * to the path. Returns TRUE if successful, else FALSE.
719 BOOL PATH_Arc(HDC hdc, INT x1, INT y1, INT x2, INT y2,
720 INT xStart, INT yStart, INT xEnd, INT yEnd)
722 GdiPath *pPath;
723 DC *pDC;
724 double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
725 /* Initialize angleEndQuadrant to silence gcc's warning */
726 double x, y;
727 FLOAT_POINT corners[2], pointStart, pointEnd;
728 BOOL start, end;
729 INT temp;
731 /* FIXME: This function should check for all possible error returns */
732 /* FIXME: Do we have to respect newStroke? */
734 /* Get pointer to DC */
735 pDC=DC_GetDCPtr(hdc);
736 if(pDC==NULL)
737 return FALSE;
739 /* Get pointer to path */
740 if(!PATH_GetPathFromHDC(hdc, &pPath))
741 return FALSE;
743 /* Check that path is open */
744 if(pPath->state!=PATH_Open)
745 return FALSE;
747 /* FIXME: Do we have to close the current figure? */
749 /* Check for zero height / width */
750 /* FIXME: Only in GM_COMPATIBLE? */
751 if(x1==x2 || y1==y2)
752 return TRUE;
754 /* Convert points to device coordinates */
755 corners[0].x=(FLOAT)x1;
756 corners[0].y=(FLOAT)y1;
757 corners[1].x=(FLOAT)x2;
758 corners[1].y=(FLOAT)y2;
759 pointStart.x=(FLOAT)xStart;
760 pointStart.y=(FLOAT)yStart;
761 pointEnd.x=(FLOAT)xEnd;
762 pointEnd.y=(FLOAT)yEnd;
763 INTERNAL_LPTODP_FLOAT(pDC, corners);
764 INTERNAL_LPTODP_FLOAT(pDC, corners+1);
765 INTERNAL_LPTODP_FLOAT(pDC, &pointStart);
766 INTERNAL_LPTODP_FLOAT(pDC, &pointEnd);
768 /* Make sure first corner is top left and second corner is bottom right */
769 if(corners[0].x>corners[1].x)
771 temp=corners[0].x;
772 corners[0].x=corners[1].x;
773 corners[1].x=temp;
775 if(corners[0].y>corners[1].y)
777 temp=corners[0].y;
778 corners[0].y=corners[1].y;
779 corners[1].y=temp;
782 /* Compute start and end angle */
783 PATH_NormalizePoint(corners, &pointStart, &x, &y);
784 angleStart=atan2(y, x);
785 PATH_NormalizePoint(corners, &pointEnd, &x, &y);
786 angleEnd=atan2(y, x);
788 /* Make sure the end angle is "on the right side" of the start angle */
789 if(GetArcDirection(hdc)==AD_CLOCKWISE)
791 if(angleEnd<=angleStart)
793 angleEnd+=2*M_PI;
794 assert(angleEnd>=angleStart);
797 else
799 if(angleEnd>=angleStart)
801 angleEnd-=2*M_PI;
802 assert(angleEnd<=angleStart);
806 /* In GM_COMPATIBLE, don't include bottom and right edges */
807 if(GetGraphicsMode(hdc)==GM_COMPATIBLE)
809 corners[1].x--;
810 corners[1].y--;
813 /* Add the arc to the path with one Bezier spline per quadrant that the
814 * arc spans */
815 start=TRUE;
816 end=FALSE;
819 /* Determine the start and end angles for this quadrant */
820 if(start)
822 angleStartQuadrant=angleStart;
823 if(GetArcDirection(hdc)==AD_CLOCKWISE)
824 angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
825 else
826 angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
828 else
830 angleStartQuadrant=angleEndQuadrant;
831 if(GetArcDirection(hdc)==AD_CLOCKWISE)
832 angleEndQuadrant+=M_PI_2;
833 else
834 angleEndQuadrant-=M_PI_2;
837 /* Have we reached the last part of the arc? */
838 if((GetArcDirection(hdc)==AD_CLOCKWISE &&
839 angleEnd<angleEndQuadrant) ||
840 (GetArcDirection(hdc)==AD_COUNTERCLOCKWISE &&
841 angleEnd>angleEndQuadrant))
843 /* Adjust the end angle for this quadrant */
844 angleEndQuadrant=angleEnd;
845 end=TRUE;
848 /* Add the Bezier spline to the path */
849 PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant,
850 start);
851 start=FALSE;
852 } while(!end);
854 return TRUE;
857 /***********************************************************************
858 * Internal functions
861 /* PATH_PathToRegion
863 * Creates a region from the specified path using the specified polygon
864 * filling mode. The path is left unchanged. A handle to the region that
865 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
866 * error occurs, SetLastError is called with the appropriate value and
867 * FALSE is returned.
869 static BOOL PATH_PathToRegion(const GdiPath *pPath, INT nPolyFillMode,
870 HRGN *pHrgn)
872 int numStrokes, iStroke, i;
873 INT *pNumPointsInStroke;
874 HRGN hrgn;
876 assert(pPath!=NULL);
877 assert(pHrgn!=NULL);
879 /* FIXME: What happens when number of points is zero? */
881 /* First pass: Find out how many strokes there are in the path */
882 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
883 numStrokes=0;
884 for(i=0; i<pPath->numEntriesUsed; i++)
885 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
886 numStrokes++;
888 /* Allocate memory for number-of-points-in-stroke array */
889 pNumPointsInStroke=(int *)malloc(sizeof(int)*numStrokes);
890 if(!pNumPointsInStroke)
892 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
893 return FALSE;
896 /* Second pass: remember number of points in each polygon */
897 iStroke=-1; /* Will get incremented to 0 at beginning of first stroke */
898 for(i=0; i<pPath->numEntriesUsed; i++)
900 /* Is this the beginning of a new stroke? */
901 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
903 iStroke++;
904 pNumPointsInStroke[iStroke]=0;
907 pNumPointsInStroke[iStroke]++;
910 /* Create a region from the strokes */
911 hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
912 numStrokes, nPolyFillMode);
913 if(hrgn==(HRGN)0)
915 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
916 return FALSE;
919 /* Free memory for number-of-points-in-stroke array */
920 free(pNumPointsInStroke);
922 /* Success! */
923 *pHrgn=hrgn;
924 return TRUE;
927 /* PATH_EmptyPath
929 * Removes all entries from the path and sets the path state to PATH_Null.
931 static void PATH_EmptyPath(GdiPath *pPath)
933 assert(pPath!=NULL);
935 pPath->state=PATH_Null;
936 pPath->numEntriesUsed=0;
939 /* PATH_AddEntry
941 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
942 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
943 * successful, FALSE otherwise (e.g. if not enough memory was available).
945 BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint, BYTE flags)
947 assert(pPath!=NULL);
949 /* FIXME: If newStroke is true, perhaps we want to check that we're
950 * getting a PT_MOVETO
953 /* Check that path is open */
954 if(pPath->state!=PATH_Open)
955 return FALSE;
957 /* Reserve enough memory for an extra path entry */
958 if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
959 return FALSE;
961 /* Store information in path entry */
962 pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
963 pPath->pFlags[pPath->numEntriesUsed]=flags;
965 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
966 if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
967 pPath->newStroke=TRUE;
969 /* Increment entry count */
970 pPath->numEntriesUsed++;
972 return TRUE;
975 /* PATH_ReserveEntries
977 * Ensures that at least "numEntries" entries (for points and flags) have
978 * been allocated; allocates larger arrays and copies the existing entries
979 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
981 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries)
983 INT numEntriesToAllocate;
984 POINT *pPointsNew;
985 BYTE *pFlagsNew;
987 assert(pPath!=NULL);
988 assert(numEntries>=0);
990 /* Do we have to allocate more memory? */
991 if(numEntries > pPath->numEntriesAllocated)
993 /* Find number of entries to allocate. We let the size of the array
994 * grow exponentially, since that will guarantee linear time
995 * complexity. */
996 if(pPath->numEntriesAllocated)
998 numEntriesToAllocate=pPath->numEntriesAllocated;
999 while(numEntriesToAllocate<numEntries)
1000 numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/
1001 GROW_FACTOR_DENOM;
1003 else
1004 numEntriesToAllocate=NUM_ENTRIES_INITIAL;
1006 /* Allocate new arrays */
1007 pPointsNew=(POINT *)malloc(numEntriesToAllocate * sizeof(POINT));
1008 if(!pPointsNew)
1009 return FALSE;
1010 pFlagsNew=(BYTE *)malloc(numEntriesToAllocate * sizeof(BYTE));
1011 if(!pFlagsNew)
1013 free(pPointsNew);
1014 return FALSE;
1017 /* Copy old arrays to new arrays and discard old arrays */
1018 if(pPath->pPoints)
1020 assert(pPath->pFlags);
1022 memcpy(pPointsNew, pPath->pPoints,
1023 sizeof(POINT)*pPath->numEntriesUsed);
1024 memcpy(pFlagsNew, pPath->pFlags,
1025 sizeof(BYTE)*pPath->numEntriesUsed);
1027 free(pPath->pPoints);
1028 free(pPath->pFlags);
1030 pPath->pPoints=pPointsNew;
1031 pPath->pFlags=pFlagsNew;
1032 pPath->numEntriesAllocated=numEntriesToAllocate;
1035 return TRUE;
1038 /* PATH_GetPathFromHDC
1040 * Retrieves a pointer to the GdiPath structure contained in an HDC and
1041 * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
1043 static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath)
1045 DC *pDC;
1047 pDC=DC_GetDCPtr(hdc);
1048 if(pDC)
1050 *ppPath=&pDC->w.path;
1051 return TRUE;
1053 else
1054 return FALSE;
1057 /* PATH_DoArcPart
1059 * Creates a Bezier spline that corresponds to part of an arc and appends the
1060 * corresponding points to the path. The start and end angles are passed in
1061 * "angleStart" and "angleEnd"; these angles should span a quarter circle
1062 * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
1063 * point is added to the path; otherwise, it is assumed that the current
1064 * position is equal to the first control point.
1066 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
1067 double angleStart, double angleEnd, BOOL addMoveTo)
1069 double halfAngle, a;
1070 double xNorm[4], yNorm[4];
1071 POINT point;
1072 int i;
1074 assert(fabs(angleEnd-angleStart)<=M_PI_2);
1076 /* FIXME: Is there an easier way of computing this? */
1078 /* Compute control points */
1079 halfAngle=(angleEnd-angleStart)/2.0;
1080 if(fabs(halfAngle)>1e-8)
1082 a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
1083 xNorm[0]=cos(angleStart);
1084 yNorm[0]=sin(angleStart);
1085 xNorm[1]=xNorm[0] - a*yNorm[0];
1086 yNorm[1]=yNorm[0] + a*xNorm[0];
1087 xNorm[3]=cos(angleEnd);
1088 yNorm[3]=sin(angleEnd);
1089 xNorm[2]=xNorm[3] + a*yNorm[3];
1090 yNorm[2]=yNorm[3] - a*xNorm[3];
1092 else
1093 for(i=0; i<4; i++)
1095 xNorm[i]=cos(angleStart);
1096 yNorm[i]=sin(angleStart);
1099 /* Add starting point to path if desired */
1100 if(addMoveTo)
1102 PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
1103 if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
1104 return FALSE;
1107 /* Add remaining control points */
1108 for(i=1; i<4; i++)
1110 PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
1111 if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1112 return FALSE;
1115 return TRUE;
1118 /* PATH_ScaleNormalizedPoint
1120 * Scales a normalized point (x, y) with respect to the box whose corners are
1121 * passed in "corners". The point is stored in "*pPoint". The normalized
1122 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1123 * (1.0, 1.0) correspond to corners[1].
1125 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
1126 double y, POINT *pPoint)
1128 pPoint->x=GDI_ROUND( (double)corners[0].x +
1129 (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
1130 pPoint->y=GDI_ROUND( (double)corners[0].y +
1131 (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
1134 /* PATH_NormalizePoint
1136 * Normalizes a point with respect to the box whose corners are passed in
1137 * "corners". The normalized coordinates are stored in "*pX" and "*pY".
1139 static void PATH_NormalizePoint(FLOAT_POINT corners[],
1140 const FLOAT_POINT *pPoint,
1141 double *pX, double *pY)
1143 *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) *
1144 2.0 - 1.0;
1145 *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) *
1146 2.0 - 1.0;
1149 /*******************************************************************
1150 * FlattenPath16 [GDI.516]
1154 BOOL16 WINAPI FlattenPath16(HDC16 hdc)
1156 return (BOOL16) FlattenPath((HDC) hdc);
1159 /*******************************************************************
1160 * FlattenPath32 [GDI32.103]
1164 BOOL WINAPI FlattenPath(HDC hdc)
1166 FIXME("FlattenPath, stub\n");
1167 return 0;
1170 /*******************************************************************
1171 * StrokeAndFillPath16 [GDI.520]
1175 BOOL16 WINAPI StrokeAndFillPath16(HDC16 hdc)
1177 return (BOOL16) StrokeAndFillPath((HDC) hdc);
1180 /*******************************************************************
1181 * StrokeAndFillPath [GDI32.352]
1185 BOOL WINAPI StrokeAndFillPath(HDC hdc)
1187 FIXME("StrokeAndFillPath, stub\n");
1188 return 0;
1191 /*******************************************************************
1192 * StrokePath16 [GDI.521]
1196 BOOL16 WINAPI StrokePath16(HDC16 hdc)
1198 return (BOOL16) StrokePath((HDC) hdc);
1201 /*******************************************************************
1202 * StrokePath [GDI32.353]
1206 BOOL WINAPI StrokePath(HDC hdc)
1208 FIXME("StrokePath, stub\n");
1209 return 0;
1212 /*******************************************************************
1213 * WidenPath16 [GDI.522]
1217 BOOL16 WINAPI WidenPath16(HDC16 hdc)
1219 return (BOOL16) WidenPath((HDC) hdc);
1222 /*******************************************************************
1223 * WidenPath [GDI32.360]
1227 BOOL WINAPI WidenPath(HDC hdc)
1229 FIXME("WidenPath, stub\n");
1230 return 0;