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.
 
 
 
 
 
 

395 lines
9.3 KiB

  1. /*
  2. * libcucul Canvas for ultrafast compositing of Unicode letters
  3. * Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
  4. * All Rights Reserved
  5. *
  6. * $Id$
  7. *
  8. * This library is free software. It comes without any warranty, to
  9. * the extent permitted by applicable law. You can redistribute it
  10. * and/or modify it under the terms of the Do What The Fuck You Want
  11. * To Public License, Version 2, as published by Sam Hocevar. See
  12. * http://sam.zoy.org/wtfpl/COPYING for more details.
  13. */
  14. /*
  15. * This file contains line and polyline drawing functions, with both thin
  16. * and thick styles.
  17. */
  18. #include "config.h"
  19. #include "common.h"
  20. #if !defined(__KERNEL__)
  21. # include <stdlib.h>
  22. #endif
  23. #include "cucul.h"
  24. #include "cucul_internals.h"
  25. #if !defined(_DOXYGEN_SKIP_ME)
  26. struct line
  27. {
  28. int x1, y1;
  29. int x2, y2;
  30. uint32_t ch;
  31. void (*draw) (cucul_canvas_t *, struct line*);
  32. };
  33. #endif
  34. static void clip_line(cucul_canvas_t*, struct line*);
  35. static uint8_t clip_bits(cucul_canvas_t*, int, int);
  36. static void draw_solid_line(cucul_canvas_t*, struct line*);
  37. static void draw_thin_line(cucul_canvas_t*, struct line*);
  38. /** \brief Draw a line on the canvas using the given character.
  39. *
  40. * This function never fails.
  41. *
  42. * \param cv The handle to the libcucul canvas.
  43. * \param x1 X coordinate of the first point.
  44. * \param y1 Y coordinate of the first point.
  45. * \param x2 X coordinate of the second point.
  46. * \param y2 Y coordinate of the second point.
  47. * \param ch UTF-32 character to be used to draw the line.
  48. * \return This function always returns 0.
  49. */
  50. int cucul_draw_line(cucul_canvas_t *cv, int x1, int y1, int x2, int y2,
  51. unsigned long int ch)
  52. {
  53. struct line s;
  54. s.x1 = x1;
  55. s.y1 = y1;
  56. s.x2 = x2;
  57. s.y2 = y2;
  58. s.ch = ch;
  59. s.draw = draw_solid_line;
  60. clip_line(cv, &s);
  61. return 0;
  62. }
  63. /** \brief Draw a polyline.
  64. *
  65. * Draw a polyline on the canvas using the given character and coordinate
  66. * arrays. The first and last points are not connected, hence in order to
  67. * draw a polygon you need to specify the starting point at the end of the
  68. * list as well.
  69. *
  70. * This function never fails.
  71. *
  72. * \param cv The handle to the libcucul canvas.
  73. * \param x Array of X coordinates. Must have \p n + 1 elements.
  74. * \param y Array of Y coordinates. Must have \p n + 1 elements.
  75. * \param n Number of lines to draw.
  76. * \param ch UTF-32 character to be used to draw the lines.
  77. * \return This function always returns 0.
  78. */
  79. int cucul_draw_polyline(cucul_canvas_t *cv, int const x[], int const y[],
  80. int n, unsigned long int ch)
  81. {
  82. int i;
  83. struct line s;
  84. s.ch = ch;
  85. s.draw = draw_solid_line;
  86. for(i = 0; i < n; i++)
  87. {
  88. s.x1 = x[i];
  89. s.y1 = y[i];
  90. s.x2 = x[i+1];
  91. s.y2 = y[i+1];
  92. clip_line(cv, &s);
  93. }
  94. return 0;
  95. }
  96. /** \brief Draw a thin line on the canvas, using ASCII art.
  97. *
  98. * This function never fails.
  99. *
  100. * \param cv The handle to the libcucul canvas.
  101. * \param x1 X coordinate of the first point.
  102. * \param y1 Y coordinate of the first point.
  103. * \param x2 X coordinate of the second point.
  104. * \param y2 Y coordinate of the second point.
  105. * \return This function always returns 0.
  106. */
  107. int cucul_draw_thin_line(cucul_canvas_t *cv, int x1, int y1, int x2, int y2)
  108. {
  109. struct line s;
  110. s.x1 = x1;
  111. s.y1 = y1;
  112. s.x2 = x2;
  113. s.y2 = y2;
  114. s.draw = draw_thin_line;
  115. clip_line(cv, &s);
  116. return 0;
  117. }
  118. /** \brief Draw an ASCII art thin polyline.
  119. *
  120. * Draw a thin polyline on the canvas using the given coordinate arrays and
  121. * with ASCII art. The first and last points are not connected, so in order
  122. * to draw a polygon you need to specify the starting point at the end of
  123. * the list as well.
  124. *
  125. * This function never fails.
  126. *
  127. * \param cv The handle to the libcucul canvas.
  128. * \param x Array of X coordinates. Must have \p n + 1 elements.
  129. * \param y Array of Y coordinates. Must have \p n + 1 elements.
  130. * \param n Number of lines to draw.
  131. * \return This function always returns 0.
  132. */
  133. int cucul_draw_thin_polyline(cucul_canvas_t *cv, int const x[], int const y[],
  134. int n)
  135. {
  136. int i;
  137. struct line s;
  138. s.draw = draw_thin_line;
  139. for(i = 0; i < n; i++)
  140. {
  141. s.x1 = x[i];
  142. s.y1 = y[i];
  143. s.x2 = x[i+1];
  144. s.y2 = y[i+1];
  145. clip_line(cv, &s);
  146. }
  147. return 0;
  148. }
  149. /*
  150. * XXX: The following functions are local.
  151. */
  152. /* Generic Cohen-Sutherland line clipping function. */
  153. static void clip_line(cucul_canvas_t *cv, struct line* s)
  154. {
  155. uint8_t bits1, bits2;
  156. bits1 = clip_bits(cv, s->x1, s->y1);
  157. bits2 = clip_bits(cv, s->x2, s->y2);
  158. if(bits1 & bits2)
  159. return;
  160. if(bits1 == 0)
  161. {
  162. if(bits2 == 0)
  163. s->draw(cv, s);
  164. else
  165. {
  166. int tmp;
  167. tmp = s->x1; s->x1 = s->x2; s->x2 = tmp;
  168. tmp = s->y1; s->y1 = s->y2; s->y2 = tmp;
  169. clip_line(cv, s);
  170. }
  171. return;
  172. }
  173. if(bits1 & (1<<0))
  174. {
  175. s->y1 = s->y2 - (s->x2 - 0) * (s->y2 - s->y1) / (s->x2 - s->x1);
  176. s->x1 = 0;
  177. }
  178. else if(bits1 & (1<<1))
  179. {
  180. int xmax = cv->width - 1;
  181. s->y1 = s->y2 - (s->x2 - xmax) * (s->y2 - s->y1) / (s->x2 - s->x1);
  182. s->x1 = xmax;
  183. }
  184. else if(bits1 & (1<<2))
  185. {
  186. s->x1 = s->x2 - (s->y2 - 0) * (s->x2 - s->x1) / (s->y2 - s->y1);
  187. s->y1 = 0;
  188. }
  189. else if(bits1 & (1<<3))
  190. {
  191. int ymax = cv->height - 1;
  192. s->x1 = s->x2 - (s->y2 - ymax) * (s->x2 - s->x1) / (s->y2 - s->y1);
  193. s->y1 = ymax;
  194. }
  195. clip_line(cv, s);
  196. }
  197. /* Helper function for clip_line(). */
  198. static uint8_t clip_bits(cucul_canvas_t *cv, int x, int y)
  199. {
  200. uint8_t b = 0;
  201. if(x < 0)
  202. b |= (1<<0);
  203. else if(x >= (int)cv->width)
  204. b |= (1<<1);
  205. if(y < 0)
  206. b |= (1<<2);
  207. else if(y >= (int)cv->height)
  208. b |= (1<<3);
  209. return b;
  210. }
  211. /* Solid line drawing function, using Bresenham's mid-point line
  212. * scan-conversion algorithm. */
  213. static void draw_solid_line(cucul_canvas_t *cv, struct line* s)
  214. {
  215. int x1, y1, x2, y2;
  216. int dx, dy;
  217. int xinc, yinc;
  218. x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2;
  219. dx = abs(x2 - x1);
  220. dy = abs(y2 - y1);
  221. xinc = (x1 > x2) ? -1 : 1;
  222. yinc = (y1 > y2) ? -1 : 1;
  223. if(dx >= dy)
  224. {
  225. int dpr = dy << 1;
  226. int dpru = dpr - (dx << 1);
  227. int delta = dpr - dx;
  228. for(; dx>=0; dx--)
  229. {
  230. cucul_put_char(cv, x1, y1, s->ch);
  231. if(delta > 0)
  232. {
  233. x1 += xinc;
  234. y1 += yinc;
  235. delta += dpru;
  236. }
  237. else
  238. {
  239. x1 += xinc;
  240. delta += dpr;
  241. }
  242. }
  243. }
  244. else
  245. {
  246. int dpr = dx << 1;
  247. int dpru = dpr - (dy << 1);
  248. int delta = dpr - dy;
  249. for(; dy >= 0; dy--)
  250. {
  251. cucul_put_char(cv, x1, y1, s->ch);
  252. if(delta > 0)
  253. {
  254. x1 += xinc;
  255. y1 += yinc;
  256. delta += dpru;
  257. }
  258. else
  259. {
  260. y1 += yinc;
  261. delta += dpr;
  262. }
  263. }
  264. }
  265. }
  266. /* Thin line drawing function, using Bresenham's mid-point line
  267. * scan-conversion algorithm and ASCII art graphics. */
  268. static void draw_thin_line(cucul_canvas_t *cv, struct line* s)
  269. {
  270. uint32_t charmapx[2], charmapy[2];
  271. int x1, y1, x2, y2;
  272. int dx, dy;
  273. int yinc;
  274. if(s->x2 >= s->x1)
  275. {
  276. charmapx[0] = (s->y1 > s->y2) ? ',' : '`';
  277. charmapx[1] = (s->y1 > s->y2) ? '\'' : '.';
  278. x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2;
  279. }
  280. else
  281. {
  282. charmapx[0] = (s->y1 > s->y2) ? '`' : '.';
  283. charmapx[1] = (s->y1 > s->y2) ? ',' : '\'';
  284. x2 = s->x1; y2 = s->y1; x1 = s->x2; y1 = s->y2;
  285. }
  286. dx = abs(x2 - x1);
  287. dy = abs(y2 - y1);
  288. if(y1 > y2)
  289. {
  290. charmapy[0] = ',';
  291. charmapy[1] = '\'';
  292. yinc = -1;
  293. }
  294. else
  295. {
  296. yinc = 1;
  297. charmapy[0] = '`';
  298. charmapy[1] = '.';
  299. }
  300. if(dx >= dy)
  301. {
  302. int dpr = dy << 1;
  303. int dpru = dpr - (dx << 1);
  304. int delta = dpr - dx;
  305. int prev = 0;
  306. for(; dx>=0; dx--)
  307. {
  308. if(delta > 0)
  309. {
  310. cucul_put_char(cv, x1, y1, charmapy[1]);
  311. x1++;
  312. y1 += yinc;
  313. delta += dpru;
  314. prev = 1;
  315. }
  316. else
  317. {
  318. if(prev)
  319. cucul_put_char(cv, x1, y1, charmapy[0]);
  320. else
  321. cucul_put_char(cv, x1, y1, '-');
  322. x1++;
  323. delta += dpr;
  324. prev = 0;
  325. }
  326. }
  327. }
  328. else
  329. {
  330. int dpr = dx << 1;
  331. int dpru = dpr - (dy << 1);
  332. int delta = dpr - dy;
  333. for(; dy >= 0; dy--)
  334. {
  335. if(delta > 0)
  336. {
  337. cucul_put_char(cv, x1, y1, charmapx[0]);
  338. cucul_put_char(cv, x1 + 1, y1, charmapx[1]);
  339. x1++;
  340. y1 += yinc;
  341. delta += dpru;
  342. }
  343. else
  344. {
  345. cucul_put_char(cv, x1, y1, '|');
  346. y1 += yinc;
  347. delta += dpr;
  348. }
  349. }
  350. }
  351. }