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.
 
 
 
 
 
 

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