1 /* Copyright (C) 2001-2017 Peter Selinger.
2 * This file is part of Potrace. It is free software and it is covered
3 * by the GNU General Public License. See the file COPYING for details. */
4
5 #ifdef HAVE_CONFIG_H
6 #include <config.h>
7 #endif
8
9 #include <math.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include "auxiliary.h"
15 #include "greymap.h"
16 #include "render.h"
17
18 /* ---------------------------------------------------------------------- */
19 /* routines for anti-aliased rendering of curves */
20
21 /* we use the following method. Given a point (x,y) (with real-valued
22 * coordinates) in the plane, let (xi,yi) be the integer part of the
23 * coordinates, i.e., xi=floor(x), yi=floor(y). Define a path from
24 * (x,y) to infinity as follows: path(x,y) =
25 * (x,y)--(xi+1,y)--(xi+1,yi)--(+infty,yi). Now as the point (x,y)
26 * moves smoothly across the plane, the path path(x,y) sweeps
27 * (non-smoothly) across a certain area. We proportionately blacken
28 * the area as the path moves "downward", and we whiten the area as
29 * the path moves "upward". This way, after the point has traversed a
30 * closed curve, the interior of the curve has been darkened
31 * (counterclockwise movement) or lightened (clockwise movement). (The
32 * "grey shift" is actually proportional to the winding number). By
33 * choosing the above path with mostly integer coordinates, we achieve
34 * that only pixels close to (x,y) receive grey values and are subject
35 * to round-off errors. The grey value of pixels far away from (x,y)
36 * is always in "integer" (where 0=black, 1=white). As a special
37 * trick, we keep an accumulator rm->a1, which holds a double value to
38 * be added to the grey value to be added to the current pixel
39 * (xi,yi). Only when changing "current" pixels, we convert this
40 * double value to an integer. This way we avoid round-off errors at
41 * the meeting points of line segments. Another speedup measure is
42 * that we sometimes use the rm->incrow_buf array to postpone
43 * incrementing or decrementing an entire row. If incrow_buf[y]=x+1!=0,
44 * then all the pixels (x,y),(x+1,y),(x+2,y),... are scheduled to be
45 * incremented/decremented (which one is the case will be clear from
46 * context). This keeps the greymap operations reasonably local. */
47
48 /* allocate a new rendering state */
render_new(greymap_t * gm)49 render_t* render_new( greymap_t* gm )
50 {
51 render_t* rm;
52
53 rm = (render_t*) malloc( sizeof( render_t ) );
54
55 if( !rm )
56 {
57 return NULL;
58 }
59
60 memset( rm, 0, sizeof( render_t ) );
61 rm->gm = gm;
62 rm->incrow_buf = (int*) calloc( gm->h, sizeof( int ) );
63
64 if( !rm->incrow_buf )
65 {
66 free( rm );
67 return NULL;
68 }
69
70 return rm;
71 }
72
73
74 /* free a given rendering state. Note: this does not free the
75 * underlying greymap. */
render_free(render_t * rm)76 void render_free( render_t* rm )
77 {
78 free( rm->incrow_buf );
79 free( rm );
80 }
81
82
83 /* close path */
render_close(render_t * rm)84 void render_close( render_t* rm )
85 {
86 if( rm->x0 != rm->x1 || rm->y0 != rm->y1 )
87 {
88 render_lineto( rm, rm->x0, rm->y0 );
89 }
90
91 GM_INC( rm->gm, rm->x0i, rm->y0i, ( rm->a0 + rm->a1 ) * 255 );
92
93 /* assert (rm->x0i != rm->x1i || rm->y0i != rm->y1i); */
94
95 /* the persistent state is now undefined */
96 }
97
98
99 /* move point */
render_moveto(render_t * rm,double x,double y)100 void render_moveto( render_t* rm, double x, double y )
101 {
102 /* close the previous path */
103 render_close( rm );
104
105 rm->x0 = rm->x1 = x;
106 rm->y0 = rm->y1 = y;
107 rm->x0i = (int) floor( rm->x0 );
108 rm->x1i = (int) floor( rm->x1 );
109 rm->y0i = (int) floor( rm->y0 );
110 rm->y1i = (int) floor( rm->y1 );
111 rm->a0 = rm->a1 = 0;
112 }
113
114
115 /* add b to pixels (x,y) and all pixels to the right of it. However,
116 * use rm->incrow_buf as a buffer to economize on multiple calls */
incrow(render_t * rm,int x,int y,int b)117 static void incrow( render_t* rm, int x, int y, int b )
118 {
119 int i, x0;
120
121 if( y < 0 || y >= rm->gm->h )
122 {
123 return;
124 }
125
126 if( x < 0 )
127 {
128 x = 0;
129 }
130 else if( x > rm->gm->w )
131 {
132 x = rm->gm->w;
133 }
134
135 if( rm->incrow_buf[y] == 0 )
136 {
137 rm->incrow_buf[y] = x + 1; /* store x+1 so that we can use 0 for "vacant" */
138 return;
139 }
140
141 x0 = rm->incrow_buf[y] - 1;
142 rm->incrow_buf[y] = 0;
143
144 if( x0 < x )
145 {
146 for( i = x0; i < x; i++ )
147 {
148 GM_INC( rm->gm, i, y, -b );
149 }
150 }
151 else
152 {
153 for( i = x; i < x0; i++ )
154 {
155 GM_INC( rm->gm, i, y, b );
156 }
157 }
158 }
159
160
161 /* render a straight line */
render_lineto(render_t * rm,double x2,double y2)162 void render_lineto( render_t* rm, double x2, double y2 )
163 {
164 int x2i, y2i;
165 double t0 = 2, s0 = 2;
166 int sn, tn;
167 double ss = 2, ts = 2;
168 double r0, r1;
169 int i, j;
170 int rxi, ryi;
171 int s;
172
173 x2i = (int) floor( x2 );
174 y2i = (int) floor( y2 );
175
176 sn = abs( x2i - rm->x1i );
177 tn = abs( y2i - rm->y1i );
178
179 if( sn )
180 {
181 s0 = ( ( x2 > rm->x1 ? rm->x1i + 1 : rm->x1i ) - rm->x1 ) / ( x2 - rm->x1 );
182 ss = fabs( 1.0 / ( x2 - rm->x1 ) );
183 }
184
185 if( tn )
186 {
187 t0 = ( ( y2 > rm->y1 ? rm->y1i + 1 : rm->y1i ) - rm->y1 ) / ( y2 - rm->y1 );
188 ts = fabs( 1.0 / ( y2 - rm->y1 ) );
189 }
190
191 r0 = 0;
192
193 i = 0;
194 j = 0;
195
196 rxi = rm->x1i;
197 ryi = rm->y1i;
198
199 while( i < sn || j < tn )
200 {
201 if( j >= tn || ( i < sn && s0 + i * ss < t0 + j * ts ) )
202 {
203 r1 = s0 + i * ss;
204 i++;
205 s = 1;
206 }
207 else
208 {
209 r1 = t0 + j * ts;
210 j++;
211 s = 0;
212 }
213
214 /* render line from r0 to r1 segment of (rm->x1,rm->y1)..(x2,y2) */
215
216 /* move point to r1 */
217 rm->a1 += ( r1 - r0 ) * ( y2 - rm->y1 )
218 * ( rxi + 1 - ( ( r0 + r1 ) / 2.0 * ( x2 - rm->x1 ) + rm->x1 ) );
219
220 /* move point across pixel boundary */
221 if( s && x2 > rm->x1 )
222 {
223 GM_INC( rm->gm, rxi, ryi, rm->a1 * 255 );
224 rm->a1 = 0;
225 rxi++;
226 rm->a1 += rm->y1 + r1 * ( y2 - rm->y1 ) - ryi;
227 }
228 else if( !s && y2 > rm->y1 )
229 {
230 GM_INC( rm->gm, rxi, ryi, rm->a1 * 255 );
231 rm->a1 = 0;
232 incrow( rm, rxi + 1, ryi, 255 );
233 ryi++;
234 }
235 else if( s && x2 <= rm->x1 )
236 {
237 rm->a1 -= rm->y1 + r1 * ( y2 - rm->y1 ) - ryi;
238 GM_INC( rm->gm, rxi, ryi, rm->a1 * 255 );
239 rm->a1 = 0;
240 rxi--;
241 }
242 else if( !s && y2 <= rm->y1 )
243 {
244 GM_INC( rm->gm, rxi, ryi, rm->a1 * 255 );
245 rm->a1 = 0;
246 ryi--;
247 incrow( rm, rxi + 1, ryi, -255 );
248 }
249
250 r0 = r1;
251 }
252
253 /* move point to (x2,y2) */
254
255 r1 = 1;
256 rm->a1 += ( r1 - r0 ) * ( y2 - rm->y1 )
257 * ( rxi + 1 - ( ( r0 + r1 ) / 2.0 * ( x2 - rm->x1 ) + rm->x1 ) );
258
259 rm->x1i = x2i;
260 rm->y1i = y2i;
261 rm->x1 = x2;
262 rm->y1 = y2;
263
264 /* assert (rxi != rm->x1i || ryi != rm->y1i); */
265 }
266
267
268 /* render a Bezier curve. */
render_curveto(render_t * rm,double x2,double y2,double x3,double y3,double x4,double y4)269 void render_curveto( render_t* rm, double x2, double y2, double x3, double y3, double x4,
270 double y4 )
271 {
272 double x1, y1, dd0, dd1, dd, delta, e2, epsilon, t;
273
274 x1 = rm->x1; /* starting point */
275 y1 = rm->y1;
276
277 /* we approximate the curve by small line segments. The interval
278 * size, epsilon, is determined on the fly so that the distance
279 * between the true curve and its approximation does not exceed the
280 * desired accuracy delta. */
281
282 delta = .1; /* desired accuracy, in pixels */
283
284 /* let dd = maximal value of 2nd derivative over curve - this must
285 * occur at an endpoint. */
286 dd0 = sq( x1 - 2 * x2 + x3 ) + sq( y1 - 2 * y2 + y3 );
287 dd1 = sq( x2 - 2 * x3 + x4 ) + sq( y2 - 2 * y3 + y4 );
288 dd = 6 * sqrt( max( dd0, dd1 ) );
289 e2 = 8 * delta <= dd ? 8 * delta / dd : 1;
290 epsilon = sqrt( e2 ); /* necessary interval size */
291
292 for( t = epsilon; t < 1; t += epsilon )
293 {
294 render_lineto( rm, x1 * cu( 1 - t ) + 3 * x2 * sq( 1 - t ) * t
295 + 3 * x3 * ( 1 - t ) * sq( t ) + x4 * cu( t ),
296 y1 * cu( 1 - t ) + 3 * y2 * sq( 1 - t ) * t + 3 * y3 * ( 1 - t ) * sq( t )
297 + y4 * cu( t ) );
298 }
299
300 render_lineto( rm, x4, y4 );
301 }
302