:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / subsys / win32k / objects / path.c
1 #undef WIN32_LEAN_AND_MEAN
2 #include <windows.h>
3 #include <ddk/ntddk.h>
4 #include <win32k/brush.h>
5 #include <win32k/dc.h>
6 #include <win32k/path.h>
7 #include <win32k/math.h>
8 #include <win32k/float.h>
9 #include <win32k/coord.h>
10 #include <win32k/line.h>
11
12 #include <math.h>
13 #include <float.h>
14
15 // #define NDEBUG
16 #include <win32k/debug1.h>
17
18 #define NUM_ENTRIES_INITIAL 16  /* Initial size of points / flags arrays  */
19 #define GROW_FACTOR_NUMER    2  /* Numerator of grow factor for the array */
20 #define GROW_FACTOR_DENOM    1  /* Denominator of grow factor             */
21
22
23 static BOOL PATH_PathToRegion(const GdiPath *pPath, INT nPolyFillMode,
24    HRGN *pHrgn);
25 static void   PATH_EmptyPath(GdiPath *pPath);
26 static BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint,
27    BYTE flags);
28 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries);
29 static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath);
30 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
31    double angleStart, double angleEnd, BOOL addMoveTo);
32 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
33    double y, POINT *pPoint);
34 static void PATH_NormalizePoint(FLOAT_POINT corners[], const FLOAT_POINT
35    *pPoint, double *pX, double *pY);
36
37 BOOL
38 STDCALL
39 W32kAbortPath(HDC  hDC)
40 {
41   UNIMPLEMENTED;
42 }
43
44 BOOL
45 STDCALL
46 W32kBeginPath(HDC  hDC)
47 {
48   UNIMPLEMENTED;
49 }
50
51 BOOL
52 STDCALL
53 W32kCloseFigure(HDC  hDC)
54 {
55   UNIMPLEMENTED;
56 }
57
58 BOOL
59 STDCALL
60 W32kEndPath(HDC  hDC)
61 {
62   UNIMPLEMENTED;
63 }
64
65 BOOL
66 STDCALL
67 W32kFillPath(HDC  hDC)
68 {
69   UNIMPLEMENTED;
70 }
71
72 BOOL
73 STDCALL
74 W32kFlattenPath(HDC  hDC)
75 {
76   UNIMPLEMENTED;
77 }
78
79
80 BOOL
81 STDCALL
82 W32kGetMiterLimit(HDC  hDC,
83                         PFLOAT  Limit)
84 {
85   UNIMPLEMENTED;
86 }
87
88 INT
89 STDCALL
90 W32kGetPath(HDC  hDC,
91                  LPPOINT  Points,
92                  LPBYTE  Types,
93                  INT  nSize)
94 {
95   UNIMPLEMENTED;
96 }
97
98 HRGN
99 STDCALL
100 W32kPathToRegion(HDC  hDC)
101 {
102   UNIMPLEMENTED;
103 }
104
105 BOOL
106 STDCALL
107 W32kSetMiterLimit(HDC  hDC,
108                         FLOAT  NewLimit,
109                         PFLOAT  OldLimit)
110 {
111   UNIMPLEMENTED;
112 }
113
114 BOOL
115 STDCALL
116 W32kStrokeAndFillPath(HDC  hDC)
117 {
118   UNIMPLEMENTED;
119 }
120
121 BOOL
122 STDCALL
123 W32kStrokePath(HDC  hDC)
124 {
125   UNIMPLEMENTED;
126 }
127
128 BOOL
129 STDCALL
130 W32kWidenPath(HDC  hDC)
131 {
132    UNIMPLEMENTED;
133 }
134
135 /***********************************************************************
136  * Exported functions
137  */
138
139 /* PATH_InitGdiPath
140  *
141  * Initializes the GdiPath structure.
142  */
143 void PATH_InitGdiPath(GdiPath *pPath)
144 {
145   assert(pPath!=NULL);
146
147   pPath->state=PATH_Null;
148   pPath->pPoints=NULL;
149   pPath->pFlags=NULL;
150   pPath->numEntriesUsed=0;
151   pPath->numEntriesAllocated=0;
152 }
153
154 /* PATH_DestroyGdiPath
155  *
156  * Destroys a GdiPath structure (frees the memory in the arrays).
157  */
158 void PATH_DestroyGdiPath(GdiPath *pPath)
159 {
160   assert(pPath!=NULL);
161
162   ExFreePool(pPath->pPoints);
163   ExFreePool(pPath->pFlags);
164 }
165
166 /* PATH_AssignGdiPath
167  *
168  * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
169  * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
170  * not just the pointers. Since this means that the arrays in pPathDest may
171  * need to be resized, pPathDest should have been initialized using
172  * PATH_InitGdiPath (in C++, this function would be an assignment operator,
173  * not a copy constructor).
174  * Returns TRUE if successful, else FALSE.
175  */
176 BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
177 {
178   assert(pPathDest!=NULL && pPathSrc!=NULL);
179
180   /* Make sure destination arrays are big enough */
181   if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
182     return FALSE;
183
184   /* Perform the copy operation */
185   memcpy(pPathDest->pPoints, pPathSrc->pPoints,
186     sizeof(POINT)*pPathSrc->numEntriesUsed);
187   memcpy(pPathDest->pFlags, pPathSrc->pFlags,
188     sizeof(BYTE)*pPathSrc->numEntriesUsed);
189
190   pPathDest->state=pPathSrc->state;
191   pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
192   pPathDest->newStroke=pPathSrc->newStroke;
193
194   return TRUE;
195 }
196
197 /* PATH_MoveTo
198  *
199  * Should be called when a MoveTo is performed on a DC that has an
200  * open path. This starts a new stroke. Returns TRUE if successful, else
201  * FALSE.
202  */
203 BOOL PATH_MoveTo(HDC hdc)
204 {
205   GdiPath *pPath;
206
207   /* Get pointer to path */
208   if(!PATH_GetPathFromHDC(hdc, &pPath))
209     return FALSE;
210
211   /* Check that path is open */
212   if(pPath->state!=PATH_Open)
213     /* FIXME: Do we have to call SetLastError? */
214     return FALSE;
215
216   /* Start a new stroke */
217   pPath->newStroke=TRUE;
218
219   return TRUE;
220 }
221
222 /* PATH_LineTo
223  *
224  * Should be called when a LineTo is performed on a DC that has an
225  * open path. This adds a PT_LINETO entry to the path (and possibly
226  * a PT_MOVETO entry, if this is the first LineTo in a stroke).
227  * Returns TRUE if successful, else FALSE.
228  */
229 BOOL PATH_LineTo(HDC hdc, INT x, INT y)
230 {
231   GdiPath *pPath;
232   POINT point, pointCurPos;
233
234   /* Get pointer to path */
235   if(!PATH_GetPathFromHDC(hdc, &pPath))
236     return FALSE;
237
238   /* Check that path is open */
239   if(pPath->state!=PATH_Open)
240     return FALSE;
241
242   /* Convert point to device coordinates */
243   point.x=x;
244   point.y=y;
245   if(!W32kLPtoDP(hdc, &point, 1))
246     return FALSE;
247
248   /* Add a PT_MOVETO if necessary */
249   if(pPath->newStroke)
250   {
251     pPath->newStroke=FALSE;
252     if(!W32kGetCurrentPositionEx(hdc, &pointCurPos) ||
253        !W32kLPtoDP(hdc, &pointCurPos, 1))
254       return FALSE;
255     if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
256       return FALSE;
257   }
258
259   /* Add a PT_LINETO entry */
260   return PATH_AddEntry(pPath, &point, PT_LINETO);
261 }
262
263 /* PATH_Rectangle
264  *
265  * Should be called when a call to Rectangle is performed on a DC that has
266  * an open path. Returns TRUE if successful, else FALSE.
267  */
268 BOOL PATH_Rectangle(HDC hdc, INT x1, INT y1, INT x2, INT y2)
269 {
270   GdiPath *pPath;
271   POINT corners[2], pointTemp;
272   INT   temp;
273
274   /* Get pointer to path */
275   if(!PATH_GetPathFromHDC(hdc, &pPath))
276     return FALSE;
277
278   /* Check that path is open */
279   if(pPath->state!=PATH_Open)
280     return FALSE;
281
282   /* Convert points to device coordinates */
283   corners[0].x=x1;
284   corners[0].y=y1;
285   corners[1].x=x2;
286   corners[1].y=y2;
287   if(!W32kLPtoDP(hdc, corners, 2))
288     return FALSE;
289
290   /* Make sure first corner is top left and second corner is bottom right */
291   if(corners[0].x>corners[1].x)
292   {
293     temp=corners[0].x;
294     corners[0].x=corners[1].x;
295     corners[1].x=temp;
296   }
297   if(corners[0].y>corners[1].y)
298   {
299     temp=corners[0].y;
300     corners[0].y=corners[1].y;
301     corners[1].y=temp;
302   }
303
304   /* In GM_COMPATIBLE, don't include bottom and right edges */
305   if(W32kGetGraphicsMode(hdc)==GM_COMPATIBLE)
306   {
307     corners[1].x--;
308     corners[1].y--;
309   }
310
311   /* Close any previous figure */
312   if(!W32kCloseFigure(hdc))
313   {
314     /* The W32kCloseFigure call shouldn't have failed */
315     assert(FALSE);
316     return FALSE;
317   }
318
319   /* Add four points to the path */
320   pointTemp.x=corners[1].x;
321   pointTemp.y=corners[0].y;
322   if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
323     return FALSE;
324   if(!PATH_AddEntry(pPath, corners, PT_LINETO))
325     return FALSE;
326   pointTemp.x=corners[0].x;
327   pointTemp.y=corners[1].y;
328   if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
329     return FALSE;
330   if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
331     return FALSE;
332
333   /* Close the rectangle figure */
334   if(!W32kCloseFigure(hdc))
335   {
336     /* The W32kCloseFigure call shouldn't have failed */
337     assert(FALSE);
338     return FALSE;
339   }
340
341   return TRUE;
342 }
343
344 /* PATH_Ellipse
345  *
346  * Should be called when a call to Ellipse is performed on a DC that has
347  * an open path. This adds four Bezier splines representing the ellipse
348  * to the path. Returns TRUE if successful, else FALSE.
349  */
350 BOOL PATH_Ellipse(HDC hdc, INT x1, INT y1, INT x2, INT y2)
351 {
352   /* TODO: This should probably be revised to call PATH_AngleArc */
353   /* (once it exists) */
354   return PATH_Arc(hdc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2);
355 }
356
357 /* PATH_Arc
358  *
359  * Should be called when a call to Arc is performed on a DC that has
360  * an open path. This adds up to five Bezier splines representing the arc
361  * to the path. Returns TRUE if successful, else FALSE.
362  */
363 BOOL PATH_Arc(HDC hdc, INT x1, INT y1, INT x2, INT y2,
364    INT xStart, INT yStart, INT xEnd, INT yEnd)
365 {
366   GdiPath *pPath;
367   DC      *pDC;
368   double  angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
369           /* Initialize angleEndQuadrant to silence gcc's warning */
370   double  x, y;
371   FLOAT_POINT corners[2], pointStart, pointEnd;
372   BOOL    start, end;
373   INT     temp;
374
375   /* FIXME: This function should check for all possible error returns */
376   /* FIXME: Do we have to respect newStroke? */
377
378   /* Get pointer to DC */
379   pDC=DC_HandleToPtr(hdc);
380   if(pDC==NULL)
381     return FALSE;
382
383   /* Get pointer to path */
384   if(!PATH_GetPathFromHDC(hdc, &pPath)){
385         DC_ReleasePtr( hdc );
386     return FALSE;
387   }
388
389   /* Check that path is open */
390   if(pPath->state!=PATH_Open){
391         DC_ReleasePtr( hdc );
392     return FALSE;
393   }
394
395   /* FIXME: Do we have to close the current figure? */
396
397   /* Check for zero height / width */
398   /* FIXME: Only in GM_COMPATIBLE? */
399   if(x1==x2 || y1==y2){
400         DC_ReleasePtr( hdc );
401     return TRUE;
402   }
403
404   /* Convert points to device coordinates */
405   corners[0].x=(FLOAT)x1;
406   corners[0].y=(FLOAT)y1;
407   corners[1].x=(FLOAT)x2;
408   corners[1].y=(FLOAT)y2;
409   pointStart.x=(FLOAT)xStart;
410   pointStart.y=(FLOAT)yStart;
411   pointEnd.x=(FLOAT)xEnd;
412   pointEnd.y=(FLOAT)yEnd;
413   INTERNAL_LPTODP_FLOAT(pDC, corners);
414   INTERNAL_LPTODP_FLOAT(pDC, corners+1);
415   INTERNAL_LPTODP_FLOAT(pDC, &pointStart);
416   INTERNAL_LPTODP_FLOAT(pDC, &pointEnd);
417
418   /* Make sure first corner is top left and second corner is bottom right */
419   if(corners[0].x>corners[1].x)
420   {
421     temp=corners[0].x;
422     corners[0].x=corners[1].x;
423     corners[1].x=temp;
424   }
425   if(corners[0].y>corners[1].y)
426   {
427     temp=corners[0].y;
428     corners[0].y=corners[1].y;
429     corners[1].y=temp;
430   }
431
432   /* Compute start and end angle */
433   PATH_NormalizePoint(corners, &pointStart, &x, &y);
434   angleStart=atan2(y, x);
435   PATH_NormalizePoint(corners, &pointEnd, &x, &y);
436   angleEnd=atan2(y, x);
437
438   /* Make sure the end angle is "on the right side" of the start angle */
439   if(W32kGetArcDirection(hdc)==AD_CLOCKWISE)
440   {
441     if(angleEnd<=angleStart)
442     {
443       angleEnd+=2*M_PI;
444       assert(angleEnd>=angleStart);
445     }
446   }
447   else
448   {
449     if(angleEnd>=angleStart)
450     {
451       angleEnd-=2*M_PI;
452       assert(angleEnd<=angleStart);
453     }
454   }
455
456   /* In GM_COMPATIBLE, don't include bottom and right edges */
457   if(W32kGetGraphicsMode(hdc)==GM_COMPATIBLE)
458   {
459     corners[1].x--;
460     corners[1].y--;
461   }
462
463   /* Add the arc to the path with one Bezier spline per quadrant that the
464    * arc spans */
465   start=TRUE;
466   end=FALSE;
467   do
468   {
469     /* Determine the start and end angles for this quadrant */
470     if(start)
471     {
472       angleStartQuadrant=angleStart;
473       if(W32kGetArcDirection(hdc)==AD_CLOCKWISE)
474         angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
475       else
476         angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
477     }
478     else
479     {
480       angleStartQuadrant=angleEndQuadrant;
481       if(W32kGetArcDirection(hdc)==AD_CLOCKWISE)
482         angleEndQuadrant+=M_PI_2;
483       else
484         angleEndQuadrant-=M_PI_2;
485     }
486
487     /* Have we reached the last part of the arc? */
488     if((W32kGetArcDirection(hdc)==AD_CLOCKWISE &&
489         angleEnd<angleEndQuadrant) ||
490         (W32kGetArcDirection(hdc)==AD_COUNTERCLOCKWISE &&
491         angleEnd>angleEndQuadrant))
492     {
493       /* Adjust the end angle for this quadrant */
494      angleEndQuadrant=angleEnd;
495      end=TRUE;
496     }
497
498     /* Add the Bezier spline to the path */
499     PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant, start);
500     start=FALSE;
501   }  while(!end);
502
503   DC_ReleasePtr( hdc );
504   return TRUE;
505 }
506
507 BOOL PATH_PolyBezierTo(HDC hdc, const POINT *pts, DWORD cbPoints)
508 {
509   GdiPath *pPath;
510   POINT pt;
511   INT i;
512
513   if(!PATH_GetPathFromHDC(hdc, &pPath))
514     return FALSE;
515
516   /* Check that path is open */
517   if(pPath->state!=PATH_Open)
518     return FALSE;
519
520   /* Add a PT_MOVETO if necessary */
521   if(pPath->newStroke)
522   {
523     pPath->newStroke=FALSE;
524     if(!W32kGetCurrentPositionEx(hdc, &pt) ||
525        !W32kLPtoDP(hdc, &pt, 1))
526         return FALSE;
527     if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
528         return FALSE;
529   }
530
531   for(i = 0; i < cbPoints; i++) {
532     pt = pts[i];
533     if(!W32kLPtoDP(hdc, &pt, 1))
534       return FALSE;
535     PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
536   }
537   return TRUE;
538 }
539
540 BOOL PATH_PolyBezier(HDC hdc, const POINT *pts, DWORD cbPoints)
541 {
542   GdiPath *pPath;
543   POINT   pt;
544   INT     i;
545
546   if(!PATH_GetPathFromHDC(hdc, &pPath))
547     return FALSE;
548
549    /* Check that path is open */
550   if(pPath->state!=PATH_Open)
551     return FALSE;
552
553   for(i = 0; i < cbPoints; i++) {
554     pt = pts[i];
555     if(!W32kLPtoDP(hdc, &pt, 1))
556       return FALSE;
557     PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO);
558   }
559   return TRUE;
560 }
561
562 BOOL PATH_Polyline(HDC hdc, const POINT *pts, DWORD cbPoints)
563 {
564   GdiPath *pPath;
565   POINT   pt;
566   INT     i;
567
568   if(!PATH_GetPathFromHDC(hdc, &pPath))
569     return FALSE;
570
571   /* Check that path is open */
572   if(pPath->state!=PATH_Open)
573     return FALSE;
574
575   for(i = 0; i < cbPoints; i++) {
576     pt = pts[i];
577     if(!W32kLPtoDP(hdc, &pt, 1))
578       return FALSE;
579     PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
580   }
581   return TRUE;
582 }
583
584 BOOL PATH_PolylineTo(HDC hdc, const POINT *pts, DWORD cbPoints)
585 {
586   GdiPath *pPath;
587   POINT   pt;
588   INT     i;
589
590   if(!PATH_GetPathFromHDC(hdc, &pPath))
591     return FALSE;
592
593   /* Check that path is open */
594   if(pPath->state!=PATH_Open)
595     return FALSE;
596
597   /* Add a PT_MOVETO if necessary */
598   if(pPath->newStroke)
599   {
600     pPath->newStroke=FALSE;
601     if(!W32kGetCurrentPositionEx(hdc, &pt) ||
602        !W32kLPtoDP(hdc, &pt, 1))
603       return FALSE;
604     if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
605       return FALSE;
606   }
607
608   for(i = 0; i < cbPoints; i++) {
609     pt = pts[i];
610     if(!W32kLPtoDP(hdc, &pt, 1))
611       return FALSE;
612     PATH_AddEntry(pPath, &pt, PT_LINETO);
613   }
614
615   return TRUE;
616 }
617
618
619 BOOL PATH_Polygon(HDC hdc, const POINT *pts, DWORD cbPoints)
620 {
621   GdiPath *pPath;
622   POINT   pt;
623   INT     i;
624
625   if(!PATH_GetPathFromHDC(hdc, &pPath))
626     return FALSE;
627
628   /* Check that path is open */
629   if(pPath->state!=PATH_Open)
630     return FALSE;
631
632   for(i = 0; i < cbPoints; i++) {
633     pt = pts[i];
634     if(!W32kLPtoDP(hdc, &pt, 1))
635       return FALSE;
636     PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
637       ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
638       PT_LINETO));
639   }
640   return TRUE;
641 }
642
643 BOOL PATH_PolyPolygon( HDC hdc, const POINT* pts, const INT* counts,
644                        UINT polygons )
645 {
646   GdiPath *pPath;
647   POINT   pt, startpt;
648   INT     poly, point, i;
649
650   if(!PATH_GetPathFromHDC(hdc, &pPath))
651     return FALSE;
652
653   /* Check that path is open */
654   if(pPath->state!=PATH_Open)
655     return FALSE;
656
657   for(i = 0, poly = 0; poly < polygons; poly++) {
658     for(point = 0; point < counts[poly]; point++, i++) {
659       pt = pts[i];
660       if(!W32kLPtoDP(hdc, &pt, 1))
661          return FALSE;
662       if(point == 0) startpt = pt;
663         PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
664     }
665     /* win98 adds an extra line to close the figure for some reason */
666     PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
667   }
668   return TRUE;
669 }
670
671 BOOL PATH_PolyPolyline( HDC hdc, const POINT* pts, const DWORD* counts,
672                         DWORD polylines )
673 {
674   GdiPath *pPath;
675   POINT   pt;
676   INT     poly, point, i;
677
678   if(!PATH_GetPathFromHDC(hdc, &pPath))
679     return FALSE;
680
681   /* Check that path is open */
682   if(pPath->state!=PATH_Open)
683     return FALSE;
684
685   for(i = 0, poly = 0; poly < polylines; poly++) {
686     for(point = 0; point < counts[poly]; point++, i++) {
687       pt = pts[i];
688       if(!W32kLPtoDP(hdc, &pt, 1))
689         return FALSE;
690       PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
691     }
692   }
693   return TRUE;
694 }
695
696 /***********************************************************************
697  * Internal functions
698  */
699
700
701 /* PATH_AddFlatBezier
702  *
703  */
704 static BOOL PATH_AddFlatBezier(GdiPath *pPath, POINT *pt, BOOL closed)
705 {
706   POINT *pts;
707   INT no, i;
708
709   pts = GDI_Bezier( pt, 4, &no );
710   if(!pts) return FALSE;
711
712   for(i = 1; i < no; i++)
713     PATH_AddEntry(pPath, &pts[i],  (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
714
715   ExFreePool(pts);
716   return TRUE;
717 }
718
719 /* PATH_FlattenPath
720  *
721  * Replaces Beziers with line segments
722  *
723  */
724 static BOOL PATH_FlattenPath(GdiPath *pPath)
725 {
726   GdiPath newPath;
727   INT srcpt;
728
729   memset(&newPath, 0, sizeof(newPath));
730   newPath.state = PATH_Open;
731   for(srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++) {
732     switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE) {
733       case PT_MOVETO:
734       case PT_LINETO:
735         PATH_AddEntry(&newPath, &pPath->pPoints[srcpt], pPath->pFlags[srcpt]);
736         break;
737       case PT_BEZIERTO:
738         PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1], pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
739         srcpt += 2;
740         break;
741     }
742   }
743   newPath.state = PATH_Closed;
744   PATH_AssignGdiPath(pPath, &newPath);
745   PATH_EmptyPath(&newPath);
746   return TRUE;
747 }
748
749 /* PATH_PathToRegion
750  *
751  * Creates a region from the specified path using the specified polygon
752  * filling mode. The path is left unchanged. A handle to the region that
753  * was created is stored in *pHrgn. If successful, TRUE is returned; if an
754  * error occurs, SetLastError is called with the appropriate value and
755  * FALSE is returned.
756  */
757 static BOOL PATH_PathToRegion(const GdiPath *pPath, INT nPolyFillMode,
758    HRGN *pHrgn)
759 {
760   int    numStrokes, iStroke, i;
761   INT  *pNumPointsInStroke;
762   HRGN hrgn;
763
764   assert(pPath!=NULL);
765   assert(pHrgn!=NULL);
766
767   PATH_FlattenPath(pPath);
768
769   /* FIXME: What happens when number of points is zero? */
770
771   /* First pass: Find out how many strokes there are in the path */
772   /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
773   numStrokes=0;
774   for(i=0; i<pPath->numEntriesUsed; i++)
775     if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
776       numStrokes++;
777
778   /* Allocate memory for number-of-points-in-stroke array */
779   pNumPointsInStroke=(int *)ExAllocatePool(NonPagedPool, sizeof(int) * numStrokes);
780   if(!pNumPointsInStroke)
781   {
782 //    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
783     return FALSE;
784   }
785
786   /* Second pass: remember number of points in each polygon */
787   iStroke=-1;  /* Will get incremented to 0 at beginning of first stroke */
788   for(i=0; i<pPath->numEntriesUsed; i++)
789   {
790     /* Is this the beginning of a new stroke? */
791     if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
792     {
793       iStroke++;
794       pNumPointsInStroke[iStroke]=0;
795     }
796
797     pNumPointsInStroke[iStroke]++;
798   }
799
800   /* Create a region from the strokes */
801 /*  hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
802     numStrokes, nPolyFillMode); FIXME: reinclude when region code implemented */
803   if(hrgn==(HRGN)0)
804   {
805 //    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
806     return FALSE;
807   }
808
809   /* Free memory for number-of-points-in-stroke array */
810   ExFreePool(pNumPointsInStroke);
811
812   /* Success! */
813   *pHrgn=hrgn;
814   return TRUE;
815 }
816
817 /* PATH_EmptyPath
818  *
819  * Removes all entries from the path and sets the path state to PATH_Null.
820  */
821 static void PATH_EmptyPath(GdiPath *pPath)
822 {
823   assert(pPath!=NULL);
824
825   pPath->state=PATH_Null;
826   pPath->numEntriesUsed=0;
827 }
828
829 /* PATH_AddEntry
830  *
831  * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
832  * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
833  * successful, FALSE otherwise (e.g. if not enough memory was available).
834  */
835 BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint, BYTE flags)
836 {
837   assert(pPath!=NULL);
838
839   /* FIXME: If newStroke is true, perhaps we want to check that we're
840    * getting a PT_MOVETO
841    */
842
843   /* Check that path is open */
844   if(pPath->state!=PATH_Open)
845     return FALSE;
846
847   /* Reserve enough memory for an extra path entry */
848   if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
849     return FALSE;
850
851   /* Store information in path entry */
852   pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
853   pPath->pFlags[pPath->numEntriesUsed]=flags;
854
855   /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
856   if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
857     pPath->newStroke=TRUE;
858
859   /* Increment entry count */
860   pPath->numEntriesUsed++;
861
862   return TRUE;
863 }
864
865 /* PATH_ReserveEntries
866  *
867  * Ensures that at least "numEntries" entries (for points and flags) have
868  * been allocated; allocates larger arrays and copies the existing entries
869  * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
870  */
871 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries)
872 {
873   INT   numEntriesToAllocate;
874   POINT *pPointsNew;
875   BYTE    *pFlagsNew;
876
877   assert(pPath!=NULL);
878   assert(numEntries>=0);
879
880   /* Do we have to allocate more memory? */
881   if(numEntries > pPath->numEntriesAllocated)
882   {
883     /* Find number of entries to allocate. We let the size of the array
884      * grow exponentially, since that will guarantee linear time
885      * complexity. */
886     if(pPath->numEntriesAllocated)
887     {
888       numEntriesToAllocate=pPath->numEntriesAllocated;
889       while(numEntriesToAllocate<numEntries)
890         numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/GROW_FACTOR_DENOM;
891     } else
892        numEntriesToAllocate=numEntries;
893
894     /* Allocate new arrays */
895     pPointsNew=(POINT *)ExAllocatePool(NonPagedPool, numEntriesToAllocate * sizeof(POINT));
896     if(!pPointsNew)
897       return FALSE;
898     pFlagsNew=(BYTE *)ExAllocatePool(NonPagedPool, numEntriesToAllocate * sizeof(BYTE));
899     if(!pFlagsNew)
900     {
901       ExFreePool(pPointsNew);
902       return FALSE;
903     }
904
905     /* Copy old arrays to new arrays and discard old arrays */
906     if(pPath->pPoints)
907     {
908       assert(pPath->pFlags);
909
910       memcpy(pPointsNew, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
911       memcpy(pFlagsNew, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
912
913       ExFreePool(pPath->pPoints);
914       ExFreePool(pPath->pFlags);
915     }
916     pPath->pPoints=pPointsNew;
917     pPath->pFlags=pFlagsNew;
918     pPath->numEntriesAllocated=numEntriesToAllocate;
919   }
920
921   return TRUE;
922 }
923
924 /* PATH_GetPathFromHDC
925  *
926  * Retrieves a pointer to the GdiPath structure contained in an HDC and
927  * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
928  */
929 static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath)
930 {
931   DC *pDC;
932
933   pDC=DC_HandleToPtr(hdc);
934   if(pDC)
935   {
936     *ppPath=&pDC->w.path;
937         DC_ReleasePtr( hdc );
938     return TRUE;
939   }
940   return FALSE;
941 }
942
943 /* PATH_DoArcPart
944  *
945  * Creates a Bezier spline that corresponds to part of an arc and appends the
946  * corresponding points to the path. The start and end angles are passed in
947  * "angleStart" and "angleEnd"; these angles should span a quarter circle
948  * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
949  * point is added to the path; otherwise, it is assumed that the current
950  * position is equal to the first control point.
951  */
952 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
953    double angleStart, double angleEnd, BOOL addMoveTo)
954 {
955   double  halfAngle, a;
956   double  xNorm[4], yNorm[4];
957   POINT point;
958   int i;
959
960   assert(fabs(angleEnd-angleStart)<=M_PI_2);
961
962   /* FIXME: Is there an easier way of computing this? */
963
964   /* Compute control points */
965   halfAngle=(angleEnd-angleStart)/2.0;
966   if(fabs(halfAngle)>1e-8)
967   {
968     a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
969     xNorm[0]=cos(angleStart);
970     yNorm[0]=sin(angleStart);
971     xNorm[1]=xNorm[0] - a*yNorm[0];
972     yNorm[1]=yNorm[0] + a*xNorm[0];
973     xNorm[3]=cos(angleEnd);
974     yNorm[3]=sin(angleEnd);
975     xNorm[2]=xNorm[3] + a*yNorm[3];
976     yNorm[2]=yNorm[3] - a*xNorm[3];
977   } else
978     for(i=0; i<4; i++)
979     {
980       xNorm[i]=cos(angleStart);
981       yNorm[i]=sin(angleStart);
982     }
983
984   /* Add starting point to path if desired */
985   if(addMoveTo)
986   {
987     PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
988     if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
989       return FALSE;
990   }
991
992   /* Add remaining control points */
993   for(i=1; i<4; i++)
994   {
995     PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
996     if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
997       return FALSE;
998   }
999
1000   return TRUE;
1001 }
1002
1003 /* PATH_ScaleNormalizedPoint
1004  *
1005  * Scales a normalized point (x, y) with respect to the box whose corners are
1006  * passed in "corners". The point is stored in "*pPoint". The normalized
1007  * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1008  * (1.0, 1.0) correspond to corners[1].
1009  */
1010 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
1011    double y, POINT *pPoint)
1012 {
1013   pPoint->x=GDI_ROUND( (double)corners[0].x + (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
1014   pPoint->y=GDI_ROUND( (double)corners[0].y + (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
1015 }
1016
1017 /* PATH_NormalizePoint
1018  *
1019  * Normalizes a point with respect to the box whose corners are passed in
1020  * corners. The normalized coordinates are stored in *pX and *pY.
1021  */
1022 static void PATH_NormalizePoint(FLOAT_POINT corners[],
1023    const FLOAT_POINT *pPoint,
1024    double *pX, double *pY)
1025 {
1026   *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) * 2.0 - 1.0;
1027   *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) * 2.0 - 1.0;
1028 }