Added sysres_Ru.s
[wine/multimedia.git] / graphics / path.c
blob6e174fc35de4741132bba8d038fa4164eed3ecf5
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>
12 #include "winbase.h"
13 #include "wingdi.h"
14 #include "winerror.h"
16 #include "dc.h"
17 #include "debug.h"
18 #include "path.h"
20 /* Notes on the implementation
22 * The implementation is based on dynamically resizable arrays of points and
23 * flags. I dithered for a bit before deciding on this implementation, and
24 * I had even done a bit of work on a linked list version before switching
25 * to arrays. It's a bit of a tradeoff. When you use linked lists, the
26 * implementation of FlattenPath is easier, because you can rip the
27 * PT_BEZIERTO entries out of the middle of the list and link the
28 * corresponding PT_LINETO entries in. However, when you use arrays,
29 * PathToRegion becomes easier, since you can essentially just pass your array
30 * of points to CreatePolyPolygonRgn. Also, if I'd used linked lists, I would
31 * have had the extra effort of creating a chunk-based allocation scheme
32 * in order to use memory effectively. That's why I finally decided to use
33 * arrays. Note by the way that the array based implementation has the same
34 * linear time complexity that linked lists would have since the arrays grow
35 * exponentially.
37 * The points are stored in the path in device coordinates. This is
38 * consistent with the way Windows does things (for instance, see the Win32
39 * SDK documentation for GetPath).
41 * The word "stroke" appears in several places (e.g. in the flag
42 * GdiPath.newStroke). A stroke consists of a PT_MOVETO followed by one or
43 * more PT_LINETOs or PT_BEZIERTOs, up to, but not including, the next
44 * PT_MOVETO. Note that this is not the same as the definition of a figure;
45 * a figure can contain several strokes.
47 * I modified the drawing functions (MoveTo, LineTo etc.) to test whether
48 * the path is open and to call the corresponding function in path.c if this
49 * is the case. A more elegant approach would be to modify the function
50 * pointers in the DC_FUNCTIONS structure; however, this would be a lot more
51 * complex. Also, the performance degradation caused by my approach in the
52 * case where no path is open is so small that it cannot be measured.
54 * Martin Boehme
57 /* FIXME: A lot of stuff isn't implemented yet. There is much more to come. */
59 #define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
60 #define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
61 #define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
64 static BOOL PATH_PathToRegion(const GdiPath *pPath, INT nPolyFillMode,
65 HRGN *pHrgn);
66 static void PATH_EmptyPath(GdiPath *pPath);
67 static BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint,
68 BYTE flags);
69 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries);
70 static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath);
71 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
72 double angleStart, double angleEnd, BOOL addMoveTo);
73 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
74 double y, POINT *pPoint);
75 static void PATH_NormalizePoint(FLOAT_POINT corners[], const FLOAT_POINT
76 *pPoint, double *pX, double *pY);
79 /***********************************************************************
80 * BeginPath16 (GDI.512)
82 BOOL16 WINAPI BeginPath16(HDC16 hdc)
84 return (BOOL16)BeginPath((HDC)hdc);
88 /***********************************************************************
89 * BeginPath32 (GDI32.9)
91 BOOL WINAPI BeginPath(HDC hdc)
93 GdiPath *pPath;
95 /* Get pointer to path */
96 if(!PATH_GetPathFromHDC(hdc, &pPath))
98 SetLastError(ERROR_INVALID_HANDLE);
99 return FALSE;
102 /* If path is already open, do nothing */
103 if(pPath->state==PATH_Open)
104 return TRUE;
106 /* Make sure that path is empty */
107 PATH_EmptyPath(pPath);
109 /* Initialize variables for new path */
110 pPath->newStroke=TRUE;
111 pPath->state=PATH_Open;
113 return TRUE;
117 /***********************************************************************
118 * EndPath16 (GDI.514)
120 BOOL16 WINAPI EndPath16(HDC16 hdc)
122 return (BOOL16)EndPath((HDC)hdc);
126 /***********************************************************************
127 * EndPath32 (GDI32.78)
129 BOOL WINAPI EndPath(HDC hdc)
131 GdiPath *pPath;
133 /* Get pointer to path */
134 if(!PATH_GetPathFromHDC(hdc, &pPath))
136 SetLastError(ERROR_INVALID_HANDLE);
137 return FALSE;
140 /* Check that path is currently being constructed */
141 if(pPath->state!=PATH_Open)
143 SetLastError(ERROR_CAN_NOT_COMPLETE);
144 return FALSE;
147 /* Set flag to indicate that path is finished */
148 pPath->state=PATH_Closed;
150 return TRUE;
154 /***********************************************************************
155 * AbortPath16 (GDI.511)
157 BOOL16 WINAPI AbortPath16(HDC16 hdc)
159 return (BOOL16)AbortPath((HDC)hdc);
163 /******************************************************************************
164 * AbortPath32 [GDI32.1]
165 * Closes and discards paths from device context
167 * NOTES
168 * Check that SetLastError is being called correctly
170 * PARAMS
171 * hdc [I] Handle to device context
173 * RETURNS STD
175 BOOL WINAPI AbortPath( HDC hdc )
177 GdiPath *pPath;
179 /* Get pointer to path */
180 if(!PATH_GetPathFromHDC(hdc, &pPath))
182 SetLastError(ERROR_INVALID_PARAMETER);
183 return FALSE;
186 /* Remove all entries from the path */
187 PATH_EmptyPath(pPath);
189 return TRUE;
193 /***********************************************************************
194 * CloseFigure16 (GDI.513)
196 BOOL16 WINAPI CloseFigure16(HDC16 hdc)
198 return (BOOL16)CloseFigure((HDC)hdc);
202 /***********************************************************************
203 * CloseFigure32 (GDI32.16)
205 * FIXME: Check that SetLastError is being called correctly
207 BOOL WINAPI CloseFigure(HDC hdc)
209 GdiPath *pPath;
211 /* Get pointer to path */
212 if(!PATH_GetPathFromHDC(hdc, &pPath))
214 SetLastError(ERROR_INVALID_PARAMETER);
215 return FALSE;
218 /* Check that path is open */
219 if(pPath->state!=PATH_Open)
221 SetLastError(ERROR_CAN_NOT_COMPLETE);
222 return FALSE;
225 /* FIXME: Shouldn't we draw a line to the beginning of the figure? */
227 /* Set PT_CLOSEFIGURE on the last entry and start a new stroke */
228 if(pPath->numEntriesUsed)
230 pPath->pFlags[pPath->numEntriesUsed-1]|=PT_CLOSEFIGURE;
231 pPath->newStroke=TRUE;
234 return TRUE;
238 /***********************************************************************
239 * GetPath16 (GDI.517)
241 INT16 WINAPI GetPath16(HDC16 hdc, LPPOINT16 pPoints, LPBYTE pTypes,
242 INT16 nSize)
244 FIXME(gdi, "(%d,%p,%p): stub\n",hdc,pPoints,pTypes);
246 return 0;
250 /***********************************************************************
251 * GetPath32 (GDI32.210)
253 INT WINAPI GetPath(HDC hdc, LPPOINT pPoints, LPBYTE pTypes,
254 INT nSize)
256 GdiPath *pPath;
258 /* Get pointer to path */
259 if(!PATH_GetPathFromHDC(hdc, &pPath))
261 SetLastError(ERROR_INVALID_PARAMETER);
262 return -1;
265 /* Check that path is closed */
266 if(pPath->state!=PATH_Closed)
268 SetLastError(ERROR_CAN_NOT_COMPLETE);
269 return -1;
272 if(nSize==0)
273 return pPath->numEntriesUsed;
274 else if(nSize<pPath->numEntriesUsed)
276 SetLastError(ERROR_INVALID_PARAMETER);
277 return -1;
279 else
281 memcpy(pPoints, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
282 memcpy(pTypes, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
284 /* Convert the points to logical coordinates */
285 if(!DPtoLP(hdc, pPoints, pPath->numEntriesUsed))
287 /* FIXME: Is this the correct value? */
288 SetLastError(ERROR_CAN_NOT_COMPLETE);
289 return -1;
292 return pPath->numEntriesUsed;
297 /***********************************************************************
298 * PathToRegion32 (GDI32.261)
300 * FIXME
301 * Check that SetLastError is being called correctly
303 * The documentation does not state this explicitly, but a test under Windows
304 * shows that the region which is returned should be in device coordinates.
306 HRGN WINAPI PathToRegion(HDC hdc)
308 GdiPath *pPath;
309 HRGN hrgnRval;
311 /* Get pointer to path */
312 if(!PATH_GetPathFromHDC(hdc, &pPath))
314 SetLastError(ERROR_INVALID_PARAMETER);
315 return 0;
318 /* Check that path is closed */
319 if(pPath->state!=PATH_Closed)
321 SetLastError(ERROR_CAN_NOT_COMPLETE);
322 return 0;
325 /* FIXME: Should we empty the path even if conversion failed? */
326 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnRval))
327 PATH_EmptyPath(pPath);
328 else
329 hrgnRval=0;
331 return hrgnRval;
335 /***********************************************************************
336 * FillPath32 (GDI32.100)
338 * FIXME
339 * Check that SetLastError is being called correctly
341 BOOL WINAPI FillPath(HDC hdc)
343 GdiPath *pPath;
344 INT mapMode, graphicsMode;
345 SIZE ptViewportExt, ptWindowExt;
346 POINT ptViewportOrg, ptWindowOrg;
347 XFORM xform;
348 HRGN hrgn;
350 /* Get pointer to path */
351 if(!PATH_GetPathFromHDC(hdc, &pPath))
353 SetLastError(ERROR_INVALID_PARAMETER);
354 return FALSE;
357 /* Check that path is closed */
358 if(pPath->state!=PATH_Closed)
360 SetLastError(ERROR_CAN_NOT_COMPLETE);
361 return FALSE;
364 /* Construct a region from the path and fill it */
365 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgn))
367 /* Since PaintRgn interprets the region as being in logical coordinates
368 * but the points we store for the path are already in device
369 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
370 * Using SaveDC to save information about the mapping mode / world
371 * transform would be easier but would require more overhead, especially
372 * now that SaveDC saves the current path.
375 /* Save the information about the old mapping mode */
376 mapMode=GetMapMode(hdc);
377 GetViewportExtEx(hdc, &ptViewportExt);
378 GetViewportOrgEx(hdc, &ptViewportOrg);
379 GetWindowExtEx(hdc, &ptWindowExt);
380 GetWindowOrgEx(hdc, &ptWindowOrg);
382 /* Save world transform
383 * NB: The Windows documentation on world transforms would lead one to
384 * believe that this has to be done only in GM_ADVANCED; however, my
385 * tests show that resetting the graphics mode to GM_COMPATIBLE does
386 * not reset the world transform.
388 GetWorldTransform(hdc, &xform);
390 /* Set MM_TEXT */
391 SetMapMode(hdc, MM_TEXT);
393 /* Paint the region */
394 PaintRgn(hdc, hrgn);
396 /* Restore the old mapping mode */
397 SetMapMode(hdc, mapMode);
398 SetViewportExtEx(hdc, ptViewportExt.cx, ptViewportExt.cy, NULL);
399 SetViewportOrgEx(hdc, ptViewportOrg.x, ptViewportOrg.y, NULL);
400 SetWindowExtEx(hdc, ptWindowExt.cx, ptWindowExt.cy, NULL);
401 SetWindowOrgEx(hdc, ptWindowOrg.x, ptWindowOrg.y, NULL);
403 /* Go to GM_ADVANCED temporarily to restore the world transform */
404 graphicsMode=GetGraphicsMode(hdc);
405 SetGraphicsMode(hdc, GM_ADVANCED);
406 SetWorldTransform(hdc, &xform);
407 SetGraphicsMode(hdc, graphicsMode);
409 /* Empty the path */
410 PATH_EmptyPath(pPath);
411 return TRUE;
413 else
415 /* FIXME: Should the path be emptied even if conversion failed? */
416 /* PATH_EmptyPath(pPath); */
417 return FALSE;
422 /***********************************************************************
423 * SelectClipPath32 (GDI32.296)
424 * FIXME
425 * Check that SetLastError is being called correctly
427 BOOL WINAPI SelectClipPath(HDC hdc, INT iMode)
429 GdiPath *pPath;
430 HRGN hrgnPath;
431 BOOL success;
433 /* Get pointer to path */
434 if(!PATH_GetPathFromHDC(hdc, &pPath))
436 SetLastError(ERROR_INVALID_PARAMETER);
437 return FALSE;
440 /* Check that path is closed */
441 if(pPath->state!=PATH_Closed)
443 SetLastError(ERROR_CAN_NOT_COMPLETE);
444 return FALSE;
447 /* Construct a region from the path */
448 if(PATH_PathToRegion(pPath, GetPolyFillMode(hdc), &hrgnPath))
450 success = ExtSelectClipRgn( hdc, hrgnPath, iMode ) != ERROR;
451 DeleteObject(hrgnPath);
453 /* Empty the path */
454 if(success)
455 PATH_EmptyPath(pPath);
456 /* FIXME: Should this function delete the path even if it failed? */
458 return success;
460 else
461 return FALSE;
465 /***********************************************************************
466 * Exported functions
469 /* PATH_InitGdiPath
471 * Initializes the GdiPath structure.
473 void PATH_InitGdiPath(GdiPath *pPath)
475 assert(pPath!=NULL);
477 pPath->state=PATH_Null;
478 pPath->pPoints=NULL;
479 pPath->pFlags=NULL;
480 pPath->numEntriesUsed=0;
481 pPath->numEntriesAllocated=0;
484 /* PATH_DestroyGdiPath
486 * Destroys a GdiPath structure (frees the memory in the arrays).
488 void PATH_DestroyGdiPath(GdiPath *pPath)
490 assert(pPath!=NULL);
492 free(pPath->pPoints);
493 free(pPath->pFlags);
496 /* PATH_AssignGdiPath
498 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
499 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
500 * not just the pointers. Since this means that the arrays in pPathDest may
501 * need to be resized, pPathDest should have been initialized using
502 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
503 * not a copy constructor).
504 * Returns TRUE if successful, else FALSE.
506 BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
508 assert(pPathDest!=NULL && pPathSrc!=NULL);
510 /* Make sure destination arrays are big enough */
511 if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
512 return FALSE;
514 /* Perform the copy operation */
515 memcpy(pPathDest->pPoints, pPathSrc->pPoints,
516 sizeof(POINT)*pPathSrc->numEntriesUsed);
517 memcpy(pPathDest->pFlags, pPathSrc->pFlags,
518 sizeof(INT)*pPathSrc->numEntriesUsed);
519 pPathDest->state=pPathSrc->state;
520 pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
521 pPathDest->newStroke=pPathSrc->newStroke;
523 return TRUE;
526 /* PATH_MoveTo
528 * Should be called when a MoveTo is performed on a DC that has an
529 * open path. This starts a new stroke. Returns TRUE if successful, else
530 * FALSE.
532 BOOL PATH_MoveTo(HDC hdc)
534 GdiPath *pPath;
536 /* Get pointer to path */
537 if(!PATH_GetPathFromHDC(hdc, &pPath))
538 return FALSE;
540 /* Check that path is open */
541 if(pPath->state!=PATH_Open)
542 /* FIXME: Do we have to call SetLastError? */
543 return FALSE;
545 /* Start a new stroke */
546 pPath->newStroke=TRUE;
548 return TRUE;
551 /* PATH_LineTo
553 * Should be called when a LineTo is performed on a DC that has an
554 * open path. This adds a PT_LINETO entry to the path (and possibly
555 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
556 * Returns TRUE if successful, else FALSE.
558 BOOL PATH_LineTo(HDC hdc, INT x, INT y)
560 GdiPath *pPath;
561 POINT point, pointCurPos;
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 return FALSE;
571 /* Convert point to device coordinates */
572 point.x=x;
573 point.y=y;
574 if(!LPtoDP(hdc, &point, 1))
575 return FALSE;
577 /* Add a PT_MOVETO if necessary */
578 if(pPath->newStroke)
580 pPath->newStroke=FALSE;
581 if(!GetCurrentPositionEx(hdc, &pointCurPos) ||
582 !LPtoDP(hdc, &pointCurPos, 1))
583 return FALSE;
584 if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
585 return FALSE;
588 /* Add a PT_LINETO entry */
589 return PATH_AddEntry(pPath, &point, PT_LINETO);
592 /* PATH_Rectangle
594 * Should be called when a call to Rectangle is performed on a DC that has
595 * an open path. Returns TRUE if successful, else FALSE.
597 BOOL PATH_Rectangle(HDC hdc, INT x1, INT y1, INT x2, INT y2)
599 GdiPath *pPath;
600 POINT corners[2], pointTemp;
601 INT temp;
603 /* Get pointer to path */
604 if(!PATH_GetPathFromHDC(hdc, &pPath))
605 return FALSE;
607 /* Check that path is open */
608 if(pPath->state!=PATH_Open)
609 return FALSE;
611 /* Convert points to device coordinates */
612 corners[0].x=x1;
613 corners[0].y=y1;
614 corners[1].x=x2;
615 corners[1].y=y2;
616 if(!LPtoDP(hdc, corners, 2))
617 return FALSE;
619 /* Make sure first corner is top left and second corner is bottom right */
620 if(corners[0].x>corners[1].x)
622 temp=corners[0].x;
623 corners[0].x=corners[1].x;
624 corners[1].x=temp;
626 if(corners[0].y>corners[1].y)
628 temp=corners[0].y;
629 corners[0].y=corners[1].y;
630 corners[1].y=temp;
633 /* In GM_COMPATIBLE, don't include bottom and right edges */
634 if(GetGraphicsMode(hdc)==GM_COMPATIBLE)
636 corners[1].x--;
637 corners[1].y--;
640 /* Close any previous figure */
641 if(!CloseFigure(hdc))
643 /* The CloseFigure call shouldn't have failed */
644 assert(FALSE);
645 return FALSE;
648 /* Add four points to the path */
649 pointTemp.x=corners[1].x;
650 pointTemp.y=corners[0].y;
651 if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
652 return FALSE;
653 if(!PATH_AddEntry(pPath, corners, PT_LINETO))
654 return FALSE;
655 pointTemp.x=corners[0].x;
656 pointTemp.y=corners[1].y;
657 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
658 return FALSE;
659 if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
660 return FALSE;
662 /* Close the rectangle figure */
663 if(!CloseFigure(hdc))
665 /* The CloseFigure call shouldn't have failed */
666 assert(FALSE);
667 return FALSE;
670 return TRUE;
673 /* PATH_Ellipse
675 * Should be called when a call to Ellipse is performed on a DC that has
676 * an open path. This adds four Bezier splines representing the ellipse
677 * to the path. Returns TRUE if successful, else FALSE.
679 BOOL PATH_Ellipse(HDC hdc, INT x1, INT y1, INT x2, INT y2)
681 /* TODO: This should probably be revised to call PATH_AngleArc */
682 /* (once it exists) */
683 return PATH_Arc(hdc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2);
686 /* PATH_Arc
688 * Should be called when a call to Arc is performed on a DC that has
689 * an open path. This adds up to five Bezier splines representing the arc
690 * to the path. Returns TRUE if successful, else FALSE.
692 BOOL PATH_Arc(HDC hdc, INT x1, INT y1, INT x2, INT y2,
693 INT xStart, INT yStart, INT xEnd, INT yEnd)
695 GdiPath *pPath;
696 DC *pDC;
697 double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
698 /* Initialize angleEndQuadrant to silence gcc's warning */
699 double x, y;
700 FLOAT_POINT corners[2], pointStart, pointEnd;
701 BOOL start, end;
702 INT temp;
704 /* FIXME: This function should check for all possible error returns */
705 /* FIXME: Do we have to respect newStroke? */
707 /* Get pointer to DC */
708 pDC=DC_GetDCPtr(hdc);
709 if(pDC==NULL)
710 return FALSE;
712 /* Get pointer to path */
713 if(!PATH_GetPathFromHDC(hdc, &pPath))
714 return FALSE;
716 /* Check that path is open */
717 if(pPath->state!=PATH_Open)
718 return FALSE;
720 /* FIXME: Do we have to close the current figure? */
722 /* Check for zero height / width */
723 /* FIXME: Only in GM_COMPATIBLE? */
724 if(x1==x2 || y1==y2)
725 return TRUE;
727 /* Convert points to device coordinates */
728 corners[0].x=(FLOAT)x1;
729 corners[0].y=(FLOAT)y1;
730 corners[1].x=(FLOAT)x2;
731 corners[1].y=(FLOAT)y2;
732 pointStart.x=(FLOAT)xStart;
733 pointStart.y=(FLOAT)yStart;
734 pointEnd.x=(FLOAT)xEnd;
735 pointEnd.y=(FLOAT)yEnd;
736 INTERNAL_LPTODP_FLOAT(pDC, corners);
737 INTERNAL_LPTODP_FLOAT(pDC, corners+1);
738 INTERNAL_LPTODP_FLOAT(pDC, &pointStart);
739 INTERNAL_LPTODP_FLOAT(pDC, &pointEnd);
741 /* Make sure first corner is top left and second corner is bottom right */
742 if(corners[0].x>corners[1].x)
744 temp=corners[0].x;
745 corners[0].x=corners[1].x;
746 corners[1].x=temp;
748 if(corners[0].y>corners[1].y)
750 temp=corners[0].y;
751 corners[0].y=corners[1].y;
752 corners[1].y=temp;
755 /* Compute start and end angle */
756 PATH_NormalizePoint(corners, &pointStart, &x, &y);
757 angleStart=atan2(y, x);
758 PATH_NormalizePoint(corners, &pointEnd, &x, &y);
759 angleEnd=atan2(y, x);
761 /* Make sure the end angle is "on the right side" of the start angle */
762 if(GetArcDirection(hdc)==AD_CLOCKWISE)
764 if(angleEnd<=angleStart)
766 angleEnd+=2*M_PI;
767 assert(angleEnd>=angleStart);
770 else
772 if(angleEnd>=angleStart)
774 angleEnd-=2*M_PI;
775 assert(angleEnd<=angleStart);
779 /* In GM_COMPATIBLE, don't include bottom and right edges */
780 if(GetGraphicsMode(hdc)==GM_COMPATIBLE)
782 corners[1].x--;
783 corners[1].y--;
786 /* Add the arc to the path with one Bezier spline per quadrant that the
787 * arc spans */
788 start=TRUE;
789 end=FALSE;
792 /* Determine the start and end angles for this quadrant */
793 if(start)
795 angleStartQuadrant=angleStart;
796 if(GetArcDirection(hdc)==AD_CLOCKWISE)
797 angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
798 else
799 angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
801 else
803 angleStartQuadrant=angleEndQuadrant;
804 if(GetArcDirection(hdc)==AD_CLOCKWISE)
805 angleEndQuadrant+=M_PI_2;
806 else
807 angleEndQuadrant-=M_PI_2;
810 /* Have we reached the last part of the arc? */
811 if((GetArcDirection(hdc)==AD_CLOCKWISE &&
812 angleEnd<angleEndQuadrant) ||
813 (GetArcDirection(hdc)==AD_COUNTERCLOCKWISE &&
814 angleEnd>angleEndQuadrant))
816 /* Adjust the end angle for this quadrant */
817 angleEndQuadrant=angleEnd;
818 end=TRUE;
821 /* Add the Bezier spline to the path */
822 PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant,
823 start);
824 start=FALSE;
825 } while(!end);
827 return TRUE;
830 /***********************************************************************
831 * Internal functions
834 /* PATH_PathToRegion
836 * Creates a region from the specified path using the specified polygon
837 * filling mode. The path is left unchanged. A handle to the region that
838 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
839 * error occurs, SetLastError is called with the appropriate value and
840 * FALSE is returned.
842 static BOOL PATH_PathToRegion(const GdiPath *pPath, INT nPolyFillMode,
843 HRGN *pHrgn)
845 int numStrokes, iStroke, i;
846 INT *pNumPointsInStroke;
847 HRGN hrgn;
849 assert(pPath!=NULL);
850 assert(pHrgn!=NULL);
852 /* FIXME: What happens when number of points is zero? */
854 /* First pass: Find out how many strokes there are in the path */
855 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
856 numStrokes=0;
857 for(i=0; i<pPath->numEntriesUsed; i++)
858 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
859 numStrokes++;
861 /* Allocate memory for number-of-points-in-stroke array */
862 pNumPointsInStroke=(int *)malloc(sizeof(int)*numStrokes);
863 if(!pNumPointsInStroke)
865 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
866 return FALSE;
869 /* Second pass: remember number of points in each polygon */
870 iStroke=-1; /* Will get incremented to 0 at beginning of first stroke */
871 for(i=0; i<pPath->numEntriesUsed; i++)
873 /* Is this the beginning of a new stroke? */
874 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
876 iStroke++;
877 pNumPointsInStroke[iStroke]=0;
880 pNumPointsInStroke[iStroke]++;
883 /* Create a region from the strokes */
884 hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
885 numStrokes, nPolyFillMode);
886 if(hrgn==(HRGN)0)
888 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
889 return FALSE;
892 /* Free memory for number-of-points-in-stroke array */
893 free(pNumPointsInStroke);
895 /* Success! */
896 *pHrgn=hrgn;
897 return TRUE;
900 /* PATH_EmptyPath
902 * Removes all entries from the path and sets the path state to PATH_Null.
904 static void PATH_EmptyPath(GdiPath *pPath)
906 assert(pPath!=NULL);
908 pPath->state=PATH_Null;
909 pPath->numEntriesUsed=0;
912 /* PATH_AddEntry
914 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
915 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
916 * successful, FALSE otherwise (e.g. if not enough memory was available).
918 BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint, BYTE flags)
920 assert(pPath!=NULL);
922 /* FIXME: If newStroke is true, perhaps we want to check that we're
923 * getting a PT_MOVETO
926 /* Check that path is open */
927 if(pPath->state!=PATH_Open)
928 return FALSE;
930 /* Reserve enough memory for an extra path entry */
931 if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
932 return FALSE;
934 /* Store information in path entry */
935 pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
936 pPath->pFlags[pPath->numEntriesUsed]=flags;
938 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
939 if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
940 pPath->newStroke=TRUE;
942 /* Increment entry count */
943 pPath->numEntriesUsed++;
945 return TRUE;
948 /* PATH_ReserveEntries
950 * Ensures that at least "numEntries" entries (for points and flags) have
951 * been allocated; allocates larger arrays and copies the existing entries
952 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
954 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries)
956 INT numEntriesToAllocate;
957 POINT *pPointsNew;
958 BYTE *pFlagsNew;
960 assert(pPath!=NULL);
961 assert(numEntries>=0);
963 /* Do we have to allocate more memory? */
964 if(numEntries > pPath->numEntriesAllocated)
966 /* Find number of entries to allocate. We let the size of the array
967 * grow exponentially, since that will guarantee linear time
968 * complexity. */
969 if(pPath->numEntriesAllocated)
971 numEntriesToAllocate=pPath->numEntriesAllocated;
972 while(numEntriesToAllocate<numEntries)
973 numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/
974 GROW_FACTOR_DENOM;
976 else
977 numEntriesToAllocate=NUM_ENTRIES_INITIAL;
979 /* Allocate new arrays */
980 pPointsNew=(POINT *)malloc(numEntriesToAllocate * sizeof(POINT));
981 if(!pPointsNew)
982 return FALSE;
983 pFlagsNew=(BYTE *)malloc(numEntriesToAllocate * sizeof(BYTE));
984 if(!pFlagsNew)
986 free(pPointsNew);
987 return FALSE;
990 /* Copy old arrays to new arrays and discard old arrays */
991 if(pPath->pPoints)
993 assert(pPath->pFlags);
995 memcpy(pPointsNew, pPath->pPoints,
996 sizeof(POINT)*pPath->numEntriesUsed);
997 memcpy(pFlagsNew, pPath->pFlags,
998 sizeof(BYTE)*pPath->numEntriesUsed);
1000 free(pPath->pPoints);
1001 free(pPath->pFlags);
1003 pPath->pPoints=pPointsNew;
1004 pPath->pFlags=pFlagsNew;
1005 pPath->numEntriesAllocated=numEntriesToAllocate;
1008 return TRUE;
1011 /* PATH_GetPathFromHDC
1013 * Retrieves a pointer to the GdiPath structure contained in an HDC and
1014 * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
1016 static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath)
1018 DC *pDC;
1020 pDC=DC_GetDCPtr(hdc);
1021 if(pDC)
1023 *ppPath=&pDC->w.path;
1024 return TRUE;
1026 else
1027 return FALSE;
1030 /* PATH_DoArcPart
1032 * Creates a Bezier spline that corresponds to part of an arc and appends the
1033 * corresponding points to the path. The start and end angles are passed in
1034 * "angleStart" and "angleEnd"; these angles should span a quarter circle
1035 * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
1036 * point is added to the path; otherwise, it is assumed that the current
1037 * position is equal to the first control point.
1039 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
1040 double angleStart, double angleEnd, BOOL addMoveTo)
1042 double halfAngle, a;
1043 double xNorm[4], yNorm[4];
1044 POINT point;
1045 int i;
1047 assert(fabs(angleEnd-angleStart)<=M_PI_2);
1049 /* FIXME: Is there an easier way of computing this? */
1051 /* Compute control points */
1052 halfAngle=(angleEnd-angleStart)/2.0;
1053 if(fabs(halfAngle)>1e-8)
1055 a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
1056 xNorm[0]=cos(angleStart);
1057 yNorm[0]=sin(angleStart);
1058 xNorm[1]=xNorm[0] - a*yNorm[0];
1059 yNorm[1]=yNorm[0] + a*xNorm[0];
1060 xNorm[3]=cos(angleEnd);
1061 yNorm[3]=sin(angleEnd);
1062 xNorm[2]=xNorm[3] + a*yNorm[3];
1063 yNorm[2]=yNorm[3] - a*xNorm[3];
1065 else
1066 for(i=0; i<4; i++)
1068 xNorm[i]=cos(angleStart);
1069 yNorm[i]=sin(angleStart);
1072 /* Add starting point to path if desired */
1073 if(addMoveTo)
1075 PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
1076 if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
1077 return FALSE;
1080 /* Add remaining control points */
1081 for(i=1; i<4; i++)
1083 PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
1084 if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1085 return FALSE;
1088 return TRUE;
1091 /* PATH_ScaleNormalizedPoint
1093 * Scales a normalized point (x, y) with respect to the box whose corners are
1094 * passed in "corners". The point is stored in "*pPoint". The normalized
1095 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1096 * (1.0, 1.0) correspond to corners[1].
1098 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
1099 double y, POINT *pPoint)
1101 pPoint->x=GDI_ROUND( (double)corners[0].x +
1102 (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
1103 pPoint->y=GDI_ROUND( (double)corners[0].y +
1104 (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
1107 /* PATH_NormalizePoint
1109 * Normalizes a point with respect to the box whose corners are passed in
1110 * "corners". The normalized coordinates are stored in "*pX" and "*pY".
1112 static void PATH_NormalizePoint(FLOAT_POINT corners[],
1113 const FLOAT_POINT *pPoint,
1114 double *pX, double *pY)
1116 *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) *
1117 2.0 - 1.0;
1118 *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) *
1119 2.0 - 1.0;
1122 /*******************************************************************
1123 * FlattenPath32 [GDI32.103]
1127 BOOL WINAPI FlattenPath(HDC hdc)
1129 FIXME(gdi, "FlattenPath, stub\n");
1130 return 0;
1133 /*******************************************************************
1134 * StrokeAndFillPath [GDI32.352]
1138 BOOL WINAPI StrokeAndFillPath(HDC hdc)
1140 FIXME(gdi, "StrokeAndFillPath, stub\n");
1141 return 0;
1144 /*******************************************************************
1145 * StrokePath [GDI32.353]
1149 BOOL WINAPI StrokePath(HDC hdc)
1151 FIXME(gdi, "StrokePath, stub\n");
1152 return 0;
1155 /*******************************************************************
1156 * WidenPath [GDI32.360]
1160 BOOL WINAPI WidenPath(HDC hdc)
1162 FIXME(gdi, "WidenPath, stub\n");
1163 return 0;