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.

301 lines
8.0 KiB

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