implemented NtGdiSelectClipPath and PATH_FillPath
[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 (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 BOOL STDCALL NtGdiSelectClipPath(HDC hDC,
239 int Mode)
240 {
241 GdiPath *pPath;
242 HRGN hrgnPath;
243 BOOL success = FALSE;
244 PDC dc = DC_LockDc ( hDC );
245
246 if( !dc ) return FALSE;
247
248 PATH_GetPathFromDC ( dc, &pPath );
249
250 /* Check that path is closed */
251 if( pPath->state != PATH_Closed )
252 SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
253 return FALSE;
254 /* Construct a region from the path */
255 else if( PATH_PathToRegion( pPath, dc->w.polyFillMode, &hrgnPath ) )
256 {
257 success = IntGdiExtSelectClipRgn( dc, hrgnPath, Mode ) != ERROR;
258 NtGdiDeleteObject( hrgnPath );
259
260 /* Empty the path */
261 if( success )
262 PATH_EmptyPath( pPath );
263 /* FIXME: Should this function delete the path even if it failed? */
264 }
265
266 DC_UnlockDc ( dc );
267 return success;
268 }
269
270 /***********************************************************************
271 * Exported functions
272 */
273
274
275 /* PATH_FillPath
276 *
277 *
278 */
279 BOOL
280 FASTCALL
281 PATH_FillPath( PDC dc, GdiPath *pPath )
282 {
283 INT mapMode, graphicsMode;
284 SIZE ptViewportExt, ptWindowExt;
285 POINT ptViewportOrg, ptWindowOrg;
286 XFORM xform;
287 HRGN hrgn;
288
289 if( pPath->state != PATH_Closed )
290 {
291 SetLastWin32Error(ERROR_CAN_NOT_COMPLETE);
292 return FALSE;
293 }
294
295 if( PATH_PathToRegion( pPath, dc->w.polyFillMode, &hrgn ))
296 {
297 /* Since PaintRgn interprets the region as being in logical coordinates
298 * but the points we store for the path are already in device
299 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
300 * Using SaveDC to save information about the mapping mode / world
301 * transform would be easier but would require more overhead, especially
302 * now that SaveDC saves the current path.
303 */
304
305 /* Save the information about the old mapping mode */
306 mapMode = NtGdiGetMapMode( dc->hSelf );
307 NtGdiGetViewportExtEx( dc->hSelf, &ptViewportExt );
308 NtGdiGetViewportOrgEx( dc->hSelf, &ptViewportOrg );
309 NtGdiGetWindowExtEx( dc->hSelf, &ptWindowExt );
310 NtGdiGetWindowOrgEx( dc->hSelf, &ptWindowOrg );
311
312 /* Save world transform
313 * NB: The Windows documentation on world transforms would lead one to
314 * believe that this has to be done only in GM_ADVANCED; however, my
315 * tests show that resetting the graphics mode to GM_COMPATIBLE does
316 * not reset the world transform.
317 */
318 NtGdiGetWorldTransform( dc->hSelf, &xform );
319
320 /* Set MM_TEXT */
321 NtGdiSetMapMode( dc->hSelf, MM_TEXT );
322 NtGdiSetViewportOrgEx( dc->hSelf, 0, 0, NULL );
323 NtGdiSetWindowOrgEx( dc->hSelf, 0, 0, NULL );
324 graphicsMode = NtGdiGetGraphicsMode( dc->hSelf );
325 NtGdiSetGraphicsMode( dc->hSelf, GM_ADVANCED );
326 NtGdiModifyWorldTransform( dc->hSelf, &xform, MWT_IDENTITY );
327 NtGdiSetGraphicsMode( dc->hSelf, graphicsMode );
328
329 /* Paint the region */
330 NtGdiPaintRgn( dc->hSelf, hrgn );
331 NtGdiDeleteObject( hrgn );
332 /* Restore the old mapping mode */
333 NtGdiSetMapMode( dc->hSelf, mapMode );
334 NtGdiSetViewportExtEx( dc->hSelf, ptViewportExt.cx, ptViewportExt.cy, NULL );
335 NtGdiSetViewportOrgEx( dc->hSelf, ptViewportOrg.x, ptViewportOrg.y, NULL );
336 NtGdiSetWindowExtEx( dc->hSelf, ptWindowExt.cx, ptWindowExt.cy, NULL );
337 NtGdiSetWindowOrgEx( dc->hSelf, ptWindowOrg.x, ptWindowOrg.y, NULL );
338
339 /* Go to GM_ADVANCED temporarily to restore the world transform */
340 graphicsMode = NtGdiGetGraphicsMode( dc->hSelf );
341 NtGdiSetGraphicsMode( dc->hSelf, GM_ADVANCED );
342 NtGdiSetWorldTransform( dc->hSelf, &xform );
343 NtGdiSetGraphicsMode( dc->hSelf, graphicsMode );
344 return TRUE;
345 }
346 return FALSE;
347 }
348
349 /* PATH_InitGdiPath
350 *
351 * Initializes the GdiPath structure.
352 */
353 VOID
354 FASTCALL
355 PATH_InitGdiPath ( GdiPath *pPath )
356 {
357 assert(pPath!=NULL);
358
359 pPath->state=PATH_Null;
360 pPath->pPoints=NULL;
361 pPath->pFlags=NULL;
362 pPath->numEntriesUsed=0;
363 pPath->numEntriesAllocated=0;
364 }
365
366 /* PATH_DestroyGdiPath
367 *
368 * Destroys a GdiPath structure (frees the memory in the arrays).
369 */
370 VOID
371 FASTCALL
372 PATH_DestroyGdiPath ( GdiPath *pPath )
373 {
374 assert(pPath!=NULL);
375
376 ExFreePool(pPath->pPoints);
377 ExFreePool(pPath->pFlags);
378 }
379
380 /* PATH_AssignGdiPath
381 *
382 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
383 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
384 * not just the pointers. Since this means that the arrays in pPathDest may
385 * need to be resized, pPathDest should have been initialized using
386 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
387 * not a copy constructor).
388 * Returns TRUE if successful, else FALSE.
389 */
390 BOOL
391 FASTCALL
392 PATH_AssignGdiPath ( GdiPath *pPathDest, const GdiPath *pPathSrc )
393 {
394 assert(pPathDest!=NULL && pPathSrc!=NULL);
395
396 /* Make sure destination arrays are big enough */
397 if ( !PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed) )
398 return FALSE;
399
400 /* Perform the copy operation */
401 memcpy(pPathDest->pPoints, pPathSrc->pPoints,
402 sizeof(POINT)*pPathSrc->numEntriesUsed);
403 memcpy(pPathDest->pFlags, pPathSrc->pFlags,
404 sizeof(BYTE)*pPathSrc->numEntriesUsed);
405
406 pPathDest->state=pPathSrc->state;
407 pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
408 pPathDest->newStroke=pPathSrc->newStroke;
409
410 return TRUE;
411 }
412
413 /* PATH_MoveTo
414 *
415 * Should be called when a MoveTo is performed on a DC that has an
416 * open path. This starts a new stroke. Returns TRUE if successful, else
417 * FALSE.
418 */
419 BOOL
420 FASTCALL
421 PATH_MoveTo ( PDC dc )
422 {
423 GdiPath *pPath;
424
425 /* Get pointer to path */
426 PATH_GetPathFromDC ( dc, &pPath );
427
428 /* Check that path is open */
429 if ( pPath->state != PATH_Open )
430 /* FIXME: Do we have to call SetLastError? */
431 return FALSE;
432
433 /* Start a new stroke */
434 pPath->newStroke = TRUE;
435
436 return TRUE;
437 }
438
439 /* PATH_LineTo
440 *
441 * Should be called when a LineTo is performed on a DC that has an
442 * open path. This adds a PT_LINETO entry to the path (and possibly
443 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
444 * Returns TRUE if successful, else FALSE.
445 */
446 BOOL
447 FASTCALL
448 PATH_LineTo ( PDC dc, INT x, INT y )
449 {
450 GdiPath *pPath;
451 POINT point, pointCurPos;
452
453 /* Get pointer to path */
454 PATH_GetPathFromDC ( dc, &pPath );
455
456 /* Check that path is open */
457 if ( pPath->state != PATH_Open )
458 return FALSE;
459
460 /* Convert point to device coordinates */
461 point.x=x;
462 point.y=y;
463 CoordLPtoDP ( dc, &point );
464
465 /* Add a PT_MOVETO if necessary */
466 if ( pPath->newStroke )
467 {
468 pPath->newStroke = FALSE;
469 IntGetCurrentPositionEx ( dc, &pointCurPos );
470 CoordLPtoDP ( dc, &pointCurPos );
471 if ( !PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO) )
472 return FALSE;
473 }
474
475 /* Add a PT_LINETO entry */
476 return PATH_AddEntry(pPath, &point, PT_LINETO);
477 }
478
479 /* PATH_Rectangle
480 *
481 * Should be called when a call to Rectangle is performed on a DC that has
482 * an open path. Returns TRUE if successful, else FALSE.
483 */
484 BOOL
485 FASTCALL
486 PATH_Rectangle ( PDC dc, INT x1, INT y1, INT x2, INT y2 )
487 {
488 GdiPath *pPath;
489 POINT corners[2], pointTemp;
490 INT temp;
491
492 /* Get pointer to path */
493 PATH_GetPathFromDC ( dc, &pPath );
494
495 /* Check that path is open */
496 if ( pPath->state != PATH_Open )
497 return FALSE;
498
499 /* Convert points to device coordinates */
500 corners[0].x=x1;
501 corners[0].y=y1;
502 corners[1].x=x2;
503 corners[1].y=y2;
504 IntLPtoDP ( dc, corners, 2 );
505
506 /* Make sure first corner is top left and second corner is bottom right */
507 if ( corners[0].x > corners[1].x )
508 {
509 temp=corners[0].x;
510 corners[0].x=corners[1].x;
511 corners[1].x=temp;
512 }
513 if ( corners[0].y > corners[1].y )
514 {
515 temp=corners[0].y;
516 corners[0].y=corners[1].y;
517 corners[1].y=temp;
518 }
519
520 /* In GM_COMPATIBLE, don't include bottom and right edges */
521 if ( IntGetGraphicsMode(dc) == GM_COMPATIBLE )
522 {
523 corners[1].x--;
524 corners[1].y--;
525 }
526
527 /* Close any previous figure */
528 if ( !IntCloseFigure ( dc ) )
529 {
530 /* The NtGdiCloseFigure call shouldn't have failed */
531 assert(FALSE);
532 return FALSE;
533 }
534
535 /* Add four points to the path */
536 pointTemp.x=corners[1].x;
537 pointTemp.y=corners[0].y;
538 if ( !PATH_AddEntry(pPath, &pointTemp, PT_MOVETO) )
539 return FALSE;
540 if ( !PATH_AddEntry(pPath, corners, PT_LINETO) )
541 return FALSE;
542 pointTemp.x=corners[0].x;
543 pointTemp.y=corners[1].y;
544 if ( !PATH_AddEntry(pPath, &pointTemp, PT_LINETO) )
545 return FALSE;
546 if ( !PATH_AddEntry(pPath, corners+1, PT_LINETO) )
547 return FALSE;
548
549 /* Close the rectangle figure */
550 if ( !IntCloseFigure ( dc ) )
551 {
552 /* The IntCloseFigure call shouldn't have failed */
553 assert(FALSE);
554 return FALSE;
555 }
556
557 return TRUE;
558 }
559
560 BOOL
561 FASTCALL
562 PATH_RoundRect (PDC dc, INT x1, INT y1, INT x2, INT y2, INT xradius, INT yradius)
563 {
564 UNIMPLEMENTED;
565 return FALSE;
566 }
567
568 /* PATH_Ellipse
569 *
570 * Should be called when a call to Ellipse is performed on a DC that has
571 * an open path. This adds four Bezier splines representing the ellipse
572 * to the path. Returns TRUE if successful, else FALSE.
573 */
574 BOOL
575 FASTCALL
576 PATH_Ellipse ( PDC dc, INT x1, INT y1, INT x2, INT y2 )
577 {
578 /* TODO: This should probably be revised to call PATH_AngleArc */
579 /* (once it exists) */
580 return PATH_Arc ( dc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2 );
581 }
582
583 /* PATH_Arc
584 *
585 * Should be called when a call to Arc is performed on a DC that has
586 * an open path. This adds up to five Bezier splines representing the arc
587 * to the path. Returns TRUE if successful, else FALSE.
588 */
589 BOOL
590 FASTCALL
591 PATH_Arc ( PDC dc, INT x1, INT y1, INT x2, INT y2,
592 INT xStart, INT yStart, INT xEnd, INT yEnd)
593 {
594 GdiPath *pPath;
595 double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
596 /* Initialize angleEndQuadrant to silence gcc's warning */
597 double x, y;
598 FLOAT_POINT corners[2], pointStart, pointEnd;
599 BOOL start, end;
600 INT temp;
601 BOOL clockwise;
602
603 /* FIXME: This function should check for all possible error returns */
604 /* FIXME: Do we have to respect newStroke? */
605
606 ASSERT ( dc );
607
608 clockwise = ( IntGdiGetArcDirection(dc) == AD_CLOCKWISE );
609
610 /* Get pointer to path */
611 PATH_GetPathFromDC ( dc, &pPath );
612
613 /* Check that path is open */
614 if ( pPath->state != PATH_Open )
615 return FALSE;
616
617 /* FIXME: Do we have to close the current figure? */
618
619 /* Check for zero height / width */
620 /* FIXME: Only in GM_COMPATIBLE? */
621 if ( x1==x2 || y1==y2 )
622 return TRUE;
623
624 /* Convert points to device coordinates */
625 corners[0].x=(FLOAT)x1;
626 corners[0].y=(FLOAT)y1;
627 corners[1].x=(FLOAT)x2;
628 corners[1].y=(FLOAT)y2;
629 pointStart.x=(FLOAT)xStart;
630 pointStart.y=(FLOAT)yStart;
631 pointEnd.x=(FLOAT)xEnd;
632 pointEnd.y=(FLOAT)yEnd;
633 INTERNAL_LPTODP_FLOAT(dc, corners);
634 INTERNAL_LPTODP_FLOAT(dc, corners+1);
635 INTERNAL_LPTODP_FLOAT(dc, &pointStart);
636 INTERNAL_LPTODP_FLOAT(dc, &pointEnd);
637
638 /* Make sure first corner is top left and second corner is bottom right */
639 if ( corners[0].x > corners[1].x )
640 {
641 temp=corners[0].x;
642 corners[0].x=corners[1].x;
643 corners[1].x=temp;
644 }
645 if ( corners[0].y > corners[1].y )
646 {
647 temp=corners[0].y;
648 corners[0].y=corners[1].y;
649 corners[1].y=temp;
650 }
651
652 /* Compute start and end angle */
653 PATH_NormalizePoint(corners, &pointStart, &x, &y);
654 angleStart=atan2(y, x);
655 PATH_NormalizePoint(corners, &pointEnd, &x, &y);
656 angleEnd=atan2(y, x);
657
658 /* Make sure the end angle is "on the right side" of the start angle */
659 if ( clockwise )
660 {
661 if ( angleEnd <= angleStart )
662 {
663 angleEnd+=2*M_PI;
664 assert(angleEnd>=angleStart);
665 }
666 }
667 else
668 {
669 if(angleEnd>=angleStart)
670 {
671 angleEnd-=2*M_PI;
672 assert(angleEnd<=angleStart);
673 }
674 }
675
676 /* In GM_COMPATIBLE, don't include bottom and right edges */
677 if ( IntGetGraphicsMode(dc) == GM_COMPATIBLE )
678 {
679 corners[1].x--;
680 corners[1].y--;
681 }
682
683 /* Add the arc to the path with one Bezier spline per quadrant that the
684 * arc spans */
685 start=TRUE;
686 end=FALSE;
687 do
688 {
689 /* Determine the start and end angles for this quadrant */
690 if(start)
691 {
692 angleStartQuadrant=angleStart;
693 if ( clockwise )
694 angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
695 else
696 angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
697 }
698 else
699 {
700 angleStartQuadrant=angleEndQuadrant;
701 if ( clockwise )
702 angleEndQuadrant+=M_PI_2;
703 else
704 angleEndQuadrant-=M_PI_2;
705 }
706
707 /* Have we reached the last part of the arc? */
708 if ( (clockwise && angleEnd<angleEndQuadrant)
709 || (!clockwise && angleEnd>angleEndQuadrant)
710 )
711 {
712 /* Adjust the end angle for this quadrant */
713 angleEndQuadrant = angleEnd;
714 end = TRUE;
715 }
716
717 /* Add the Bezier spline to the path */
718 PATH_DoArcPart ( pPath, corners, angleStartQuadrant, angleEndQuadrant, start );
719 start = FALSE;
720 } while(!end);
721
722 return TRUE;
723 }
724
725 BOOL
726 FASTCALL
727 PATH_PolyBezierTo ( PDC dc, const POINT *pts, DWORD cbPoints )
728 {
729 GdiPath *pPath;
730 POINT pt;
731 ULONG i;
732
733 ASSERT ( dc );
734 ASSERT ( pts );
735 ASSERT ( cbPoints );
736
737 PATH_GetPathFromDC ( dc, &pPath );
738
739 /* Check that path is open */
740 if ( pPath->state != PATH_Open )
741 return FALSE;
742
743 /* Add a PT_MOVETO if necessary */
744 if ( pPath->newStroke )
745 {
746 pPath->newStroke=FALSE;
747 IntGetCurrentPositionEx ( dc, &pt );
748 CoordLPtoDP ( dc, &pt );
749 if ( !PATH_AddEntry(pPath, &pt, PT_MOVETO) )
750 return FALSE;
751 }
752
753 for(i = 0; i < cbPoints; i++)
754 {
755 pt = pts[i];
756 CoordLPtoDP ( dc, &pt );
757 PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
758 }
759 return TRUE;
760 }
761
762 BOOL
763 FASTCALL
764 PATH_PolyBezier ( PDC dc, const POINT *pts, DWORD cbPoints )
765 {
766 GdiPath *pPath;
767 POINT pt;
768 ULONG i;
769
770 ASSERT ( dc );
771 ASSERT ( pts );
772 ASSERT ( cbPoints );
773
774 PATH_GetPathFromDC ( dc, &pPath );
775
776 /* Check that path is open */
777 if ( pPath->state != PATH_Open )
778 return FALSE;
779
780 for ( i = 0; i < cbPoints; i++ )
781 {
782 pt = pts[i];
783 CoordLPtoDP ( dc, &pt );
784 PATH_AddEntry ( pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO );
785 }
786
787 return TRUE;
788 }
789
790 BOOL
791 FASTCALL
792 PATH_Polyline ( PDC dc, const POINT *pts, DWORD cbPoints )
793 {
794 GdiPath *pPath;
795 POINT pt;
796 ULONG i;
797
798 ASSERT ( dc );
799 ASSERT ( pts );
800 ASSERT ( cbPoints );
801
802 PATH_GetPathFromDC ( dc, &pPath );
803
804 /* Check that path is open */
805 if ( pPath->state != PATH_Open )
806 return FALSE;
807
808 for ( i = 0; i < cbPoints; i++ )
809 {
810 pt = pts[i];
811 CoordLPtoDP ( dc, &pt );
812 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
813 }
814 return TRUE;
815 }
816
817 BOOL
818 FASTCALL
819 PATH_PolylineTo ( PDC dc, const POINT *pts, DWORD cbPoints )
820 {
821 GdiPath *pPath;
822 POINT pt;
823 ULONG i;
824
825 ASSERT ( dc );
826 ASSERT ( pts );
827 ASSERT ( cbPoints );
828
829 PATH_GetPathFromDC ( dc, &pPath );
830
831 /* Check that path is open */
832 if ( pPath->state != PATH_Open )
833 return FALSE;
834
835 /* Add a PT_MOVETO if necessary */
836 if ( pPath->newStroke )
837 {
838 pPath->newStroke = FALSE;
839 IntGetCurrentPositionEx ( dc, &pt );
840 CoordLPtoDP ( dc, &pt );
841 if ( !PATH_AddEntry(pPath, &pt, PT_MOVETO) )
842 return FALSE;
843 }
844
845 for(i = 0; i < cbPoints; i++)
846 {
847 pt = pts[i];
848 CoordLPtoDP ( dc, &pt );
849 PATH_AddEntry(pPath, &pt, PT_LINETO);
850 }
851
852 return TRUE;
853 }
854
855
856 BOOL
857 FASTCALL
858 PATH_Polygon ( PDC dc, const POINT *pts, DWORD cbPoints )
859 {
860 GdiPath *pPath;
861 POINT pt;
862 ULONG i;
863
864 ASSERT ( dc );
865 ASSERT ( pts );
866
867 PATH_GetPathFromDC ( dc, &pPath );
868
869 /* Check that path is open */
870 if ( pPath->state != PATH_Open )
871 return FALSE;
872
873 for(i = 0; i < cbPoints; i++)
874 {
875 pt = pts[i];
876 CoordLPtoDP ( dc, &pt );
877 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
878 ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
879 PT_LINETO));
880 }
881 return TRUE;
882 }
883
884 BOOL
885 FASTCALL
886 PATH_PolyPolygon ( PDC dc, const POINT* pts, const INT* counts, UINT polygons )
887 {
888 GdiPath *pPath;
889 POINT pt, startpt;
890 ULONG poly, point, i;
891
892 ASSERT ( dc );
893 ASSERT ( pts );
894 ASSERT ( counts );
895 ASSERT ( polygons );
896
897 PATH_GetPathFromDC ( dc, &pPath );
898
899 /* Check that path is open */
900 if ( pPath->state != PATH_Open );
901 return FALSE;
902
903 for(i = 0, poly = 0; poly < polygons; poly++)
904 {
905 for(point = 0; point < (ULONG) counts[poly]; point++, i++)
906 {
907 pt = pts[i];
908 CoordLPtoDP ( dc, &pt );
909 if(point == 0) startpt = pt;
910 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
911 }
912 /* win98 adds an extra line to close the figure for some reason */
913 PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
914 }
915 return TRUE;
916 }
917
918 BOOL
919 FASTCALL
920 PATH_PolyPolyline ( PDC dc, const POINT* pts, const DWORD* counts, DWORD polylines )
921 {
922 GdiPath *pPath;
923 POINT pt;
924 ULONG poly, point, i;
925
926 ASSERT ( dc );
927 ASSERT ( pts );
928 ASSERT ( counts );
929 ASSERT ( polylines );
930
931 PATH_GetPathFromDC ( dc, &pPath );
932
933 /* Check that path is open */
934 if ( pPath->state != PATH_Open )
935 return FALSE;
936
937 for(i = 0, poly = 0; poly < polylines; poly++)
938 {
939 for(point = 0; point < counts[poly]; point++, i++)
940 {
941 pt = pts[i];
942 CoordLPtoDP ( dc, &pt );
943 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
944 }
945 }
946 return TRUE;
947 }
948
949 /***********************************************************************
950 * Internal functions
951 */
952
953
954 /* PATH_AddFlatBezier
955 *
956 */
957 BOOL
958 FASTCALL
959 PATH_AddFlatBezier ( GdiPath *pPath, POINT *pt, BOOL closed )
960 {
961 POINT *pts;
962 INT no, i;
963
964 pts = GDI_Bezier( pt, 4, &no );
965 if ( !pts ) return FALSE;
966
967 for(i = 1; i < no; i++)
968 PATH_AddEntry(pPath, &pts[i], (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
969
970 ExFreePool(pts);
971 return TRUE;
972 }
973
974 /* PATH_FlattenPath
975 *
976 * Replaces Beziers with line segments
977 *
978 */
979 BOOL
980 FASTCALL
981 PATH_FlattenPath(GdiPath *pPath)
982 {
983 GdiPath newPath;
984 INT srcpt;
985
986 memset(&newPath, 0, sizeof(newPath));
987 newPath.state = PATH_Open;
988 for(srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++) {
989 switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE) {
990 case PT_MOVETO:
991 case PT_LINETO:
992 PATH_AddEntry(&newPath, &pPath->pPoints[srcpt], pPath->pFlags[srcpt]);
993 break;
994 case PT_BEZIERTO:
995 PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1], pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
996 srcpt += 2;
997 break;
998 }
999 }
1000 newPath.state = PATH_Closed;
1001 PATH_AssignGdiPath(pPath, &newPath);
1002 PATH_EmptyPath(&newPath);
1003 return TRUE;
1004 }
1005
1006 /* PATH_PathToRegion
1007 *
1008 * Creates a region from the specified path using the specified polygon
1009 * filling mode. The path is left unchanged. A handle to the region that
1010 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
1011 * error occurs, SetLastError is called with the appropriate value and
1012 * FALSE is returned.
1013 */
1014
1015
1016 BOOL
1017 FASTCALL
1018 PATH_PathToRegion ( GdiPath *pPath, INT nPolyFillMode, HRGN *pHrgn )
1019 {
1020 int numStrokes, iStroke, i;
1021 INT *pNumPointsInStroke;
1022 HRGN hrgn;
1023
1024 assert ( pPath!=NULL );
1025 assert ( pHrgn!=NULL );
1026
1027 PATH_FlattenPath ( pPath );
1028
1029 /* FIXME: What happens when number of points is zero? */
1030
1031 /* First pass: Find out how many strokes there are in the path */
1032 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
1033 numStrokes=0;
1034 for(i=0; i<pPath->numEntriesUsed; i++)
1035 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1036 numStrokes++;
1037
1038 /* Allocate memory for number-of-points-in-stroke array */
1039 pNumPointsInStroke=(int *)ExAllocatePoolWithTag(PagedPool, sizeof(int) * numStrokes, TAG_PATH);
1040 if(!pNumPointsInStroke)
1041 {
1042 // SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1043 return FALSE;
1044 }
1045
1046 /* Second pass: remember number of points in each polygon */
1047 iStroke=-1; /* Will get incremented to 0 at beginning of first stroke */
1048 for(i=0; i<pPath->numEntriesUsed; i++)
1049 {
1050 /* Is this the beginning of a new stroke? */
1051 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1052 {
1053 iStroke++;
1054 pNumPointsInStroke[iStroke]=0;
1055 }
1056
1057 pNumPointsInStroke[iStroke]++;
1058 }
1059
1060 /* Create a region from the strokes */
1061 /* hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
1062 numStrokes, nPolyFillMode); FIXME: reinclude when region code implemented */
1063 if(hrgn==(HRGN)0)
1064 {
1065 // SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1066 return FALSE;
1067 }
1068
1069 /* Free memory for number-of-points-in-stroke array */
1070 ExFreePool(pNumPointsInStroke);
1071
1072 /* Success! */
1073 *pHrgn=hrgn;
1074 return TRUE;
1075 }
1076
1077 /* PATH_EmptyPath
1078 *
1079 * Removes all entries from the path and sets the path state to PATH_Null.
1080 */
1081 VOID
1082 FASTCALL
1083 PATH_EmptyPath ( GdiPath *pPath )
1084 {
1085 assert(pPath!=NULL);
1086
1087 pPath->state=PATH_Null;
1088 pPath->numEntriesUsed=0;
1089 }
1090
1091 /* PATH_AddEntry
1092 *
1093 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
1094 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
1095 * successful, FALSE otherwise (e.g. if not enough memory was available).
1096 */
1097 BOOL
1098 FASTCALL
1099 PATH_AddEntry ( GdiPath *pPath, const POINT *pPoint, BYTE flags )
1100 {
1101 assert(pPath!=NULL);
1102
1103 /* FIXME: If newStroke is true, perhaps we want to check that we're
1104 * getting a PT_MOVETO
1105 */
1106
1107 /* Check that path is open */
1108 if ( pPath->state != PATH_Open )
1109 return FALSE;
1110
1111 /* Reserve enough memory for an extra path entry */
1112 if ( !PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1) )
1113 return FALSE;
1114
1115 /* Store information in path entry */
1116 pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
1117 pPath->pFlags[pPath->numEntriesUsed]=flags;
1118
1119 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
1120 if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
1121 pPath->newStroke=TRUE;
1122
1123 /* Increment entry count */
1124 pPath->numEntriesUsed++;
1125
1126 return TRUE;
1127 }
1128
1129 /* PATH_ReserveEntries
1130 *
1131 * Ensures that at least "numEntries" entries (for points and flags) have
1132 * been allocated; allocates larger arrays and copies the existing entries
1133 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
1134 */
1135 BOOL
1136 FASTCALL
1137 PATH_ReserveEntries ( GdiPath *pPath, INT numEntries )
1138 {
1139 INT numEntriesToAllocate;
1140 POINT *pPointsNew;
1141 BYTE *pFlagsNew;
1142
1143 assert(pPath!=NULL);
1144 assert(numEntries>=0);
1145
1146 /* Do we have to allocate more memory? */
1147 if(numEntries > pPath->numEntriesAllocated)
1148 {
1149 /* Find number of entries to allocate. We let the size of the array
1150 * grow exponentially, since that will guarantee linear time
1151 * complexity. */
1152 if(pPath->numEntriesAllocated)
1153 {
1154 numEntriesToAllocate=pPath->numEntriesAllocated;
1155 while(numEntriesToAllocate<numEntries)
1156 numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/GROW_FACTOR_DENOM;
1157 } else
1158 numEntriesToAllocate=numEntries;
1159
1160 /* Allocate new arrays */
1161 pPointsNew=(POINT *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(POINT), TAG_PATH);
1162 if(!pPointsNew)
1163 return FALSE;
1164 pFlagsNew=(BYTE *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(BYTE), TAG_PATH);
1165 if(!pFlagsNew)
1166 {
1167 ExFreePool(pPointsNew);
1168 return FALSE;
1169 }
1170
1171 /* Copy old arrays to new arrays and discard old arrays */
1172 if(pPath->pPoints)
1173 {
1174 assert(pPath->pFlags);
1175
1176 memcpy(pPointsNew, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
1177 memcpy(pFlagsNew, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
1178
1179 ExFreePool(pPath->pPoints);
1180 ExFreePool(pPath->pFlags);
1181 }
1182 pPath->pPoints=pPointsNew;
1183 pPath->pFlags=pFlagsNew;
1184 pPath->numEntriesAllocated=numEntriesToAllocate;
1185 }
1186
1187 return TRUE;
1188 }
1189
1190 /* PATH_GetPathFromDC
1191 *
1192 * Retrieves a pointer to the GdiPath structure contained in an HDC and
1193 * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
1194 */
1195 VOID
1196 FASTCALL
1197 PATH_GetPathFromDC ( PDC dc, GdiPath **ppPath )
1198 {
1199 ASSERT ( dc );
1200 ASSERT ( ppPath );
1201 *ppPath = &dc->w.path;
1202 }
1203
1204 /* PATH_DoArcPart
1205 *
1206 * Creates a Bezier spline that corresponds to part of an arc and appends the
1207 * corresponding points to the path. The start and end angles are passed in
1208 * "angleStart" and "angleEnd"; these angles should span a quarter circle
1209 * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
1210 * point is added to the path; otherwise, it is assumed that the current
1211 * position is equal to the first control point.
1212 */
1213 BOOL
1214 FASTCALL
1215 PATH_DoArcPart ( GdiPath *pPath, FLOAT_POINT corners[],
1216 double angleStart, double angleEnd, BOOL addMoveTo )
1217 {
1218 double halfAngle, a;
1219 double xNorm[4], yNorm[4];
1220 POINT point;
1221 int i;
1222
1223 assert(fabs(angleEnd-angleStart)<=M_PI_2);
1224
1225 /* FIXME: Is there an easier way of computing this? */
1226
1227 /* Compute control points */
1228 halfAngle=(angleEnd-angleStart)/2.0;
1229 if(fabs(halfAngle)>1e-8)
1230 {
1231 a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
1232 xNorm[0]=cos(angleStart);
1233 yNorm[0]=sin(angleStart);
1234 xNorm[1]=xNorm[0] - a*yNorm[0];
1235 yNorm[1]=yNorm[0] + a*xNorm[0];
1236 xNorm[3]=cos(angleEnd);
1237 yNorm[3]=sin(angleEnd);
1238 xNorm[2]=xNorm[3] + a*yNorm[3];
1239 yNorm[2]=yNorm[3] - a*xNorm[3];
1240 } else
1241 for(i=0; i<4; i++)
1242 {
1243 xNorm[i]=cos(angleStart);
1244 yNorm[i]=sin(angleStart);
1245 }
1246
1247 /* Add starting point to path if desired */
1248 if(addMoveTo)
1249 {
1250 PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
1251 if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
1252 return FALSE;
1253 }
1254
1255 /* Add remaining control points */
1256 for(i=1; i<4; i++)
1257 {
1258 PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
1259 if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1260 return FALSE;
1261 }
1262
1263 return TRUE;
1264 }
1265
1266 /* PATH_ScaleNormalizedPoint
1267 *
1268 * Scales a normalized point (x, y) with respect to the box whose corners are
1269 * passed in "corners". The point is stored in "*pPoint". The normalized
1270 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1271 * (1.0, 1.0) correspond to corners[1].
1272 */
1273 VOID
1274 FASTCALL
1275 PATH_ScaleNormalizedPoint ( FLOAT_POINT corners[], double x,
1276 double y, POINT *pPoint )
1277 {
1278 ASSERT ( corners );
1279 ASSERT ( pPoint );
1280 pPoint->x=GDI_ROUND( (double)corners[0].x + (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
1281 pPoint->y=GDI_ROUND( (double)corners[0].y + (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
1282 }
1283
1284 /* PATH_NormalizePoint
1285 *
1286 * Normalizes a point with respect to the box whose corners are passed in
1287 * corners. The normalized coordinates are stored in *pX and *pY.
1288 */
1289 VOID
1290 FASTCALL
1291 PATH_NormalizePoint ( FLOAT_POINT corners[],
1292 const FLOAT_POINT *pPoint,
1293 double *pX, double *pY)
1294 {
1295 ASSERT ( corners );
1296 ASSERT ( pPoint );
1297 ASSERT ( pX );
1298 ASSERT ( pY );
1299 *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) * 2.0 - 1.0;
1300 *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) * 2.0 - 1.0;
1301 }
1302 /* EOF */