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.
 
 
 
 
 
 

376 lines
9.0 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 c;
  29. void (*draw) (cucul_t *, struct line*);
  30. };
  31. #endif
  32. static void clip_line(cucul_t*, struct line*);
  33. static uint8_t clip_bits(cucul_t*, int, int);
  34. static void draw_solid_line(cucul_t*, struct line*);
  35. static void draw_thin_line(cucul_t*, struct line*);
  36. /** \brief Draw a line on the canvas using the given character.
  37. *
  38. * \param qq 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_t *qq, 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.c = _cucul_utf8_to_utf32(str);
  55. s.draw = draw_solid_line;
  56. clip_line(qq, &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 qq 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_t *qq, int const x[], int const y[], int n,
  73. char const *str)
  74. {
  75. int i;
  76. struct line s;
  77. s.c = _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(qq, &s);
  86. }
  87. }
  88. /** \brief Draw a thin line on the canvas, using ASCII art.
  89. *
  90. * \param qq 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_t *qq, 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(qq, &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 qq 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_t *qq, int const x[], int const y[], int n)
  121. {
  122. int i;
  123. struct line s;
  124. s.draw = draw_thin_line;
  125. for(i = 0; i < n; i++)
  126. {
  127. s.x1 = x[i];
  128. s.y1 = y[i];
  129. s.x2 = x[i+1];
  130. s.y2 = y[i+1];
  131. clip_line(qq, &s);
  132. }
  133. }
  134. /*
  135. * XXX: The following functions are local.
  136. */
  137. /* Generic Cohen-Sutherland line clipping function. */
  138. static void clip_line(cucul_t *qq, struct line* s)
  139. {
  140. uint8_t bits1, bits2;
  141. bits1 = clip_bits(qq, s->x1, s->y1);
  142. bits2 = clip_bits(qq, s->x2, s->y2);
  143. if(bits1 & bits2)
  144. return;
  145. if(bits1 == 0)
  146. {
  147. if(bits2 == 0)
  148. s->draw(qq, s);
  149. else
  150. {
  151. int tmp;
  152. tmp = s->x1; s->x1 = s->x2; s->x2 = tmp;
  153. tmp = s->y1; s->y1 = s->y2; s->y2 = tmp;
  154. clip_line(qq, s);
  155. }
  156. return;
  157. }
  158. if(bits1 & (1<<0))
  159. {
  160. s->y1 = s->y2 - (s->x2 - 0) * (s->y2 - s->y1) / (s->x2 - s->x1);
  161. s->x1 = 0;
  162. }
  163. else if(bits1 & (1<<1))
  164. {
  165. int xmax = qq->width - 1;
  166. s->y1 = s->y2 - (s->x2 - xmax) * (s->y2 - s->y1) / (s->x2 - s->x1);
  167. s->x1 = xmax;
  168. }
  169. else if(bits1 & (1<<2))
  170. {
  171. s->x1 = s->x2 - (s->y2 - 0) * (s->x2 - s->x1) / (s->y2 - s->y1);
  172. s->y1 = 0;
  173. }
  174. else if(bits1 & (1<<3))
  175. {
  176. int ymax = qq->height - 1;
  177. s->x1 = s->x2 - (s->y2 - ymax) * (s->x2 - s->x1) / (s->y2 - s->y1);
  178. s->y1 = ymax;
  179. }
  180. clip_line(qq, s);
  181. }
  182. /* Helper function for clip_line(). */
  183. static uint8_t clip_bits(cucul_t *qq, int x, int y)
  184. {
  185. uint8_t b = 0;
  186. if(x < 0)
  187. b |= (1<<0);
  188. else if(x >= (int)qq->width)
  189. b |= (1<<1);
  190. if(y < 0)
  191. b |= (1<<2);
  192. else if(y >= (int)qq->height)
  193. b |= (1<<3);
  194. return b;
  195. }
  196. /* Solid line drawing function, using Bresenham's mid-point line
  197. * scan-conversion algorithm. */
  198. static void draw_solid_line(cucul_t *qq, struct line* s)
  199. {
  200. int x1, y1, x2, y2;
  201. int dx, dy;
  202. int xinc, yinc;
  203. x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2;
  204. dx = abs(x2 - x1);
  205. dy = abs(y2 - y1);
  206. xinc = (x1 > x2) ? -1 : 1;
  207. yinc = (y1 > y2) ? -1 : 1;
  208. if(dx >= dy)
  209. {
  210. int dpr = dy << 1;
  211. int dpru = dpr - (dx << 1);
  212. int delta = dpr - dx;
  213. for(; dx>=0; dx--)
  214. {
  215. _cucul_putchar32(qq, x1, y1, s->c);
  216. if(delta > 0)
  217. {
  218. x1 += xinc;
  219. y1 += yinc;
  220. delta += dpru;
  221. }
  222. else
  223. {
  224. x1 += xinc;
  225. delta += dpr;
  226. }
  227. }
  228. }
  229. else
  230. {
  231. int dpr = dx << 1;
  232. int dpru = dpr - (dy << 1);
  233. int delta = dpr - dy;
  234. for(; dy >= 0; dy--)
  235. {
  236. _cucul_putchar32(qq, x1, y1, s->c);
  237. if(delta > 0)
  238. {
  239. x1 += xinc;
  240. y1 += yinc;
  241. delta += dpru;
  242. }
  243. else
  244. {
  245. y1 += yinc;
  246. delta += dpr;
  247. }
  248. }
  249. }
  250. }
  251. /* Thin line drawing function, using Bresenham's mid-point line
  252. * scan-conversion algorithm and ASCII art graphics. */
  253. static void draw_thin_line(cucul_t *qq, struct line* s)
  254. {
  255. uint32_t charmapx[2], charmapy[2];
  256. int x1, y1, x2, y2;
  257. int dx, dy;
  258. int yinc;
  259. if(s->x2 >= s->x1)
  260. {
  261. charmapx[0] = (s->y1 > s->y2) ? (uint32_t)',' : (uint32_t)'`';
  262. charmapx[1] = (s->y1 > s->y2) ? (uint32_t)'\'' : (uint32_t)'.';
  263. x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2;
  264. }
  265. else
  266. {
  267. charmapx[0] = (s->y1 > s->y2) ? (uint32_t)'`' : (uint32_t)'.';
  268. charmapx[1] = (s->y1 > s->y2) ? (uint32_t)',' : (uint32_t)'\'';
  269. x2 = s->x1; y2 = s->y1; x1 = s->x2; y1 = s->y2;
  270. }
  271. dx = abs(x2 - x1);
  272. dy = abs(y2 - y1);
  273. if(y1 > y2)
  274. {
  275. charmapy[0] = (uint32_t)',';
  276. charmapy[1] = (uint32_t)'\'';
  277. yinc = -1;
  278. }
  279. else
  280. {
  281. yinc = 1;
  282. charmapy[0] = (uint32_t)'`';
  283. charmapy[1] = (uint32_t)'.';
  284. }
  285. if(dx >= dy)
  286. {
  287. int dpr = dy << 1;
  288. int dpru = dpr - (dx << 1);
  289. int delta = dpr - dx;
  290. int prev = 0;
  291. for(; dx>=0; dx--)
  292. {
  293. if(delta > 0)
  294. {
  295. _cucul_putchar32(qq, x1, y1, charmapy[1]);
  296. x1++;
  297. y1 += yinc;
  298. delta += dpru;
  299. prev = 1;
  300. }
  301. else
  302. {
  303. if(prev)
  304. _cucul_putchar32(qq, x1, y1, charmapy[0]);
  305. else
  306. _cucul_putchar32(qq, x1, y1, (uint32_t)'-');
  307. x1++;
  308. delta += dpr;
  309. prev = 0;
  310. }
  311. }
  312. }
  313. else
  314. {
  315. int dpr = dx << 1;
  316. int dpru = dpr - (dy << 1);
  317. int delta = dpr - dy;
  318. for(; dy >= 0; dy--)
  319. {
  320. if(delta > 0)
  321. {
  322. _cucul_putchar32(qq, x1, y1, charmapx[0]);
  323. _cucul_putchar32(qq, x1 + 1, y1, charmapx[1]);
  324. x1++;
  325. y1 += yinc;
  326. delta += dpru;
  327. }
  328. else
  329. {
  330. _cucul_putchar32(qq, x1, y1, (uint32_t)'|');
  331. y1 += yinc;
  332. delta += dpr;
  333. }
  334. }
  335. }
  336. }