/* * ReactOS W32 Subsystem * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Id$ */ #include #include #include /****************************************************************** * * *Very* simple bezier drawing code, * * It uses a recursive algorithm to divide the curve in a series * of straight line segements. Not ideal but for me sufficient. * If you are in need for something better look for some incremental * algorithm. * * 7 July 1998 Rein Klazes */ /* * some macro definitions for bezier drawing * * to avoid trucation errors the coordinates are * shifted upwards. When used in drawing they are * shifted down again, including correct rounding * and avoiding floating point arithmatic * 4 bits should allow 27 bits coordinates which I saw * somewere in the win32 doc's * */ #define BEZIERSHIFTBITS 4 #define BEZIERSHIFTUP(x) ((x)<>BEZIERSHIFTBITS) /* maximum depth of recursion */ #define BEZIERMAXDEPTH 8 /* size of array to store points on */ /* enough for one curve */ #define BEZIER_INITBUFSIZE (150) /* calculate Bezier average, in this case the middle * correctly rounded... * */ #define BEZIERMIDDLE(Mid, P1, P2) \ (Mid).x=((P1).x+(P2).x + 1)/2;\ (Mid).y=((P1).y+(P2).y + 1)/2; static int abs ( int __x ) { if ( __x < 0 ) return -__x; else return __x; } /********************************************************** * BezierCheck helper function to check * that recursion can be terminated * Points[0] and Points[3] are begin and endpoint * Points[1] and Points[2] are control points * level is the recursion depth * returns true if the recusion can be terminated */ static BOOL FASTCALL BezierCheck( int level, POINT *Points) { INT dx, dy; dx=Points[3].x-Points[0].x; dy=Points[3].y-Points[0].y; if ( abs(dy) <= abs(dx) ) /* shallow line */ { /* check that control points are between begin and end */ if ( Points[1].x < Points[0].x ) { if ( Points[1].x < Points[3].x ) return FALSE; } else if ( Points[1].x > Points[3].x ) return FALSE; if ( Points[2].x < Points[0].x) { if ( Points[2].x < Points[3].x ) return FALSE; } else if ( Points[2].x > Points[3].x ) return FALSE; dx = BEZIERSHIFTDOWN(dx); if ( !dx ) return TRUE; if ( abs(Points[1].y-Points[0].y-(dy/dx)* BEZIERSHIFTDOWN(Points[1].x-Points[0].x)) > BEZIERPIXEL || abs(Points[2].y-Points[0].y-(dy/dx)* BEZIERSHIFTDOWN(Points[2].x-Points[0].x)) > BEZIERPIXEL ) return FALSE; else return TRUE; } else { /* steep line */ /* check that control points are between begin and end */ if(Points[1].y < Points[0].y) { if(Points[1].y < Points[3].y) return FALSE; } else if(Points[1].y > Points[3].y) return FALSE; if ( Points[2].y < Points[0].y ) { if ( Points[2].y < Points[3].y ) return FALSE; } else if ( Points[2].y > Points[3].y ) return FALSE; dy = BEZIERSHIFTDOWN(dy); if ( !dy ) return TRUE; if ( abs(Points[1].x-Points[0].x-(dx/dy)* BEZIERSHIFTDOWN(Points[1].y-Points[0].y)) > BEZIERPIXEL || abs(Points[2].x-Points[0].x-(dx/dy)* BEZIERSHIFTDOWN(Points[2].y-Points[0].y)) > BEZIERPIXEL ) return FALSE; else return TRUE; } } /* Helper for GDI_Bezier. * Just handles one Bezier, so Points should point to four POINTs */ static void STDCALL GDI_InternalBezier( POINT *Points, POINT **PtsOut, INT *dwOut, INT *nPtsOut, INT level ) { if(*nPtsOut == *dwOut) { *dwOut *= 2; *PtsOut = ExAllocatePool(NonPagedPool, *dwOut * sizeof(POINT)); } if(!level || BezierCheck(level, Points)) { if(*nPtsOut == 0) { (*PtsOut)[0].x = BEZIERSHIFTDOWN(Points[0].x); (*PtsOut)[0].y = BEZIERSHIFTDOWN(Points[0].y); *nPtsOut = 1; } (*PtsOut)[*nPtsOut].x = BEZIERSHIFTDOWN(Points[3].x); (*PtsOut)[*nPtsOut].y = BEZIERSHIFTDOWN(Points[3].y); (*nPtsOut) ++; } else { POINT Points2[4]; /* for the second recursive call */ Points2[3]=Points[3]; BEZIERMIDDLE(Points2[2], Points[2], Points[3]); BEZIERMIDDLE(Points2[0], Points[1], Points[2]); BEZIERMIDDLE(Points2[1],Points2[0],Points2[2]); BEZIERMIDDLE(Points[1], Points[0], Points[1]); BEZIERMIDDLE(Points[2], Points[1], Points2[0]); BEZIERMIDDLE(Points[3], Points[2], Points2[1]); Points2[0]=Points[3]; /* do the two halves */ GDI_InternalBezier(Points, PtsOut, dwOut, nPtsOut, level-1); GDI_InternalBezier(Points2, PtsOut, dwOut, nPtsOut, level-1); } } /*********************************************************************** * GDI_Bezier [INTERNAL] * Calculate line segments that approximate -what microsoft calls- a bezier * curve. * The routine recursively divides the curve in two parts until a straight * line can be drawn * * PARAMS * * Points [I] Ptr to count POINTs which are the end and control points * of the set of Bezier curves to flatten. * count [I] Number of Points. Must be 3n+1. * nPtsOut [O] Will contain no of points that have been produced (i.e. no. of * lines+1). * * RETURNS * * Ptr to an array of POINTs that contain the lines that approximinate the * Beziers. The array is allocated on the process heap and it is the caller's * responsibility to HeapFree it. [this is not a particularly nice interface * but since we can't know in advance how many points will generate, the * alternative would be to call the function twice, once to determine the size * and a second time to do the work - I decided this was too much of a pain]. */ POINT * FASTCALL GDI_Bezier( const POINT *Points, INT count, INT *nPtsOut ) { POINT *out; INT Bezier, dwOut = BEZIER_INITBUFSIZE, i; if((count - 1) % 3 != 0) { return NULL; } *nPtsOut = 0; out = ExAllocatePool(NonPagedPool, dwOut * sizeof(POINT)); for(Bezier = 0; Bezier < (count-1)/3; Bezier++) { POINT ptBuf[4]; memcpy(ptBuf, Points + Bezier * 3, sizeof(POINT) * 4); for(i = 0; i < 4; i++) { ptBuf[i].x = BEZIERSHIFTUP(ptBuf[i].x); ptBuf[i].y = BEZIERSHIFTUP(ptBuf[i].y); } GDI_InternalBezier( ptBuf, &out, &dwOut, nPtsOut, BEZIERMAXDEPTH ); } return out; } /* EOF */