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