File indexing completed on 2024-12-22 04:04:14
0001 /* Copyright (C) 2001-2019 Peter Selinger. 0002 This file is part of Potrace. It is free software and it is covered 0003 by the GNU General Public License. See the file COPYING for details. */ 0004 0005 #ifdef HAVE_CONFIG_H 0006 #include <config.h> 0007 #endif 0008 0009 #include <stdio.h> 0010 #include <stdlib.h> 0011 #include <math.h> 0012 #include <string.h> 0013 0014 #include "render.h" 0015 #include "greymap.h" 0016 #include "auxiliary.h" 0017 0018 /* ---------------------------------------------------------------------- */ 0019 /* routines for anti-aliased rendering of curves */ 0020 0021 /* we use the following method. Given a point (x,y) (with real-valued 0022 coordinates) in the plane, let (xi,yi) be the integer part of the 0023 coordinates, i.e., xi=floor(x), yi=floor(y). Define a path from 0024 (x,y) to infinity as follows: path(x,y) = 0025 (x,y)--(xi+1,y)--(xi+1,yi)--(+infty,yi). Now as the point (x,y) 0026 moves smoothly across the plane, the path path(x,y) sweeps 0027 (non-smoothly) across a certain area. We proportionately blacken 0028 the area as the path moves "downward", and we whiten the area as 0029 the path moves "upward". This way, after the point has traversed a 0030 closed curve, the interior of the curve has been darkened 0031 (counterclockwise movement) or lightened (clockwise movement). (The 0032 "grey shift" is actually proportional to the winding number). By 0033 choosing the above path with mostly integer coordinates, we achieve 0034 that only pixels close to (x,y) receive grey values and are subject 0035 to round-off errors. The grey value of pixels far away from (x,y) 0036 is always in "integer" (where 0=black, 1=white). As a special 0037 trick, we keep an accumulator rm->a1, which holds a double value to 0038 be added to the grey value to be added to the current pixel 0039 (xi,yi). Only when changing "current" pixels, we convert this 0040 double value to an integer. This way we avoid round-off errors at 0041 the meeting points of line segments. Another speedup measure is 0042 that we sometimes use the rm->incrow_buf array to postpone 0043 incrementing or decrementing an entire row. If incrow_buf[y]=x+1!=0, 0044 then all the pixels (x,y),(x+1,y),(x+2,y),... are scheduled to be 0045 incremented/decremented (which one is the case will be clear from 0046 context). This keeps the greymap operations reasonably local. */ 0047 0048 /* allocate a new rendering state */ 0049 render_t *render_new(greymap_t *gm) { 0050 render_t *rm; 0051 0052 rm = (render_t *) malloc(sizeof(render_t)); 0053 if (!rm) { 0054 return NULL; 0055 } 0056 memset(rm, 0, sizeof(render_t)); 0057 rm->gm = gm; 0058 rm->incrow_buf = (int *) calloc(gm->h, sizeof(int)); 0059 if (!rm->incrow_buf) { 0060 free(rm); 0061 return NULL; 0062 } 0063 return rm; 0064 } 0065 0066 /* free a given rendering state. Note: this does not free the 0067 underlying greymap. */ 0068 void render_free(render_t *rm) { 0069 free(rm->incrow_buf); 0070 free(rm); 0071 } 0072 0073 /* close path */ 0074 void render_close(render_t *rm) { 0075 if (rm->x0 != rm->x1 || rm->y0 != rm->y1) { 0076 render_lineto(rm, rm->x0, rm->y0); 0077 } 0078 GM_INC(rm->gm, rm->x0i, rm->y0i, (rm->a0+rm->a1)*255); 0079 0080 /* assert (rm->x0i != rm->x1i || rm->y0i != rm->y1i); */ 0081 0082 /* the persistent state is now undefined */ 0083 } 0084 0085 /* move point */ 0086 void render_moveto(render_t *rm, double x, double y) { 0087 /* close the previous path */ 0088 render_close(rm); 0089 0090 rm->x0 = rm->x1 = x; 0091 rm->y0 = rm->y1 = y; 0092 rm->x0i = (int)floor(rm->x0); 0093 rm->x1i = (int)floor(rm->x1); 0094 rm->y0i = (int)floor(rm->y0); 0095 rm->y1i = (int)floor(rm->y1); 0096 rm->a0 = rm->a1 = 0; 0097 } 0098 0099 /* add b to pixels (x,y) and all pixels to the right of it. However, 0100 use rm->incrow_buf as a buffer to economize on multiple calls */ 0101 static void incrow(render_t *rm, int x, int y, int b) { 0102 int i, x0; 0103 0104 if (y < 0 || y >= rm->gm->h) { 0105 return; 0106 } 0107 0108 if (x < 0) { 0109 x = 0; 0110 } else if (x > rm->gm->w) { 0111 x = rm->gm->w; 0112 } 0113 if (rm->incrow_buf[y] == 0) { 0114 rm->incrow_buf[y] = x+1; /* store x+1 so that we can use 0 for "vacant" */ 0115 return; 0116 } 0117 x0 = rm->incrow_buf[y]-1; 0118 rm->incrow_buf[y] = 0; 0119 if (x0 < x) { 0120 for (i=x0; i<x; i++) { 0121 GM_INC(rm->gm, i, y, -b); 0122 } 0123 } else { 0124 for (i=x; i<x0; i++) { 0125 GM_INC(rm->gm, i, y, b); 0126 } 0127 } 0128 } 0129 0130 /* render a straight line */ 0131 void render_lineto(render_t *rm, double x2, double y2) { 0132 int x2i, y2i; 0133 double t0=2, s0=2; 0134 int sn, tn; 0135 double ss=2, ts=2; 0136 double r0, r1; 0137 int i, j; 0138 int rxi, ryi; 0139 int s; 0140 0141 x2i = (int)floor(x2); 0142 y2i = (int)floor(y2); 0143 0144 sn = abs(x2i - rm->x1i); 0145 tn = abs(y2i - rm->y1i); 0146 0147 if (sn) { 0148 s0 = ((x2>rm->x1 ? rm->x1i+1 : rm->x1i) - rm->x1)/(x2-rm->x1); 0149 ss = fabs(1.0/(x2-rm->x1)); 0150 } 0151 if (tn) { 0152 t0 = ((y2>rm->y1 ? rm->y1i+1 : rm->y1i) - rm->y1)/(y2-rm->y1); 0153 ts = fabs(1.0/(y2-rm->y1)); 0154 } 0155 0156 r0 = 0; 0157 0158 i = 0; 0159 j = 0; 0160 0161 rxi = rm->x1i; 0162 ryi = rm->y1i; 0163 0164 while (i<sn || j<tn) { 0165 if (j>=tn || (i<sn && s0+i*ss < t0+j*ts)) { 0166 r1 = s0+i*ss; 0167 i++; 0168 s = 1; 0169 } else { 0170 r1 = t0+j*ts; 0171 j++; 0172 s = 0; 0173 } 0174 /* render line from r0 to r1 segment of (rm->x1,rm->y1)..(x2,y2) */ 0175 0176 /* move point to r1 */ 0177 rm->a1 += (r1-r0)*(y2-rm->y1)*(rxi+1-((r0+r1)/2.0*(x2-rm->x1)+rm->x1)); 0178 0179 /* move point across pixel boundary */ 0180 if (s && x2>rm->x1) { 0181 GM_INC(rm->gm, rxi, ryi, rm->a1*255); 0182 rm->a1 = 0; 0183 rxi++; 0184 rm->a1 += rm->y1+r1*(y2-rm->y1)-ryi; 0185 } else if (!s && y2>rm->y1) { 0186 GM_INC(rm->gm, rxi, ryi, rm->a1*255); 0187 rm->a1 = 0; 0188 incrow(rm, rxi+1, ryi, 255); 0189 ryi++; 0190 } else if (s && x2<=rm->x1) { 0191 rm->a1 -= rm->y1+r1*(y2-rm->y1)-ryi; 0192 GM_INC(rm->gm, rxi, ryi, rm->a1*255); 0193 rm->a1 = 0; 0194 rxi--; 0195 } else if (!s && y2<=rm->y1) { 0196 GM_INC(rm->gm, rxi, ryi, rm->a1*255); 0197 rm->a1 = 0; 0198 ryi--; 0199 incrow(rm, rxi+1, ryi, -255); 0200 } 0201 0202 r0 = r1; 0203 } 0204 0205 /* move point to (x2,y2) */ 0206 0207 r1 = 1; 0208 rm->a1 += (r1-r0)*(y2-rm->y1)*(rxi+1-((r0+r1)/2.0*(x2-rm->x1)+rm->x1)); 0209 0210 rm->x1i = x2i; 0211 rm->y1i = y2i; 0212 rm->x1 = x2; 0213 rm->y1 = y2; 0214 0215 /* assert (rxi != rm->x1i || ryi != rm->y1i); */ 0216 } 0217 0218 /* render a Bezier curve. */ 0219 void render_curveto(render_t *rm, double x2, double y2, double x3, double y3, double x4, double y4) { 0220 double x1, y1, dd0, dd1, dd, delta, e2, epsilon, t; 0221 0222 x1 = rm->x1; /* starting point */ 0223 y1 = rm->y1; 0224 0225 /* we approximate the curve by small line segments. The interval 0226 size, epsilon, is determined on the fly so that the distance 0227 between the true curve and its approximation does not exceed the 0228 desired accuracy delta. */ 0229 0230 delta = .1; /* desired accuracy, in pixels */ 0231 0232 /* let dd = maximal value of 2nd derivative over curve - this must 0233 occur at an endpoint. */ 0234 dd0 = sq(x1-2*x2+x3) + sq(y1-2*y2+y3); 0235 dd1 = sq(x2-2*x3+x4) + sq(y2-2*y3+y4); 0236 dd = 6*sqrt(max(dd0, dd1)); 0237 e2 = 8*delta <= dd ? 8*delta/dd : 1; 0238 epsilon = sqrt(e2); /* necessary interval size */ 0239 0240 for (t=epsilon; t<1; t+=epsilon) { 0241 render_lineto(rm, x1*cu(1-t)+3*x2*sq(1-t)*t+3*x3*(1-t)*sq(t)+x4*cu(t), 0242 y1*cu(1-t)+3*y2*sq(1-t)*t+3*y3*(1-t)*sq(t)+y4*cu(t)); 0243 } 0244 render_lineto(rm, x4, y4); 0245 }