1 #undef WIN32_LEAN_AND_MEAN
4 #include <win32k/brush.h>
6 #include <win32k/path.h>
7 #include <win32k/math.h>
8 #include <win32k/float.h>
9 #include <win32k/coord.h>
10 #include <win32k/line.h>
16 #include <win32k/debug1.h>
18 #define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
19 #define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
20 #define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
23 static BOOL PATH_PathToRegion(const GdiPath *pPath, INT nPolyFillMode,
25 static void PATH_EmptyPath(GdiPath *pPath);
26 static BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint,
28 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries);
29 static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath);
30 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
31 double angleStart, double angleEnd, BOOL addMoveTo);
32 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
33 double y, POINT *pPoint);
34 static void PATH_NormalizePoint(FLOAT_POINT corners[], const FLOAT_POINT
35 *pPoint, double *pX, double *pY);
39 W32kAbortPath(HDC hDC)
46 W32kBeginPath(HDC hDC)
53 W32kCloseFigure(HDC hDC)
74 W32kFlattenPath(HDC hDC)
82 W32kGetMiterLimit(HDC hDC,
100 W32kPathToRegion(HDC hDC)
107 W32kSetMiterLimit(HDC hDC,
116 W32kStrokeAndFillPath(HDC hDC)
123 W32kStrokePath(HDC hDC)
130 W32kWidenPath(HDC hDC)
135 /***********************************************************************
141 * Initializes the GdiPath structure.
143 void PATH_InitGdiPath(GdiPath *pPath)
147 pPath->state=PATH_Null;
150 pPath->numEntriesUsed=0;
151 pPath->numEntriesAllocated=0;
154 /* PATH_DestroyGdiPath
156 * Destroys a GdiPath structure (frees the memory in the arrays).
158 void PATH_DestroyGdiPath(GdiPath *pPath)
162 ExFreePool(pPath->pPoints);
163 ExFreePool(pPath->pFlags);
166 /* PATH_AssignGdiPath
168 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
169 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
170 * not just the pointers. Since this means that the arrays in pPathDest may
171 * need to be resized, pPathDest should have been initialized using
172 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
173 * not a copy constructor).
174 * Returns TRUE if successful, else FALSE.
176 BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc)
178 assert(pPathDest!=NULL && pPathSrc!=NULL);
180 /* Make sure destination arrays are big enough */
181 if(!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
184 /* Perform the copy operation */
185 memcpy(pPathDest->pPoints, pPathSrc->pPoints,
186 sizeof(POINT)*pPathSrc->numEntriesUsed);
187 memcpy(pPathDest->pFlags, pPathSrc->pFlags,
188 sizeof(BYTE)*pPathSrc->numEntriesUsed);
190 pPathDest->state=pPathSrc->state;
191 pPathDest->numEntriesUsed=pPathSrc->numEntriesUsed;
192 pPathDest->newStroke=pPathSrc->newStroke;
199 * Should be called when a MoveTo is performed on a DC that has an
200 * open path. This starts a new stroke. Returns TRUE if successful, else
203 BOOL PATH_MoveTo(HDC hdc)
207 /* Get pointer to path */
208 if(!PATH_GetPathFromHDC(hdc, &pPath))
211 /* Check that path is open */
212 if(pPath->state!=PATH_Open)
213 /* FIXME: Do we have to call SetLastError? */
216 /* Start a new stroke */
217 pPath->newStroke=TRUE;
224 * Should be called when a LineTo is performed on a DC that has an
225 * open path. This adds a PT_LINETO entry to the path (and possibly
226 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
227 * Returns TRUE if successful, else FALSE.
229 BOOL PATH_LineTo(HDC hdc, INT x, INT y)
232 POINT point, pointCurPos;
234 /* Get pointer to path */
235 if(!PATH_GetPathFromHDC(hdc, &pPath))
238 /* Check that path is open */
239 if(pPath->state!=PATH_Open)
242 /* Convert point to device coordinates */
245 if(!W32kLPtoDP(hdc, &point, 1))
248 /* Add a PT_MOVETO if necessary */
251 pPath->newStroke=FALSE;
252 if(!W32kGetCurrentPositionEx(hdc, &pointCurPos) ||
253 !W32kLPtoDP(hdc, &pointCurPos, 1))
255 if(!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
259 /* Add a PT_LINETO entry */
260 return PATH_AddEntry(pPath, &point, PT_LINETO);
265 * Should be called when a call to Rectangle is performed on a DC that has
266 * an open path. Returns TRUE if successful, else FALSE.
268 BOOL PATH_Rectangle(HDC hdc, INT x1, INT y1, INT x2, INT y2)
271 POINT corners[2], pointTemp;
274 /* Get pointer to path */
275 if(!PATH_GetPathFromHDC(hdc, &pPath))
278 /* Check that path is open */
279 if(pPath->state!=PATH_Open)
282 /* Convert points to device coordinates */
287 if(!W32kLPtoDP(hdc, corners, 2))
290 /* Make sure first corner is top left and second corner is bottom right */
291 if(corners[0].x>corners[1].x)
294 corners[0].x=corners[1].x;
297 if(corners[0].y>corners[1].y)
300 corners[0].y=corners[1].y;
304 /* In GM_COMPATIBLE, don't include bottom and right edges */
305 if(W32kGetGraphicsMode(hdc)==GM_COMPATIBLE)
311 /* Close any previous figure */
312 if(!W32kCloseFigure(hdc))
314 /* The W32kCloseFigure call shouldn't have failed */
319 /* Add four points to the path */
320 pointTemp.x=corners[1].x;
321 pointTemp.y=corners[0].y;
322 if(!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
324 if(!PATH_AddEntry(pPath, corners, PT_LINETO))
326 pointTemp.x=corners[0].x;
327 pointTemp.y=corners[1].y;
328 if(!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
330 if(!PATH_AddEntry(pPath, corners+1, PT_LINETO))
333 /* Close the rectangle figure */
334 if(!W32kCloseFigure(hdc))
336 /* The W32kCloseFigure call shouldn't have failed */
346 * Should be called when a call to Ellipse is performed on a DC that has
347 * an open path. This adds four Bezier splines representing the ellipse
348 * to the path. Returns TRUE if successful, else FALSE.
350 BOOL PATH_Ellipse(HDC hdc, INT x1, INT y1, INT x2, INT y2)
352 /* TODO: This should probably be revised to call PATH_AngleArc */
353 /* (once it exists) */
354 return PATH_Arc(hdc, x1, y1, x2, y2, x1, (y1+y2)/2, x1, (y1+y2)/2);
359 * Should be called when a call to Arc is performed on a DC that has
360 * an open path. This adds up to five Bezier splines representing the arc
361 * to the path. Returns TRUE if successful, else FALSE.
363 BOOL PATH_Arc(HDC hdc, INT x1, INT y1, INT x2, INT y2,
364 INT xStart, INT yStart, INT xEnd, INT yEnd)
368 double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant=0.0;
369 /* Initialize angleEndQuadrant to silence gcc's warning */
371 FLOAT_POINT corners[2], pointStart, pointEnd;
375 /* FIXME: This function should check for all possible error returns */
376 /* FIXME: Do we have to respect newStroke? */
378 /* Get pointer to DC */
379 pDC=DC_HandleToPtr(hdc);
383 /* Get pointer to path */
384 if(!PATH_GetPathFromHDC(hdc, &pPath)){
385 DC_ReleasePtr( hdc );
389 /* Check that path is open */
390 if(pPath->state!=PATH_Open){
391 DC_ReleasePtr( hdc );
395 /* FIXME: Do we have to close the current figure? */
397 /* Check for zero height / width */
398 /* FIXME: Only in GM_COMPATIBLE? */
399 if(x1==x2 || y1==y2){
400 DC_ReleasePtr( hdc );
404 /* Convert points to device coordinates */
405 corners[0].x=(FLOAT)x1;
406 corners[0].y=(FLOAT)y1;
407 corners[1].x=(FLOAT)x2;
408 corners[1].y=(FLOAT)y2;
409 pointStart.x=(FLOAT)xStart;
410 pointStart.y=(FLOAT)yStart;
411 pointEnd.x=(FLOAT)xEnd;
412 pointEnd.y=(FLOAT)yEnd;
413 INTERNAL_LPTODP_FLOAT(pDC, corners);
414 INTERNAL_LPTODP_FLOAT(pDC, corners+1);
415 INTERNAL_LPTODP_FLOAT(pDC, &pointStart);
416 INTERNAL_LPTODP_FLOAT(pDC, &pointEnd);
418 /* Make sure first corner is top left and second corner is bottom right */
419 if(corners[0].x>corners[1].x)
422 corners[0].x=corners[1].x;
425 if(corners[0].y>corners[1].y)
428 corners[0].y=corners[1].y;
432 /* Compute start and end angle */
433 PATH_NormalizePoint(corners, &pointStart, &x, &y);
434 angleStart=atan2(y, x);
435 PATH_NormalizePoint(corners, &pointEnd, &x, &y);
436 angleEnd=atan2(y, x);
438 /* Make sure the end angle is "on the right side" of the start angle */
439 if(W32kGetArcDirection(hdc)==AD_CLOCKWISE)
441 if(angleEnd<=angleStart)
444 assert(angleEnd>=angleStart);
449 if(angleEnd>=angleStart)
452 assert(angleEnd<=angleStart);
456 /* In GM_COMPATIBLE, don't include bottom and right edges */
457 if(W32kGetGraphicsMode(hdc)==GM_COMPATIBLE)
463 /* Add the arc to the path with one Bezier spline per quadrant that the
469 /* Determine the start and end angles for this quadrant */
472 angleStartQuadrant=angleStart;
473 if(W32kGetArcDirection(hdc)==AD_CLOCKWISE)
474 angleEndQuadrant=(floor(angleStart/M_PI_2)+1.0)*M_PI_2;
476 angleEndQuadrant=(ceil(angleStart/M_PI_2)-1.0)*M_PI_2;
480 angleStartQuadrant=angleEndQuadrant;
481 if(W32kGetArcDirection(hdc)==AD_CLOCKWISE)
482 angleEndQuadrant+=M_PI_2;
484 angleEndQuadrant-=M_PI_2;
487 /* Have we reached the last part of the arc? */
488 if((W32kGetArcDirection(hdc)==AD_CLOCKWISE &&
489 angleEnd<angleEndQuadrant) ||
490 (W32kGetArcDirection(hdc)==AD_COUNTERCLOCKWISE &&
491 angleEnd>angleEndQuadrant))
493 /* Adjust the end angle for this quadrant */
494 angleEndQuadrant=angleEnd;
498 /* Add the Bezier spline to the path */
499 PATH_DoArcPart(pPath, corners, angleStartQuadrant, angleEndQuadrant, start);
503 DC_ReleasePtr( hdc );
507 BOOL PATH_PolyBezierTo(HDC hdc, const POINT *pts, DWORD cbPoints)
513 if(!PATH_GetPathFromHDC(hdc, &pPath))
516 /* Check that path is open */
517 if(pPath->state!=PATH_Open)
520 /* Add a PT_MOVETO if necessary */
523 pPath->newStroke=FALSE;
524 if(!W32kGetCurrentPositionEx(hdc, &pt) ||
525 !W32kLPtoDP(hdc, &pt, 1))
527 if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
531 for(i = 0; i < cbPoints; i++) {
533 if(!W32kLPtoDP(hdc, &pt, 1))
535 PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
540 BOOL PATH_PolyBezier(HDC hdc, const POINT *pts, DWORD cbPoints)
546 if(!PATH_GetPathFromHDC(hdc, &pPath))
549 /* Check that path is open */
550 if(pPath->state!=PATH_Open)
553 for(i = 0; i < cbPoints; i++) {
555 if(!W32kLPtoDP(hdc, &pt, 1))
557 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO);
562 BOOL PATH_Polyline(HDC hdc, const POINT *pts, DWORD cbPoints)
568 if(!PATH_GetPathFromHDC(hdc, &pPath))
571 /* Check that path is open */
572 if(pPath->state!=PATH_Open)
575 for(i = 0; i < cbPoints; i++) {
577 if(!W32kLPtoDP(hdc, &pt, 1))
579 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
584 BOOL PATH_PolylineTo(HDC hdc, const POINT *pts, DWORD cbPoints)
590 if(!PATH_GetPathFromHDC(hdc, &pPath))
593 /* Check that path is open */
594 if(pPath->state!=PATH_Open)
597 /* Add a PT_MOVETO if necessary */
600 pPath->newStroke=FALSE;
601 if(!W32kGetCurrentPositionEx(hdc, &pt) ||
602 !W32kLPtoDP(hdc, &pt, 1))
604 if(!PATH_AddEntry(pPath, &pt, PT_MOVETO))
608 for(i = 0; i < cbPoints; i++) {
610 if(!W32kLPtoDP(hdc, &pt, 1))
612 PATH_AddEntry(pPath, &pt, PT_LINETO);
619 BOOL PATH_Polygon(HDC hdc, const POINT *pts, DWORD cbPoints)
625 if(!PATH_GetPathFromHDC(hdc, &pPath))
628 /* Check that path is open */
629 if(pPath->state!=PATH_Open)
632 for(i = 0; i < cbPoints; i++) {
634 if(!W32kLPtoDP(hdc, &pt, 1))
636 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
637 ((i == cbPoints-1) ? PT_LINETO | PT_CLOSEFIGURE :
643 BOOL PATH_PolyPolygon( HDC hdc, const POINT* pts, const INT* counts,
650 if(!PATH_GetPathFromHDC(hdc, &pPath))
653 /* Check that path is open */
654 if(pPath->state!=PATH_Open)
657 for(i = 0, poly = 0; poly < polygons; poly++) {
658 for(point = 0; point < counts[poly]; point++, i++) {
660 if(!W32kLPtoDP(hdc, &pt, 1))
662 if(point == 0) startpt = pt;
663 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
665 /* win98 adds an extra line to close the figure for some reason */
666 PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
671 BOOL PATH_PolyPolyline( HDC hdc, const POINT* pts, const DWORD* counts,
678 if(!PATH_GetPathFromHDC(hdc, &pPath))
681 /* Check that path is open */
682 if(pPath->state!=PATH_Open)
685 for(i = 0, poly = 0; poly < polylines; poly++) {
686 for(point = 0; point < counts[poly]; point++, i++) {
688 if(!W32kLPtoDP(hdc, &pt, 1))
690 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
696 /***********************************************************************
701 /* PATH_AddFlatBezier
704 static BOOL PATH_AddFlatBezier(GdiPath *pPath, POINT *pt, BOOL closed)
709 pts = GDI_Bezier( pt, 4, &no );
710 if(!pts) return FALSE;
712 for(i = 1; i < no; i++)
713 PATH_AddEntry(pPath, &pts[i], (i == no-1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
721 * Replaces Beziers with line segments
724 static BOOL PATH_FlattenPath(GdiPath *pPath)
729 memset(&newPath, 0, sizeof(newPath));
730 newPath.state = PATH_Open;
731 for(srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++) {
732 switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE) {
735 PATH_AddEntry(&newPath, &pPath->pPoints[srcpt], pPath->pFlags[srcpt]);
738 PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt-1], pPath->pFlags[srcpt+2] & PT_CLOSEFIGURE);
743 newPath.state = PATH_Closed;
744 PATH_AssignGdiPath(pPath, &newPath);
745 PATH_EmptyPath(&newPath);
751 * Creates a region from the specified path using the specified polygon
752 * filling mode. The path is left unchanged. A handle to the region that
753 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
754 * error occurs, SetLastError is called with the appropriate value and
757 static BOOL PATH_PathToRegion(const GdiPath *pPath, INT nPolyFillMode,
760 int numStrokes, iStroke, i;
761 INT *pNumPointsInStroke;
767 PATH_FlattenPath(pPath);
769 /* FIXME: What happens when number of points is zero? */
771 /* First pass: Find out how many strokes there are in the path */
772 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
774 for(i=0; i<pPath->numEntriesUsed; i++)
775 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
778 /* Allocate memory for number-of-points-in-stroke array */
779 pNumPointsInStroke=(int *)ExAllocatePool(NonPagedPool, sizeof(int) * numStrokes);
780 if(!pNumPointsInStroke)
782 // SetLastError(ERROR_NOT_ENOUGH_MEMORY);
786 /* Second pass: remember number of points in each polygon */
787 iStroke=-1; /* Will get incremented to 0 at beginning of first stroke */
788 for(i=0; i<pPath->numEntriesUsed; i++)
790 /* Is this the beginning of a new stroke? */
791 if((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
794 pNumPointsInStroke[iStroke]=0;
797 pNumPointsInStroke[iStroke]++;
800 /* Create a region from the strokes */
801 /* hrgn=CreatePolyPolygonRgn(pPath->pPoints, pNumPointsInStroke,
802 numStrokes, nPolyFillMode); FIXME: reinclude when region code implemented */
805 // SetLastError(ERROR_NOT_ENOUGH_MEMORY);
809 /* Free memory for number-of-points-in-stroke array */
810 ExFreePool(pNumPointsInStroke);
819 * Removes all entries from the path and sets the path state to PATH_Null.
821 static void PATH_EmptyPath(GdiPath *pPath)
825 pPath->state=PATH_Null;
826 pPath->numEntriesUsed=0;
831 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
832 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
833 * successful, FALSE otherwise (e.g. if not enough memory was available).
835 BOOL PATH_AddEntry(GdiPath *pPath, const POINT *pPoint, BYTE flags)
839 /* FIXME: If newStroke is true, perhaps we want to check that we're
840 * getting a PT_MOVETO
843 /* Check that path is open */
844 if(pPath->state!=PATH_Open)
847 /* Reserve enough memory for an extra path entry */
848 if(!PATH_ReserveEntries(pPath, pPath->numEntriesUsed+1))
851 /* Store information in path entry */
852 pPath->pPoints[pPath->numEntriesUsed]=*pPoint;
853 pPath->pFlags[pPath->numEntriesUsed]=flags;
855 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
856 if((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
857 pPath->newStroke=TRUE;
859 /* Increment entry count */
860 pPath->numEntriesUsed++;
865 /* PATH_ReserveEntries
867 * Ensures that at least "numEntries" entries (for points and flags) have
868 * been allocated; allocates larger arrays and copies the existing entries
869 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
871 static BOOL PATH_ReserveEntries(GdiPath *pPath, INT numEntries)
873 INT numEntriesToAllocate;
878 assert(numEntries>=0);
880 /* Do we have to allocate more memory? */
881 if(numEntries > pPath->numEntriesAllocated)
883 /* Find number of entries to allocate. We let the size of the array
884 * grow exponentially, since that will guarantee linear time
886 if(pPath->numEntriesAllocated)
888 numEntriesToAllocate=pPath->numEntriesAllocated;
889 while(numEntriesToAllocate<numEntries)
890 numEntriesToAllocate=numEntriesToAllocate*GROW_FACTOR_NUMER/GROW_FACTOR_DENOM;
892 numEntriesToAllocate=numEntries;
894 /* Allocate new arrays */
895 pPointsNew=(POINT *)ExAllocatePool(NonPagedPool, numEntriesToAllocate * sizeof(POINT));
898 pFlagsNew=(BYTE *)ExAllocatePool(NonPagedPool, numEntriesToAllocate * sizeof(BYTE));
901 ExFreePool(pPointsNew);
905 /* Copy old arrays to new arrays and discard old arrays */
908 assert(pPath->pFlags);
910 memcpy(pPointsNew, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
911 memcpy(pFlagsNew, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
913 ExFreePool(pPath->pPoints);
914 ExFreePool(pPath->pFlags);
916 pPath->pPoints=pPointsNew;
917 pPath->pFlags=pFlagsNew;
918 pPath->numEntriesAllocated=numEntriesToAllocate;
924 /* PATH_GetPathFromHDC
926 * Retrieves a pointer to the GdiPath structure contained in an HDC and
927 * places it in *ppPath. TRUE is returned if successful, FALSE otherwise.
929 static BOOL PATH_GetPathFromHDC(HDC hdc, GdiPath **ppPath)
933 pDC=DC_HandleToPtr(hdc);
936 *ppPath=&pDC->w.path;
937 DC_ReleasePtr( hdc );
945 * Creates a Bezier spline that corresponds to part of an arc and appends the
946 * corresponding points to the path. The start and end angles are passed in
947 * "angleStart" and "angleEnd"; these angles should span a quarter circle
948 * at most. If "addMoveTo" is true, a PT_MOVETO entry for the first control
949 * point is added to the path; otherwise, it is assumed that the current
950 * position is equal to the first control point.
952 static BOOL PATH_DoArcPart(GdiPath *pPath, FLOAT_POINT corners[],
953 double angleStart, double angleEnd, BOOL addMoveTo)
956 double xNorm[4], yNorm[4];
960 assert(fabs(angleEnd-angleStart)<=M_PI_2);
962 /* FIXME: Is there an easier way of computing this? */
964 /* Compute control points */
965 halfAngle=(angleEnd-angleStart)/2.0;
966 if(fabs(halfAngle)>1e-8)
968 a=4.0/3.0*(1-cos(halfAngle))/sin(halfAngle);
969 xNorm[0]=cos(angleStart);
970 yNorm[0]=sin(angleStart);
971 xNorm[1]=xNorm[0] - a*yNorm[0];
972 yNorm[1]=yNorm[0] + a*xNorm[0];
973 xNorm[3]=cos(angleEnd);
974 yNorm[3]=sin(angleEnd);
975 xNorm[2]=xNorm[3] + a*yNorm[3];
976 yNorm[2]=yNorm[3] - a*xNorm[3];
980 xNorm[i]=cos(angleStart);
981 yNorm[i]=sin(angleStart);
984 /* Add starting point to path if desired */
987 PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
988 if(!PATH_AddEntry(pPath, &point, PT_MOVETO))
992 /* Add remaining control points */
995 PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
996 if(!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1003 /* PATH_ScaleNormalizedPoint
1005 * Scales a normalized point (x, y) with respect to the box whose corners are
1006 * passed in "corners". The point is stored in "*pPoint". The normalized
1007 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1008 * (1.0, 1.0) correspond to corners[1].
1010 static void PATH_ScaleNormalizedPoint(FLOAT_POINT corners[], double x,
1011 double y, POINT *pPoint)
1013 pPoint->x=GDI_ROUND( (double)corners[0].x + (double)(corners[1].x-corners[0].x)*0.5*(x+1.0) );
1014 pPoint->y=GDI_ROUND( (double)corners[0].y + (double)(corners[1].y-corners[0].y)*0.5*(y+1.0) );
1017 /* PATH_NormalizePoint
1019 * Normalizes a point with respect to the box whose corners are passed in
1020 * corners. The normalized coordinates are stored in *pX and *pY.
1022 static void PATH_NormalizePoint(FLOAT_POINT corners[],
1023 const FLOAT_POINT *pPoint,
1024 double *pX, double *pY)
1026 *pX=(double)(pPoint->x-corners[0].x)/(double)(corners[1].x-corners[0].x) * 2.0 - 1.0;
1027 *pY=(double)(pPoint->y-corners[0].y)/(double)(corners[1].y-corners[0].y) * 2.0 - 1.0;