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