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.
20 #undef WIN32_LEAN_AND_MEAN
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>
38 #include <win32k/debug1.h>
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 */
48 NtGdiAbortPath(HDC hDC)
55 NtGdiBeginPath(HDC hDC)
62 IntCloseFigure ( PDC dc )
70 NtGdiCloseFigure ( HDC hDC )
72 PDC dc = DC_LockDc ( hDC );
73 BOOL ret = FALSE; // default to failure
77 ret = IntCloseFigure ( dc );
93 NtGdiFillPath(HDC hDC)
100 NtGdiFlattenPath(HDC hDC)
108 NtGdiGetMiterLimit(HDC hDC,
116 NtGdiGetPath(HDC hDC,
126 NtGdiPathToRegion(HDC hDC)
133 NtGdiSetMiterLimit(HDC hDC,
142 NtGdiStrokeAndFillPath(HDC hDC)
149 NtGdiStrokePath(HDC hDC)
156 NtGdiWidenPath(HDC hDC)
161 /***********************************************************************
167 * Initializes the GdiPath structure.
171 PATH_InitGdiPath ( GdiPath *pPath )
175 pPath->state=PATH_Null;
178 pPath->numEntriesUsed=0;
179 pPath->numEntriesAllocated=0;
182 /* PATH_DestroyGdiPath
184 * Destroys a GdiPath structure (frees the memory in the arrays).
188 PATH_DestroyGdiPath ( GdiPath *pPath )
192 ExFreePool(pPath->pPoints);
193 ExFreePool(pPath->pFlags);
196 /* PATH_AssignGdiPath
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.
208 PATH_AssignGdiPath ( GdiPath *pPathDest, const GdiPath *pPathSrc )
210 assert(pPathDest!=NULL && pPathSrc!=NULL);
212 /* Make sure destination arrays are big enough */
213 if ( !PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed) )
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);
222 pPathDest->state=pPathSrc->state;
223 pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
224 pPathDest->newStroke=pPathSrc->newStroke;
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
237 PATH_MoveTo ( PDC dc )
241 /* Get pointer to path */
242 PATH_GetPathFromDC ( dc, &pPath );
244 /* Check that path is open */
245 if ( pPath->state != PATH_Open )
246 /* FIXME: Do we have to call SetLastError? */
249 /* Start a new stroke */
250 pPath->newStroke = TRUE;
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.
264 PATH_LineTo ( PDC dc, INT x, INT y )
267 POINT point, pointCurPos;
269 /* Get pointer to path */
270 PATH_GetPathFromDC ( dc, &pPath );
272 /* Check that path is open */
273 if ( pPath->state != PATH_Open )
276 /* Convert point to device coordinates */
279 CoordLPtoDP ( dc, &point );
281 /* Add a PT_MOVETO if necessary */
282 if ( pPath->newStroke )
284 pPath->newStroke = FALSE;
285 IntGetCurrentPositionEx ( dc, &pointCurPos );
286 CoordLPtoDP ( dc, &pointCurPos );
287 if ( !PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO) )
291 /* Add a PT_LINETO entry */
292 return PATH_AddEntry(pPath, &point, PT_LINETO);
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.
302 PATH_Rectangle ( PDC dc, INT x1, INT y1, INT x2, INT y2 )
305 POINT corners[2], pointTemp;
308 /* Get pointer to path */
309 PATH_GetPathFromDC ( dc, &pPath );
311 /* Check that path is open */
312 if ( pPath->state != PATH_Open )
315 /* Convert points to device coordinates */
320 IntLPtoDP ( dc, corners, 2 );
322 /* Make sure first corner is top left and second corner is bottom right */
323 if ( corners[0].x > corners[1].x )
326 corners[0].x=corners[1].x;
329 if ( corners[0].y > corners[1].y )
332 corners[0].y=corners[1].y;
336 /* In GM_COMPATIBLE, don't include bottom and right edges */
337 if ( IntGetGraphicsMode(dc) == GM_COMPATIBLE )
343 /* Close any previous figure */
344 if ( !IntCloseFigure ( dc ) )
346 /* The NtGdiCloseFigure call shouldn't have failed */
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) )
356 if ( !PATH_AddEntry(pPath, corners, PT_LINETO) )
358 pointTemp.x=corners[0].x;
359 pointTemp.y=corners[1].y;
360 if ( !PATH_AddEntry(pPath, &pointTemp, PT_LINETO) )
362 if ( !PATH_AddEntry(pPath, corners+1, PT_LINETO) )
365 /* Close the rectangle figure */
366 if ( !IntCloseFigure ( dc ) )
368 /* The IntCloseFigure call shouldn't have failed */
378 PATH_RoundRect (PDC dc, INT x1, INT y1, INT x2, INT y2, INT xradius, INT yradius)
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.
392 PATH_Ellipse ( PDC dc, INT x1, INT y1, INT x2, INT y2 )
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 );
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.
407 PATH_Arc ( PDC dc, INT x1, INT y1, INT x2, INT y2,
408 INT xStart, INT yStart, INT xEnd, INT yEnd)
412 double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
413 /* Initialize angleEndQuadrant to silence gcc's warning */
415 FLOAT_POINT corners[2], pointStart, pointEnd;
420 /* FIXME: This function should check for all possible error returns */
421 /* FIXME: Do we have to respect newStroke? */
425 clockwise = ( IntGetArcDirection(dc) == AD_CLOCKWISE );
427 /* Get pointer to path */
428 PATH_GetPathFromDC ( dc, &pPath );
430 /* Check that path is open */
431 if ( pPath->state != PATH_Open )
434 /* FIXME: Do we have to close the current figure? */
436 /* Check for zero height / width */
437 /* FIXME: Only in GM_COMPATIBLE? */
438 if ( x1==x2 || y1==y2 )
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);
455 /* Make sure first corner is top left and second corner is bottom right */
456 if ( corners[0].x > corners[1].x )
459 corners[0].x=corners[1].x;
462 if ( corners[0].y > corners[1].y )
465 corners[0].y=corners[1].y;
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);
475 /* Make sure the end angle is "on the right side" of the start angle */
478 if ( angleEnd <= angleStart )
481 assert(angleEnd>=angleStart);
486 if(angleEnd>=angleStart)
489 assert(angleEnd<=angleStart);
493 /* In GM_COMPATIBLE, don't include bottom and right edges */
494 if ( IntGetGraphicsMode(dc) == GM_COMPATIBLE )
500 /* Add the arc to the path with one Bezier spline per quadrant that the
506 /* Determine the start and end angles for this quadrant */
509 angleStartQuadrant=angleStart;
511 angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
513 angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
517 angleStartQuadrant=angleEndQuadrant;
519 angleEndQuadrant+=M_PI_2;
521 angleEndQuadrant-=M_PI_2;
524 /* Have we reached the last part of the arc? */
525 if ( (clockwise && angleEnd<angleEndQuadrant)
526 || (!clockwise && angleEnd>angleEndQuadrant)
529 /* Adjust the end angle for this quadrant */
530 angleEndQuadrant = angleEnd;
534 /* Add the Bezier spline to the path */
535 PATH_DoArcPart ( pPath, corners, angleStartQuadrant, angleEndQuadrant, start );
544 PATH_PolyBezierTo ( PDC dc, const POINT *pts, DWORD cbPoints )
554 PATH_GetPathFromDC ( dc, &pPath );
556 /* Check that path is open */
557 if ( pPath->state != PATH_Open )
560 /* Add a PT_MOVETO if necessary */
561 if ( pPath->newStroke )
563 pPath->newStroke=FALSE;
564 IntGetCurrentPositionEx ( dc, &pt );
565 CoordLPtoDP ( dc, &pt );
566 if ( !PATH_AddEntry(pPath, &pt, PT_MOVETO) )
570 for(i = 0; i < cbPoints; i++)
573 CoordLPtoDP ( dc, &pt );
574 PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
581 PATH_PolyBezier ( PDC dc, const POINT *pts, DWORD cbPoints )
591 PATH_GetPathFromDC ( dc, &pPath );
593 /* Check that path is open */
594 if ( pPath->state != PATH_Open )
597 for ( i = 0; i < cbPoints; i++ )
600 CoordLPtoDP ( dc, &pt );
601 PATH_AddEntry ( pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO );
609 PATH_Polyline ( PDC dc, const POINT *pts, DWORD cbPoints )
619 PATH_GetPathFromDC ( dc, &pPath );
621 /* Check that path is open */
622 if ( pPath->state != PATH_Open )
625 for ( i = 0; i < cbPoints; i++ )
628 CoordLPtoDP ( dc, &pt );
629 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
636 PATH_PolylineTo ( PDC dc, const POINT *pts, DWORD cbPoints )
646 PATH_GetPathFromDC ( dc, &pPath );
648 /* Check that path is open */
649 if ( pPath->state != PATH_Open )
652 /* Add a PT_MOVETO if necessary */
653 if ( pPath->newStroke )
655 pPath->newStroke = FALSE;
656 IntGetCurrentPositionEx ( dc, &pt );
657 CoordLPtoDP ( dc, &pt );
658 if ( !PATH_AddEntry(pPath, &pt, PT_MOVETO) )
662 for(i = 0; i < cbPoints; i++)
665 CoordLPtoDP ( dc, &pt );
666 PATH_AddEntry(pPath, &pt, PT_LINETO);
675 PATH_Polygon ( PDC dc, const POINT *pts, DWORD cbPoints )
684 PATH_GetPathFromDC ( dc, &pPath );
686 /* Check that path is open */
687 if ( pPath->state != PATH_Open )
690 for(i = 0; i < cbPoints; i++)
693 CoordLPtoDP ( dc, &pt );
694 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
695 ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
703 PATH_PolyPolygon ( PDC dc, const POINT* pts, const INT* counts, UINT polygons )
707 ULONG poly, point, i;
714 PATH_GetPathFromDC ( dc, &pPath );
716 /* Check that path is open */
717 if ( pPath->state != PATH_Open );
720 for(i = 0, poly = 0; poly < polygons; poly++)
722 for(point = 0; point < (ULONG) counts[poly]; point++, i++)
725 CoordLPtoDP ( dc, &pt );
726 if(point == 0) startpt = pt;
727 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
729 /* win98 adds an extra line to close the figure for some reason */
730 PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
737 PATH_PolyPolyline ( PDC dc, const POINT* pts, const DWORD* counts, DWORD polylines )
741 ULONG poly, point, i;
746 ASSERT ( polylines );
748 PATH_GetPathFromDC ( dc, &pPath );
750 /* Check that path is open */
751 if ( pPath->state != PATH_Open )
754 for(i = 0, poly = 0; poly < polylines; poly++)
756 for(point = 0; point < counts[poly]; point++, i++)
759 CoordLPtoDP ( dc, &pt );
760 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
766 /***********************************************************************
771 /* PATH_AddFlatBezier
776 PATH_AddFlatBezier ( GdiPath *pPath, POINT *pt, BOOL closed )
781 pts = GDI_Bezier( pt, 4, &no );
782 if ( !pts ) return FALSE;
784 for(i = 1; i < no; i++)
785 PATH_AddEntry(pPath, &pts[i], (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
793 * Replaces Beziers with line segments
798 PATH_FlattenPath(GdiPath *pPath)
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) {
809 PATH_AddEntry(&newPath, &pPath->pPoints[srcpt], pPath->pFlags[srcpt]);
812 PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1], pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
817 newPath.state = PATH_Closed;
818 PATH_AssignGdiPath(pPath, &newPath);
819 PATH_EmptyPath(&newPath);
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
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
839 PATH_PathToRegion ( const GdiPath *pPath, INT nPolyFillMode, HRGN *pHrgn )
841 int numStrokes, iStroke, i;
842 INT *pNumPointsInStroke;
845 assert ( pPath!=NULL );
846 assert ( pHrgn!=NULL );
848 PATH_FlattenPath ( pPath );
850 /* FIXME: What happens when number of points is zero? */
852 /* First pass: Find out how many strokes there are in the path */
853 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
855 for(i=0; i<pPath->numEntriesUsed; i++)
856 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
859 /* Allocate memory for number-of-points-in-stroke array */
860 pNumPointsInStroke=(int *)ExAllocatePool(NonPagedPool, sizeof(int) * numStrokes);
861 if(!pNumPointsInStroke)
863 // SetLastError(ERROR_NOT_ENOUGH_MEMORY);
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++)
871 /* Is this the beginning of a new stroke? */
872 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
875 pNumPointsInStroke[iStroke]=0;
878 pNumPointsInStroke[iStroke]++;
881 /* Create a region from the strokes */
882 /* hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
883 numStrokes, nPolyFillMode); FIXME: reinclude when region code implemented */
886 // SetLastError(ERROR_NOT_ENOUGH_MEMORY);
890 /* Free memory for number-of-points-in-stroke array */
891 ExFreePool(pNumPointsInStroke);
901 * Removes all entries from the path and sets the path state to PATH_Null.
905 PATH_EmptyPath ( GdiPath *pPath )
909 pPath->state=PATH_Null;
910 pPath->numEntriesUsed=0;
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).
921 PATH_AddEntry ( GdiPath *pPath, const POINT *pPoint, BYTE flags )
925 /* FIXME: If newStroke is true, perhaps we want to check that we're
926 * getting a PT_MOVETO
929 /* Check that path is open */
930 if ( pPath->state != PATH_Open )
933 /* Reserve enough memory for an extra path entry */
934 if ( !PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1) )
937 /* Store information in path entry */
938 pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
939 pPath->pFlags[pPath->numEntriesUsed]=flags;
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;
945 /* Increment entry count */
946 pPath->numEntriesUsed++;
951 /* PATH_ReserveEntries
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.
959 PATH_ReserveEntries ( GdiPath *pPath, INT numEntries )
961 INT numEntriesToAllocate;
966 assert(numEntries>=0);
968 /* Do we have to allocate more memory? */
969 if(numEntries > pPath->numEntriesAllocated)
971 /* Find number of entries to allocate. We let the size of the array
972 * grow exponentially, since that will guarantee linear time
974 if(pPath->numEntriesAllocated)
976 numEntriesToAllocate=pPath->numEntriesAllocated;
977 while(numEntriesToAllocate<numEntries)
978 numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/GROW_FACTOR_DENOM;
980 numEntriesToAllocate=numEntries;
982 /* Allocate new arrays */
983 pPointsNew=(POINT *)ExAllocatePool(NonPagedPool, numEntriesToAllocate * sizeof(POINT));
986 pFlagsNew=(BYTE *)ExAllocatePool(NonPagedPool, numEntriesToAllocate * sizeof(BYTE));
989 ExFreePool(pPointsNew);
993 /* Copy old arrays to new arrays and discard old arrays */
996 assert(pPath->pFlags);
998 memcpy(pPointsNew, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
999 memcpy(pFlagsNew, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
1001 ExFreePool(pPath->pPoints);
1002 ExFreePool(pPath->pFlags);
1004 pPath->pPoints=pPointsNew;
1005 pPath->pFlags=pFlagsNew;
1006 pPath->numEntriesAllocated=numEntriesToAllocate;
1012 /* PATH_GetPathFromDC
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.
1019 PATH_GetPathFromDC ( PDC dc, GdiPath **ppPath )
1023 *ppPath = &dc->w.path;
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.
1037 PATH_DoArcPart ( GdiPath *pPath, FLOAT_POINT corners[],
1038 double angleStart, double angleEnd, BOOL addMoveTo )
1040 double halfAngle, a;
1041 double xNorm[4], yNorm[4];
1045 assert(fabs(angleEnd-angleStart)<=M_PI_2);
1047 /* FIXME: Is there an easier way of computing this? */
1049 /* Compute control points */
1050 halfAngle=(angleEnd-angleStart)/2.0;
1051 if(fabs(halfAngle)>1e-8)
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];
1065 xNorm[i]=cos(angleStart);
1066 yNorm[i]=sin(angleStart);
1069 /* Add starting point to path if desired */
1072 PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
1073 if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
1077 /* Add remaining control points */
1080 PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
1081 if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1088 /* PATH_ScaleNormalizedPoint
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].
1097 PATH_ScaleNormalizedPoint ( FLOAT_POINT corners[], double x,
1098 double y, POINT *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) );
1106 /* PATH_NormalizePoint
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.
1113 PATH_NormalizePoint ( FLOAT_POINT corners[],
1114 const FLOAT_POINT *pPoint,
1115 double *pX, double *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;