You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

294 lines
8.1 KiB

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