748cd9d3ba85c2f724b160c2951dc9b2abc32aae
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
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.
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.
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.
22 #include <win32k/float.h>
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 */
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
);
44 IntGdiGetArcDirection(DC
*dc
);
48 NtGdiAbortPath(HDC hDC
)
52 PDC dc
= DC_LockDc ( hDC
);
54 if( !dc
) return FALSE
;
56 /* Get pointer to path */
57 PATH_GetPathFromDC ( dc
, &pPath
);
59 PATH_EmptyPath( pPath
);
67 NtGdiBeginPath( HDC hDC
)
71 PDC dc
= DC_LockDc ( hDC
);
73 if( !dc
) return FALSE
;
75 /* Get pointer to path */
76 PATH_GetPathFromDC ( dc
, &pPath
);
78 /* If path is already open, do nothing */
79 if ( pPath
->state
!= PATH_Open
)
81 /* Make sure that path is empty */
82 PATH_EmptyPath( pPath
);
84 /* Initialize variables for new path */
85 pPath
->newStroke
= TRUE
;
86 pPath
->state
= PATH_Open
;
95 IntCloseFigure ( PDC dc
)
103 NtGdiCloseFigure ( HDC hDC
)
105 PDC dc
= DC_LockDc ( hDC
);
106 BOOL ret
= FALSE
; // default to failure
110 ret
= IntCloseFigure ( dc
);
119 NtGdiEndPath(HDC hDC
)
123 PDC dc
= DC_LockDc ( hDC
);
125 if ( !dc
) return FALSE
;
127 /* Get pointer to path */
128 PATH_GetPathFromDC ( dc
, &pPath
);
130 /* Check that path is currently being constructed */
131 if( pPath
->state
!= PATH_Open
)
135 /* Set flag to indicate that path is finished */
136 else pPath
->state
= PATH_Closed
;
144 NtGdiFillPath(HDC hDC
)
148 PDC dc
= DC_LockDc ( hDC
);
150 if ( !dc
) return FALSE
;
152 /* Get pointer to path */
153 PATH_GetPathFromDC ( dc
, &pPath
);
155 ret
= PATH_FillPath( dc
, pPath
);
158 /* FIXME: Should the path be emptied even if conversion
160 PATH_EmptyPath( pPath
);
169 NtGdiFlattenPath(HDC hDC
)
178 NtGdiGetMiterLimit(HDC hDC
,
187 NtGdiGetPath(HDC hDC
,
198 NtGdiPathToRegion(HDC hDC
)
206 NtGdiSetMiterLimit(HDC hDC
,
216 NtGdiStrokeAndFillPath(HDC hDC
)
224 NtGdiStrokePath(HDC hDC
)
232 NtGdiWidenPath(HDC hDC
)
238 BOOL STDCALL
NtGdiSelectClipPath(HDC hDC
,
243 BOOL success
= FALSE
;
244 PDC dc
= DC_LockDc ( hDC
);
246 if( !dc
) return FALSE
;
248 PATH_GetPathFromDC ( dc
, &pPath
);
250 /* Check that path is closed */
251 if( pPath
->state
!= PATH_Closed
)
253 SetLastWin32Error(ERROR_CAN_NOT_COMPLETE
);
256 /* Construct a region from the path */
257 else if( PATH_PathToRegion( pPath
, dc
->w
.polyFillMode
, &hrgnPath
) )
259 success
= IntGdiExtSelectClipRgn( dc
, hrgnPath
, Mode
) != ERROR
;
260 NtGdiDeleteObject( hrgnPath
);
264 PATH_EmptyPath( pPath
);
265 /* FIXME: Should this function delete the path even if it failed? */
272 /***********************************************************************
283 PATH_FillPath( PDC dc
, GdiPath
*pPath
)
285 INT mapMode
, graphicsMode
;
286 SIZE ptViewportExt
, ptWindowExt
;
287 POINT ptViewportOrg
, ptWindowOrg
;
291 if( pPath
->state
!= PATH_Closed
)
293 SetLastWin32Error(ERROR_CAN_NOT_COMPLETE
);
297 if( PATH_PathToRegion( pPath
, dc
->w
.polyFillMode
, &hrgn
))
299 /* Since PaintRgn interprets the region as being in logical coordinates
300 * but the points we store for the path are already in device
301 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
302 * Using SaveDC to save information about the mapping mode / world
303 * transform would be easier but would require more overhead, especially
304 * now that SaveDC saves the current path.
307 /* Save the information about the old mapping mode */
308 mapMode
= NtGdiGetMapMode( dc
->hSelf
);
309 NtGdiGetViewportExtEx( dc
->hSelf
, &ptViewportExt
);
310 NtGdiGetViewportOrgEx( dc
->hSelf
, &ptViewportOrg
);
311 NtGdiGetWindowExtEx( dc
->hSelf
, &ptWindowExt
);
312 NtGdiGetWindowOrgEx( dc
->hSelf
, &ptWindowOrg
);
314 /* Save world transform
315 * NB: The Windows documentation on world transforms would lead one to
316 * believe that this has to be done only in GM_ADVANCED; however, my
317 * tests show that resetting the graphics mode to GM_COMPATIBLE does
318 * not reset the world transform.
320 NtGdiGetWorldTransform( dc
->hSelf
, &xform
);
323 NtGdiSetMapMode( dc
->hSelf
, MM_TEXT
);
324 NtGdiSetViewportOrgEx( dc
->hSelf
, 0, 0, NULL
);
325 NtGdiSetWindowOrgEx( dc
->hSelf
, 0, 0, NULL
);
326 graphicsMode
= NtGdiGetGraphicsMode( dc
->hSelf
);
327 NtGdiSetGraphicsMode( dc
->hSelf
, GM_ADVANCED
);
328 NtGdiModifyWorldTransform( dc
->hSelf
, &xform
, MWT_IDENTITY
);
329 NtGdiSetGraphicsMode( dc
->hSelf
, graphicsMode
);
331 /* Paint the region */
332 NtGdiPaintRgn( dc
->hSelf
, hrgn
);
333 NtGdiDeleteObject( hrgn
);
334 /* Restore the old mapping mode */
335 NtGdiSetMapMode( dc
->hSelf
, mapMode
);
336 NtGdiSetViewportExtEx( dc
->hSelf
, ptViewportExt
.cx
, ptViewportExt
.cy
, NULL
);
337 NtGdiSetViewportOrgEx( dc
->hSelf
, ptViewportOrg
.x
, ptViewportOrg
.y
, NULL
);
338 NtGdiSetWindowExtEx( dc
->hSelf
, ptWindowExt
.cx
, ptWindowExt
.cy
, NULL
);
339 NtGdiSetWindowOrgEx( dc
->hSelf
, ptWindowOrg
.x
, ptWindowOrg
.y
, NULL
);
341 /* Go to GM_ADVANCED temporarily to restore the world transform */
342 graphicsMode
= NtGdiGetGraphicsMode( dc
->hSelf
);
343 NtGdiSetGraphicsMode( dc
->hSelf
, GM_ADVANCED
);
344 NtGdiSetWorldTransform( dc
->hSelf
, &xform
);
345 NtGdiSetGraphicsMode( dc
->hSelf
, graphicsMode
);
353 * Initializes the GdiPath structure.
357 PATH_InitGdiPath ( GdiPath
*pPath
)
361 pPath
->state
=PATH_Null
;
364 pPath
->numEntriesUsed
=0;
365 pPath
->numEntriesAllocated
=0;
368 /* PATH_DestroyGdiPath
370 * Destroys a GdiPath structure (frees the memory in the arrays).
374 PATH_DestroyGdiPath ( GdiPath
*pPath
)
378 ExFreePool(pPath
->pPoints
);
379 ExFreePool(pPath
->pFlags
);
382 /* PATH_AssignGdiPath
384 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
385 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
386 * not just the pointers. Since this means that the arrays in pPathDest may
387 * need to be resized, pPathDest should have been initialized using
388 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
389 * not a copy constructor).
390 * Returns TRUE if successful, else FALSE.
394 PATH_AssignGdiPath ( GdiPath
*pPathDest
, const GdiPath
*pPathSrc
)
396 assert(pPathDest
!=NULL
&& pPathSrc
!=NULL
);
398 /* Make sure destination arrays are big enough */
399 if ( !PATH_ReserveEntries(pPathDest
, pPathSrc
->numEntriesUsed
) )
402 /* Perform the copy operation */
403 memcpy(pPathDest
->pPoints
, pPathSrc
->pPoints
,
404 sizeof(POINT
)*pPathSrc
->numEntriesUsed
);
405 memcpy(pPathDest
->pFlags
, pPathSrc
->pFlags
,
406 sizeof(BYTE
)*pPathSrc
->numEntriesUsed
);
408 pPathDest
->state
=pPathSrc
->state
;
409 pPathDest
->numEntriesUsed
=pPathSrc
->numEntriesUsed
;
410 pPathDest
->newStroke
=pPathSrc
->newStroke
;
417 * Should be called when a MoveTo is performed on a DC that has an
418 * open path. This starts a new stroke. Returns TRUE if successful, else
423 PATH_MoveTo ( PDC dc
)
427 /* Get pointer to path */
428 PATH_GetPathFromDC ( dc
, &pPath
);
430 /* Check that path is open */
431 if ( pPath
->state
!= PATH_Open
)
432 /* FIXME: Do we have to call SetLastError? */
435 /* Start a new stroke */
436 pPath
->newStroke
= TRUE
;
443 * Should be called when a LineTo is performed on a DC that has an
444 * open path. This adds a PT_LINETO entry to the path (and possibly
445 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
446 * Returns TRUE if successful, else FALSE.
450 PATH_LineTo ( PDC dc
, INT x
, INT y
)
453 POINT point
, pointCurPos
;
455 /* Get pointer to path */
456 PATH_GetPathFromDC ( dc
, &pPath
);
458 /* Check that path is open */
459 if ( pPath
->state
!= PATH_Open
)
462 /* Convert point to device coordinates */
465 CoordLPtoDP ( dc
, &point
);
467 /* Add a PT_MOVETO if necessary */
468 if ( pPath
->newStroke
)
470 pPath
->newStroke
= FALSE
;
471 IntGetCurrentPositionEx ( dc
, &pointCurPos
);
472 CoordLPtoDP ( dc
, &pointCurPos
);
473 if ( !PATH_AddEntry(pPath
, &pointCurPos
, PT_MOVETO
) )
477 /* Add a PT_LINETO entry */
478 return PATH_AddEntry(pPath
, &point
, PT_LINETO
);
483 * Should be called when a call to Rectangle is performed on a DC that has
484 * an open path. Returns TRUE if successful, else FALSE.
488 PATH_Rectangle ( PDC dc
, INT x1
, INT y1
, INT x2
, INT y2
)
491 POINT corners
[2], pointTemp
;
494 /* Get pointer to path */
495 PATH_GetPathFromDC ( dc
, &pPath
);
497 /* Check that path is open */
498 if ( pPath
->state
!= PATH_Open
)
501 /* Convert points to device coordinates */
506 IntLPtoDP ( dc
, corners
, 2 );
508 /* Make sure first corner is top left and second corner is bottom right */
509 if ( corners
[0].x
> corners
[1].x
)
512 corners
[0].x
=corners
[1].x
;
515 if ( corners
[0].y
> corners
[1].y
)
518 corners
[0].y
=corners
[1].y
;
522 /* In GM_COMPATIBLE, don't include bottom and right edges */
523 if ( IntGetGraphicsMode(dc
) == GM_COMPATIBLE
)
529 /* Close any previous figure */
530 if ( !IntCloseFigure ( dc
) )
532 /* The NtGdiCloseFigure call shouldn't have failed */
537 /* Add four points to the path */
538 pointTemp
.x
=corners
[1].x
;
539 pointTemp
.y
=corners
[0].y
;
540 if ( !PATH_AddEntry(pPath
, &pointTemp
, PT_MOVETO
) )
542 if ( !PATH_AddEntry(pPath
, corners
, PT_LINETO
) )
544 pointTemp
.x
=corners
[0].x
;
545 pointTemp
.y
=corners
[1].y
;
546 if ( !PATH_AddEntry(pPath
, &pointTemp
, PT_LINETO
) )
548 if ( !PATH_AddEntry(pPath
, corners
+1, PT_LINETO
) )
551 /* Close the rectangle figure */
552 if ( !IntCloseFigure ( dc
) )
554 /* The IntCloseFigure call shouldn't have failed */
564 PATH_RoundRect (PDC dc
, INT x1
, INT y1
, INT x2
, INT y2
, INT xradius
, INT yradius
)
572 * Should be called when a call to Ellipse is performed on a DC that has
573 * an open path. This adds four Bezier splines representing the ellipse
574 * to the path. Returns TRUE if successful, else FALSE.
578 PATH_Ellipse ( PDC dc
, INT x1
, INT y1
, INT x2
, INT y2
)
580 /* TODO: This should probably be revised to call PATH_AngleArc */
581 /* (once it exists) */
582 return PATH_Arc ( dc
, x1
, y1
, x2
, y2
, x1
, (y1
+y2
)/2, x1
, (y1
+y2
)/2 );
587 * Should be called when a call to Arc is performed on a DC that has
588 * an open path. This adds up to five Bezier splines representing the arc
589 * to the path. Returns TRUE if successful, else FALSE.
593 PATH_Arc ( PDC dc
, INT x1
, INT y1
, INT x2
, INT y2
,
594 INT xStart
, INT yStart
, INT xEnd
, INT yEnd
)
597 double angleStart
, angleEnd
, angleStartQuadrant
, angleEndQuadrant
=0.0;
598 /* Initialize angleEndQuadrant to silence gcc's warning */
600 FLOAT_POINT corners
[2], pointStart
, pointEnd
;
605 /* FIXME: This function should check for all possible error returns */
606 /* FIXME: Do we have to respect newStroke? */
610 clockwise
= ( IntGdiGetArcDirection(dc
) == AD_CLOCKWISE
);
612 /* Get pointer to path */
613 PATH_GetPathFromDC ( dc
, &pPath
);
615 /* Check that path is open */
616 if ( pPath
->state
!= PATH_Open
)
619 /* FIXME: Do we have to close the current figure? */
621 /* Check for zero height / width */
622 /* FIXME: Only in GM_COMPATIBLE? */
623 if ( x1
==x2
|| y1
==y2
)
626 /* Convert points to device coordinates */
627 corners
[0].x
=(FLOAT
)x1
;
628 corners
[0].y
=(FLOAT
)y1
;
629 corners
[1].x
=(FLOAT
)x2
;
630 corners
[1].y
=(FLOAT
)y2
;
631 pointStart
.x
=(FLOAT
)xStart
;
632 pointStart
.y
=(FLOAT
)yStart
;
633 pointEnd
.x
=(FLOAT
)xEnd
;
634 pointEnd
.y
=(FLOAT
)yEnd
;
635 INTERNAL_LPTODP_FLOAT(dc
, corners
);
636 INTERNAL_LPTODP_FLOAT(dc
, corners
+1);
637 INTERNAL_LPTODP_FLOAT(dc
, &pointStart
);
638 INTERNAL_LPTODP_FLOAT(dc
, &pointEnd
);
640 /* Make sure first corner is top left and second corner is bottom right */
641 if ( corners
[0].x
> corners
[1].x
)
644 corners
[0].x
=corners
[1].x
;
647 if ( corners
[0].y
> corners
[1].y
)
650 corners
[0].y
=corners
[1].y
;
654 /* Compute start and end angle */
655 PATH_NormalizePoint(corners
, &pointStart
, &x
, &y
);
656 angleStart
=atan2(y
, x
);
657 PATH_NormalizePoint(corners
, &pointEnd
, &x
, &y
);
658 angleEnd
=atan2(y
, x
);
660 /* Make sure the end angle is "on the right side" of the start angle */
663 if ( angleEnd
<= angleStart
)
666 assert(angleEnd
>=angleStart
);
671 if(angleEnd
>=angleStart
)
674 assert(angleEnd
<=angleStart
);
678 /* In GM_COMPATIBLE, don't include bottom and right edges */
679 if ( IntGetGraphicsMode(dc
) == GM_COMPATIBLE
)
685 /* Add the arc to the path with one Bezier spline per quadrant that the
691 /* Determine the start and end angles for this quadrant */
694 angleStartQuadrant
=angleStart
;
696 angleEndQuadrant
=(floor(angleStart
/M_PI_2
)+1.0)*M_PI_2
;
698 angleEndQuadrant
=(ceil(angleStart
/M_PI_2
)-1.0)*M_PI_2
;
702 angleStartQuadrant
=angleEndQuadrant
;
704 angleEndQuadrant
+=M_PI_2
;
706 angleEndQuadrant
-=M_PI_2
;
709 /* Have we reached the last part of the arc? */
710 if ( (clockwise
&& angleEnd
<angleEndQuadrant
)
711 || (!clockwise
&& angleEnd
>angleEndQuadrant
)
714 /* Adjust the end angle for this quadrant */
715 angleEndQuadrant
= angleEnd
;
719 /* Add the Bezier spline to the path */
720 PATH_DoArcPart ( pPath
, corners
, angleStartQuadrant
, angleEndQuadrant
, start
);
729 PATH_PolyBezierTo ( PDC dc
, const POINT
*pts
, DWORD cbPoints
)
739 PATH_GetPathFromDC ( dc
, &pPath
);
741 /* Check that path is open */
742 if ( pPath
->state
!= PATH_Open
)
745 /* Add a PT_MOVETO if necessary */
746 if ( pPath
->newStroke
)
748 pPath
->newStroke
=FALSE
;
749 IntGetCurrentPositionEx ( dc
, &pt
);
750 CoordLPtoDP ( dc
, &pt
);
751 if ( !PATH_AddEntry(pPath
, &pt
, PT_MOVETO
) )
755 for(i
= 0; i
< cbPoints
; i
++)
758 CoordLPtoDP ( dc
, &pt
);
759 PATH_AddEntry(pPath
, &pt
, PT_BEZIERTO
);
766 PATH_PolyBezier ( PDC dc
, const POINT
*pts
, DWORD cbPoints
)
776 PATH_GetPathFromDC ( dc
, &pPath
);
778 /* Check that path is open */
779 if ( pPath
->state
!= PATH_Open
)
782 for ( i
= 0; i
< cbPoints
; i
++ )
785 CoordLPtoDP ( dc
, &pt
);
786 PATH_AddEntry ( pPath
, &pt
, (i
== 0) ? PT_MOVETO
: PT_BEZIERTO
);
794 PATH_Polyline ( PDC dc
, const POINT
*pts
, DWORD cbPoints
)
804 PATH_GetPathFromDC ( dc
, &pPath
);
806 /* Check that path is open */
807 if ( pPath
->state
!= PATH_Open
)
810 for ( i
= 0; i
< cbPoints
; i
++ )
813 CoordLPtoDP ( dc
, &pt
);
814 PATH_AddEntry(pPath
, &pt
, (i
== 0) ? PT_MOVETO
: PT_LINETO
);
821 PATH_PolylineTo ( PDC dc
, const POINT
*pts
, DWORD cbPoints
)
831 PATH_GetPathFromDC ( dc
, &pPath
);
833 /* Check that path is open */
834 if ( pPath
->state
!= PATH_Open
)
837 /* Add a PT_MOVETO if necessary */
838 if ( pPath
->newStroke
)
840 pPath
->newStroke
= FALSE
;
841 IntGetCurrentPositionEx ( dc
, &pt
);
842 CoordLPtoDP ( dc
, &pt
);
843 if ( !PATH_AddEntry(pPath
, &pt
, PT_MOVETO
) )
847 for(i
= 0; i
< cbPoints
; i
++)
850 CoordLPtoDP ( dc
, &pt
);
851 PATH_AddEntry(pPath
, &pt
, PT_LINETO
);
860 PATH_Polygon ( PDC dc
, const POINT
*pts
, DWORD cbPoints
)
869 PATH_GetPathFromDC ( dc
, &pPath
);
871 /* Check that path is open */
872 if ( pPath
->state
!= PATH_Open
)
875 for(i
= 0; i
< cbPoints
; i
++)
878 CoordLPtoDP ( dc
, &pt
);
879 PATH_AddEntry(pPath
, &pt
, (i
== 0) ? PT_MOVETO
:
880 ((i
== cbPoints
-1) ? PT_LINETO
| PT_CLOSEFIGURE
:
888 PATH_PolyPolygon ( PDC dc
, const POINT
* pts
, const INT
* counts
, UINT polygons
)
892 ULONG poly
, point
, i
;
899 PATH_GetPathFromDC ( dc
, &pPath
);
901 /* Check that path is open */
902 if ( pPath
->state
!= PATH_Open
);
905 for(i
= 0, poly
= 0; poly
< polygons
; poly
++)
907 for(point
= 0; point
< (ULONG
) counts
[poly
]; point
++, i
++)
910 CoordLPtoDP ( dc
, &pt
);
911 if(point
== 0) startpt
= pt
;
912 PATH_AddEntry(pPath
, &pt
, (point
== 0) ? PT_MOVETO
: PT_LINETO
);
914 /* win98 adds an extra line to close the figure for some reason */
915 PATH_AddEntry(pPath
, &startpt
, PT_LINETO
| PT_CLOSEFIGURE
);
922 PATH_PolyPolyline ( PDC dc
, const POINT
* pts
, const DWORD
* counts
, DWORD polylines
)
926 ULONG poly
, point
, i
;
931 ASSERT ( polylines
);
933 PATH_GetPathFromDC ( dc
, &pPath
);
935 /* Check that path is open */
936 if ( pPath
->state
!= PATH_Open
)
939 for(i
= 0, poly
= 0; poly
< polylines
; poly
++)
941 for(point
= 0; point
< counts
[poly
]; point
++, i
++)
944 CoordLPtoDP ( dc
, &pt
);
945 PATH_AddEntry(pPath
, &pt
, (point
== 0) ? PT_MOVETO
: PT_LINETO
);
951 /***********************************************************************
956 /* PATH_AddFlatBezier
961 PATH_AddFlatBezier ( GdiPath
*pPath
, POINT
*pt
, BOOL closed
)
966 pts
= GDI_Bezier( pt
, 4, &no
);
967 if ( !pts
) return FALSE
;
969 for(i
= 1; i
< no
; i
++)
970 PATH_AddEntry(pPath
, &pts
[i
], (i
== no
-1 && closed
) ? PT_LINETO
| PT_CLOSEFIGURE
: PT_LINETO
);
978 * Replaces Beziers with line segments
983 PATH_FlattenPath(GdiPath
*pPath
)
988 memset(&newPath
, 0, sizeof(newPath
));
989 newPath
.state
= PATH_Open
;
990 for(srcpt
= 0; srcpt
< pPath
->numEntriesUsed
; srcpt
++) {
991 switch(pPath
->pFlags
[srcpt
] & ~PT_CLOSEFIGURE
) {
994 PATH_AddEntry(&newPath
, &pPath
->pPoints
[srcpt
], pPath
->pFlags
[srcpt
]);
997 PATH_AddFlatBezier(&newPath
, &pPath
->pPoints
[srcpt
-1], pPath
->pFlags
[srcpt
+2] & PT_CLOSEFIGURE
);
1002 newPath
.state
= PATH_Closed
;
1003 PATH_AssignGdiPath(pPath
, &newPath
);
1004 PATH_EmptyPath(&newPath
);
1008 /* PATH_PathToRegion
1010 * Creates a region from the specified path using the specified polygon
1011 * filling mode. The path is left unchanged. A handle to the region that
1012 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
1013 * error occurs, SetLastError is called with the appropriate value and
1014 * FALSE is returned.
1020 PATH_PathToRegion ( GdiPath
*pPath
, INT nPolyFillMode
, HRGN
*pHrgn
)
1022 int numStrokes
, iStroke
, i
;
1023 INT
*pNumPointsInStroke
;
1026 assert ( pPath
!=NULL
);
1027 assert ( pHrgn
!=NULL
);
1029 PATH_FlattenPath ( pPath
);
1031 /* FIXME: What happens when number of points is zero? */
1033 /* First pass: Find out how many strokes there are in the path */
1034 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
1036 for(i
=0; i
<pPath
->numEntriesUsed
; i
++)
1037 if((pPath
->pFlags
[i
] & ~PT_CLOSEFIGURE
) == PT_MOVETO
)
1040 /* Allocate memory for number-of-points-in-stroke array */
1041 pNumPointsInStroke
=(int *)ExAllocatePoolWithTag(PagedPool
, sizeof(int) * numStrokes
, TAG_PATH
);
1042 if(!pNumPointsInStroke
)
1044 // SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1048 /* Second pass: remember number of points in each polygon */
1049 iStroke
=-1; /* Will get incremented to 0 at beginning of first stroke */
1050 for(i
=0; i
<pPath
->numEntriesUsed
; i
++)
1052 /* Is this the beginning of a new stroke? */
1053 if((pPath
->pFlags
[i
] & ~PT_CLOSEFIGURE
) == PT_MOVETO
)
1056 pNumPointsInStroke
[iStroke
]=0;
1059 pNumPointsInStroke
[iStroke
]++;
1062 /* Create a region from the strokes */
1063 /* hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
1064 numStrokes, nPolyFillMode); FIXME: reinclude when region code implemented */
1067 // SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1071 /* Free memory for number-of-points-in-stroke array */
1072 ExFreePool(pNumPointsInStroke
);
1081 * Removes all entries from the path and sets the path state to PATH_Null.
1085 PATH_EmptyPath ( GdiPath
*pPath
)
1087 assert(pPath
!=NULL
);
1089 pPath
->state
=PATH_Null
;
1090 pPath
->numEntriesUsed
=0;
1095 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
1096 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
1097 * successful, FALSE otherwise (e.g. if not enough memory was available).
1101 PATH_AddEntry ( GdiPath
*pPath
, const POINT
*pPoint
, BYTE flags
)
1103 assert(pPath
!=NULL
);
1105 /* FIXME: If newStroke is true, perhaps we want to check that we're
1106 * getting a PT_MOVETO
1109 /* Check that path is open */
1110 if ( pPath
->state
!= PATH_Open
)
1113 /* Reserve enough memory for an extra path entry */
1114 if ( !PATH_ReserveEntries(pPath
, pPath
->numEntriesUsed
+1) )
1117 /* Store information in path entry */
1118 pPath
->pPoints
[pPath
->numEntriesUsed
]=*pPoint
;
1119 pPath
->pFlags
[pPath
->numEntriesUsed
]=flags
;
1121 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
1122 if((flags
& PT_CLOSEFIGURE
) == PT_CLOSEFIGURE
)
1123 pPath
->newStroke
=TRUE
;
1125 /* Increment entry count */
1126 pPath
->numEntriesUsed
++;
1131 /* PATH_ReserveEntries
1133 * Ensures that at least "numEntries" entries (for points and flags) have
1134 * been allocated; allocates larger arrays and copies the existing entries
1135 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
1139 PATH_ReserveEntries ( GdiPath
*pPath
, INT numEntries
)
1141 INT numEntriesToAllocate
;
1145 assert(pPath
!=NULL
);
1146 assert(numEntries
>=0);
1148 /* Do we have to allocate more memory? */
1149 if(numEntries
> pPath
->numEntriesAllocated
)
1151 /* Find number of entries to allocate. We let the size of the array
1152 * grow exponentially, since that will guarantee linear time
1154 if(pPath
->numEntriesAllocated
)
1156 numEntriesToAllocate
=pPath
->numEntriesAllocated
;
1157 while(numEntriesToAllocate
<numEntries
)
1158 numEntriesToAllocate
=numEntriesToAllocate
*GROW_FACTOR_NUMER
/GROW_FACTOR_DENOM
;
1160 numEntriesToAllocate
=numEntries
;
1162 /* Allocate new arrays */
1163 pPointsNew
=(POINT
*)ExAllocatePoolWithTag(PagedPool
, numEntriesToAllocate
* sizeof(POINT
), TAG_PATH
);
1166 pFlagsNew
=(BYTE
*)ExAllocatePoolWithTag(PagedPool
, numEntriesToAllocate
* sizeof(BYTE
), TAG_PATH
);
1169 ExFreePool(pPointsNew
);
1173 /* Copy old arrays to new arrays and discard old arrays */
1176 assert(pPath
->pFlags
);
1178 memcpy(pPointsNew
, pPath
->pPoints
, sizeof(POINT
)*pPath
->numEntriesUsed
);
1179 memcpy(pFlagsNew
, pPath
->pFlags
, sizeof(BYTE
)*pPath
->numEntriesUsed
);
1181 ExFreePool(pPath
->pPoints
);
1182 ExFreePool(pPath
->pFlags
);
1184 pPath
->pPoints
=pPointsNew
;
1185 pPath
->pFlags
=pFlagsNew
;
1186 pPath
->numEntriesAllocated
=numEntriesToAllocate
;
1192 /* PATH_GetPathFromDC
1194 * Retrieves a pointer to the GdiPath structure contained in an HDC and
1195 * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
1199 PATH_GetPathFromDC ( PDC dc
, GdiPath
**ppPath
)
1203 *ppPath
= &dc
->w
.path
;
1208 * Creates a Bezier spline that corresponds to part of an arc and appends the
1209 * corresponding points to the path. The start and end angles are passed in
1210 * "angleStart" and "angleEnd"; these angles should span a quarter circle
1211 * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
1212 * point is added to the path; otherwise, it is assumed that the current
1213 * position is equal to the first control point.
1217 PATH_DoArcPart ( GdiPath
*pPath
, FLOAT_POINT corners
[],
1218 double angleStart
, double angleEnd
, BOOL addMoveTo
)
1220 double halfAngle
, a
;
1221 double xNorm
[4], yNorm
[4];
1225 assert(fabs(angleEnd
-angleStart
)<=M_PI_2
);
1227 /* FIXME: Is there an easier way of computing this? */
1229 /* Compute control points */
1230 halfAngle
=(angleEnd
-angleStart
)/2.0;
1231 if(fabs(halfAngle
)>1e-8)
1233 a
=4.0/3.0*(1-cos(halfAngle
))/sin(halfAngle
);
1234 xNorm
[0]=cos(angleStart
);
1235 yNorm
[0]=sin(angleStart
);
1236 xNorm
[1]=xNorm
[0] - a
*yNorm
[0];
1237 yNorm
[1]=yNorm
[0] + a
*xNorm
[0];
1238 xNorm
[3]=cos(angleEnd
);
1239 yNorm
[3]=sin(angleEnd
);
1240 xNorm
[2]=xNorm
[3] + a
*yNorm
[3];
1241 yNorm
[2]=yNorm
[3] - a
*xNorm
[3];
1245 xNorm
[i
]=cos(angleStart
);
1246 yNorm
[i
]=sin(angleStart
);
1249 /* Add starting point to path if desired */
1252 PATH_ScaleNormalizedPoint(corners
, xNorm
[0], yNorm
[0], &point
);
1253 if(!PATH_AddEntry(pPath
, &point
, PT_MOVETO
))
1257 /* Add remaining control points */
1260 PATH_ScaleNormalizedPoint(corners
, xNorm
[i
], yNorm
[i
], &point
);
1261 if(!PATH_AddEntry(pPath
, &point
, PT_BEZIERTO
))
1268 /* PATH_ScaleNormalizedPoint
1270 * Scales a normalized point (x, y) with respect to the box whose corners are
1271 * passed in "corners". The point is stored in "*pPoint". The normalized
1272 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1273 * (1.0, 1.0) correspond to corners[1].
1277 PATH_ScaleNormalizedPoint ( FLOAT_POINT corners
[], double x
,
1278 double y
, POINT
*pPoint
)
1282 pPoint
->x
=GDI_ROUND( (double)corners
[0].x
+ (double)(corners
[1].x
-corners
[0].x
)*0.5*(x
+1.0) );
1283 pPoint
->y
=GDI_ROUND( (double)corners
[0].y
+ (double)(corners
[1].y
-corners
[0].y
)*0.5*(y
+1.0) );
1286 /* PATH_NormalizePoint
1288 * Normalizes a point with respect to the box whose corners are passed in
1289 * corners. The normalized coordinates are stored in *pX and *pY.
1293 PATH_NormalizePoint ( FLOAT_POINT corners
[],
1294 const FLOAT_POINT
*pPoint
,
1295 double *pX
, double *pY
)
1301 *pX
=(double)(pPoint
->x
-corners
[0].x
)/(double)(corners
[1].x
-corners
[0].x
) * 2.0 - 1.0;
1302 *pY
=(double)(pPoint
->y
-corners
[0].y
)/(double)(corners
[1].y
-corners
[0].y
) * 2.0 - 1.0;