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