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.
21 #include <ddk/ntddk.h>
24 /******************************************************************
26 * *Very* simple bezier drawing code,
28 * It uses a recursive algorithm to divide the curve in a series
29 * of straight line segements. Not ideal but for me sufficient.
30 * If you are in need for something better look for some incremental
33 * 7 July 1998 Rein Klazes
37 * some macro definitions for bezier drawing
39 * to avoid trucation errors the coordinates are
40 * shifted upwards. When used in drawing they are
41 * shifted down again, including correct rounding
42 * and avoiding floating point arithmatic
43 * 4 bits should allow 27 bits coordinates which I saw
44 * somewere in the win32 doc's
48 #define BEZIERSHIFTBITS 4
49 #define BEZIERSHIFTUP(x) ((x)<<BEZIERSHIFTBITS)
50 #define BEZIERPIXEL BEZIERSHIFTUP(1)
51 #define BEZIERSHIFTDOWN(x) (((x)+(1<<(BEZIERSHIFTBITS-1)))>>BEZIERSHIFTBITS)
52 /* maximum depth of recursion */
53 #define BEZIERMAXDEPTH 8
55 /* size of array to store points on */
56 /* enough for one curve */
57 #define BEZIER_INITBUFSIZE (150)
59 /* calculate Bezier average, in this case the middle
60 * correctly rounded...
63 #define BEZIERMIDDLE(Mid, P1, P2) \
64 (Mid).x=((P1).x+(P2).x + 1)/2;\
65 (Mid).y=((P1).y+(P2).y + 1)/2;
67 static int abs ( int __x )
75 /**********************************************************
76 * BezierCheck helper function to check
77 * that recursion can be terminated
78 * Points[0] and Points[3] are begin and endpoint
79 * Points[1] and Points[2] are control points
80 * level is the recursion depth
81 * returns true if the recusion can be terminated
83 static BOOL FASTCALL BezierCheck( int level, POINT *Points)
87 dx=Points[3].x-Points[0].x;
88 dy=Points[3].y-Points[0].y;
89 if ( abs(dy) <= abs(dx) ) /* shallow line */
91 /* check that control points are between begin and end */
92 if ( Points[1].x < Points[0].x )
94 if ( Points[1].x < Points[3].x )
97 else if ( Points[1].x > Points[3].x )
99 if ( Points[2].x < Points[0].x)
101 if ( Points[2].x < Points[3].x )
104 else if ( Points[2].x > Points[3].x )
106 dx = BEZIERSHIFTDOWN(dx);
109 if ( abs(Points[1].y-Points[0].y-(dy/dx)*
110 BEZIERSHIFTDOWN(Points[1].x-Points[0].x)) > BEZIERPIXEL ||
111 abs(Points[2].y-Points[0].y-(dy/dx)*
112 BEZIERSHIFTDOWN(Points[2].x-Points[0].x)) > BEZIERPIXEL
120 /* check that control points are between begin and end */
121 if(Points[1].y < Points[0].y)
123 if(Points[1].y < Points[3].y)
126 else if(Points[1].y > Points[3].y)
128 if ( Points[2].y < Points[0].y )
130 if ( Points[2].y < Points[3].y )
133 else if ( Points[2].y > Points[3].y )
135 dy = BEZIERSHIFTDOWN(dy);
138 if ( abs(Points[1].x-Points[0].x-(dx/dy)*
139 BEZIERSHIFTDOWN(Points[1].y-Points[0].y)) > BEZIERPIXEL ||
140 abs(Points[2].x-Points[0].x-(dx/dy)*
141 BEZIERSHIFTDOWN(Points[2].y-Points[0].y)) > BEZIERPIXEL
149 /* Helper for GDI_Bezier.
150 * Just handles one Bezier, so Points should point to four POINTs
152 static void STDCALL GDI_InternalBezier( POINT *Points, POINT **PtsOut, INT *dwOut,
153 INT *nPtsOut, INT level )
155 if(*nPtsOut == *dwOut) {
157 *PtsOut = ExAllocatePool(NonPagedPool, *dwOut * sizeof(POINT));
160 if(!level || BezierCheck(level, Points)) {
162 (*PtsOut)[0].x = BEZIERSHIFTDOWN(Points[0].x);
163 (*PtsOut)[0].y = BEZIERSHIFTDOWN(Points[0].y);
166 (*PtsOut)[*nPtsOut].x = BEZIERSHIFTDOWN(Points[3].x);
167 (*PtsOut)[*nPtsOut].y = BEZIERSHIFTDOWN(Points[3].y);
170 POINT Points2[4]; /* for the second recursive call */
171 Points2[3]=Points[3];
172 BEZIERMIDDLE(Points2[2], Points[2], Points[3]);
173 BEZIERMIDDLE(Points2[0], Points[1], Points[2]);
174 BEZIERMIDDLE(Points2[1],Points2[0],Points2[2]);
176 BEZIERMIDDLE(Points[1], Points[0], Points[1]);
177 BEZIERMIDDLE(Points[2], Points[1], Points2[0]);
178 BEZIERMIDDLE(Points[3], Points[2], Points2[1]);
180 Points2[0]=Points[3];
182 /* do the two halves */
183 GDI_InternalBezier(Points, PtsOut, dwOut, nPtsOut, level-1);
184 GDI_InternalBezier(Points2, PtsOut, dwOut, nPtsOut, level-1);
188 /***********************************************************************
189 * GDI_Bezier [INTERNAL]
190 * Calculate line segments that approximate -what microsoft calls- a bezier
192 * The routine recursively divides the curve in two parts until a straight
197 * Points [I] Ptr to count POINTs which are the end and control points
198 * of the set of Bezier curves to flatten.
199 * count [I] Number of Points. Must be 3n+1.
200 * nPtsOut [O] Will contain no of points that have been produced (i.e. no. of
205 * Ptr to an array of POINTs that contain the lines that approximinate the
206 * Beziers. The array is allocated on the process heap and it is the caller's
207 * responsibility to HeapFree it. [this is not a particularly nice interface
208 * but since we can't know in advance how many points will generate, the
209 * alternative would be to call the function twice, once to determine the size
210 * and a second time to do the work - I decided this was too much of a pain].
212 POINT * FASTCALL GDI_Bezier( const POINT *Points, INT count, INT *nPtsOut )
215 INT Bezier, dwOut = BEZIER_INITBUFSIZE, i;
217 if((count - 1) % 3 != 0) {
221 out = ExAllocatePool(NonPagedPool, dwOut * sizeof(POINT));
222 for(Bezier = 0; Bezier < (count-1)/3; Bezier++) {
224 memcpy(ptBuf, Points + Bezier * 3, sizeof(POINT) * 4);
225 for(i = 0; i < 4; i++) {
226 ptBuf[i].x = BEZIERSHIFTUP(ptBuf[i].x);
227 ptBuf[i].y = BEZIERSHIFTUP(ptBuf[i].y);
229 GDI_InternalBezier( ptBuf, &out, &dwOut, nPtsOut, BEZIERMAXDEPTH );