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.
19 /* $Id: bezier.c,v 1.3 2003/05/18 17:16:17 ea Exp $ */
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 /**********************************************************
68 * BezierCheck helper function to check
69 * that recursion can be terminated
70 * Points[0] and Points[3] are begin and endpoint
71 * Points[1] and Points[2] are control points
72 * level is the recursion depth
73 * returns true if the recusion can be terminated
75 static BOOL FASTCALL
BezierCheck( int level
, POINT
*Points
)
79 dx
=Points
[3].x
-Points
[0].x
;
80 dy
=Points
[3].y
-Points
[0].y
;
81 if(abs(dy
)<=abs(dx
)) {/* shallow line */
82 /* check that control points are between begin and end */
83 if(Points
[1].x
< Points
[0].x
){
84 if(Points
[1].x
< Points
[3].x
) return FALSE
;
86 if(Points
[1].x
> Points
[3].x
) return FALSE
;
87 if(Points
[2].x
< Points
[0].x
) {
88 if(Points
[2].x
< Points
[3].x
) return FALSE
;
90 if(Points
[2].x
> Points
[3].x
) return FALSE
;
91 dx
=BEZIERSHIFTDOWN(dx
);
93 if(abs(Points
[1].y
-Points
[0].y
-(dy
/dx
)*
94 BEZIERSHIFTDOWN(Points
[1].x
-Points
[0].x
)) > BEZIERPIXEL
||
95 abs(Points
[2].y
-Points
[0].y
-(dy
/dx
)*
96 BEZIERSHIFTDOWN(Points
[2].x
-Points
[0].x
)) > BEZIERPIXEL
) return FALSE
;
99 } else{ /* steep line */
100 /* check that control points are between begin and end */
101 if(Points
[1].y
< Points
[0].y
){
102 if(Points
[1].y
< Points
[3].y
) return FALSE
;
104 if(Points
[1].y
> Points
[3].y
) return FALSE
;
105 if(Points
[2].y
< Points
[0].y
){
106 if(Points
[2].y
< Points
[3].y
) return FALSE
;
108 if(Points
[2].y
> Points
[3].y
) return FALSE
;
109 dy
=BEZIERSHIFTDOWN(dy
);
111 if(abs(Points
[1].x
-Points
[0].x
-(dx
/dy
)*
112 BEZIERSHIFTDOWN(Points
[1].y
-Points
[0].y
)) > BEZIERPIXEL
||
113 abs(Points
[2].x
-Points
[0].x
-(dx
/dy
)*
114 BEZIERSHIFTDOWN(Points
[2].y
-Points
[0].y
)) > BEZIERPIXEL
) return FALSE
;
120 /* Helper for GDI_Bezier.
121 * Just handles one Bezier, so Points should point to four POINTs
123 static void STDCALL
GDI_InternalBezier( POINT
*Points
, POINT
**PtsOut
, INT
*dwOut
,
124 INT
*nPtsOut
, INT level
)
126 if(*nPtsOut
== *dwOut
) {
128 *PtsOut
= ExAllocatePool(NonPagedPool
, *dwOut
* sizeof(POINT
));
131 if(!level
|| BezierCheck(level
, Points
)) {
133 (*PtsOut
)[0].x
= BEZIERSHIFTDOWN(Points
[0].x
);
134 (*PtsOut
)[0].y
= BEZIERSHIFTDOWN(Points
[0].y
);
137 (*PtsOut
)[*nPtsOut
].x
= BEZIERSHIFTDOWN(Points
[3].x
);
138 (*PtsOut
)[*nPtsOut
].y
= BEZIERSHIFTDOWN(Points
[3].y
);
141 POINT Points2
[4]; /* for the second recursive call */
142 Points2
[3]=Points
[3];
143 BEZIERMIDDLE(Points2
[2], Points
[2], Points
[3]);
144 BEZIERMIDDLE(Points2
[0], Points
[1], Points
[2]);
145 BEZIERMIDDLE(Points2
[1],Points2
[0],Points2
[2]);
147 BEZIERMIDDLE(Points
[1], Points
[0], Points
[1]);
148 BEZIERMIDDLE(Points
[2], Points
[1], Points2
[0]);
149 BEZIERMIDDLE(Points
[3], Points
[2], Points2
[1]);
151 Points2
[0]=Points
[3];
153 /* do the two halves */
154 GDI_InternalBezier(Points
, PtsOut
, dwOut
, nPtsOut
, level
-1);
155 GDI_InternalBezier(Points2
, PtsOut
, dwOut
, nPtsOut
, level
-1);
159 /***********************************************************************
160 * GDI_Bezier [INTERNAL]
161 * Calculate line segments that approximate -what microsoft calls- a bezier
163 * The routine recursively divides the curve in two parts until a straight
168 * Points [I] Ptr to count POINTs which are the end and control points
169 * of the set of Bezier curves to flatten.
170 * count [I] Number of Points. Must be 3n+1.
171 * nPtsOut [O] Will contain no of points that have been produced (i.e. no. of
176 * Ptr to an array of POINTs that contain the lines that approximinate the
177 * Beziers. The array is allocated on the process heap and it is the caller's
178 * responsibility to HeapFree it. [this is not a particularly nice interface
179 * but since we can't know in advance how many points will generate, the
180 * alternative would be to call the function twice, once to determine the size
181 * and a second time to do the work - I decided this was too much of a pain].
183 POINT
* FASTCALL
GDI_Bezier( const POINT
*Points
, INT count
, INT
*nPtsOut
)
186 INT Bezier
, dwOut
= BEZIER_INITBUFSIZE
, i
;
188 if((count
- 1) % 3 != 0) {
192 out
= ExAllocatePool(NonPagedPool
, dwOut
* sizeof(POINT
));
193 for(Bezier
= 0; Bezier
< (count
-1)/3; Bezier
++) {
195 memcpy(ptBuf
, Points
+ Bezier
* 3, sizeof(POINT
) * 4);
196 for(i
= 0; i
< 4; i
++) {
197 ptBuf
[i
].x
= BEZIERSHIFTUP(ptBuf
[i
].x
);
198 ptBuf
[i
].y
= BEZIERSHIFTUP(ptBuf
[i
].y
);
200 GDI_InternalBezier( ptBuf
, &out
, &dwOut
, nPtsOut
, BEZIERMAXDEPTH
);