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.
 
 
 
 
 
 

501 lines
14 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 various canvas handling functions such as character
  16. * and string drawing.
  17. */
  18. #include "config.h"
  19. #include "common.h"
  20. #if !defined(__KERNEL__)
  21. # include <stdio.h> /* BUFSIZ */
  22. # include <string.h>
  23. # include <stdlib.h>
  24. # include <stdarg.h>
  25. # if defined(HAVE_UNISTD_H)
  26. # include <unistd.h>
  27. # endif
  28. # if defined(HAVE_SIGNAL_H)
  29. # include <signal.h>
  30. # endif
  31. # if defined(HAVE_SYS_IOCTL_H)
  32. # include <sys/ioctl.h>
  33. # endif
  34. #endif
  35. #include "cucul.h"
  36. #include "cucul_internals.h"
  37. /** \brief Set cursor position.
  38. *
  39. * Put the cursor at the given coordinates. Functions making use of the
  40. * cursor will use the new values. Setting the cursor position outside the
  41. * canvas is legal but the cursor will not be shown.
  42. *
  43. * This function never fails.
  44. *
  45. * \param cv A handle to the libcucul canvas.
  46. * \param x X cursor coordinate.
  47. * \param y Y cursor coordinate.
  48. * \return This function always returns 0.
  49. */
  50. int cucul_gotoxy(cucul_canvas_t *cv, int x, int y)
  51. {
  52. cv->frames[cv->frame].x = x;
  53. cv->frames[cv->frame].y = y;
  54. return 0;
  55. }
  56. /** \brief Get X cursor position.
  57. *
  58. * Retrieve the X coordinate of the cursor's position.
  59. *
  60. * This function never fails.
  61. *
  62. * \param cv A handle to the libcucul canvas.
  63. * \return The cursor's X coordinate.
  64. */
  65. int cucul_get_cursor_x(cucul_canvas_t const *cv)
  66. {
  67. return cv->frames[cv->frame].x;
  68. }
  69. /** \brief Get Y cursor position.
  70. *
  71. * Retrieve the Y coordinate of the cursor's position.
  72. *
  73. * This function never fails.
  74. *
  75. * \param cv A handle to the libcucul canvas.
  76. * \return The cursor's Y coordinate.
  77. */
  78. int cucul_get_cursor_y(cucul_canvas_t const *cv)
  79. {
  80. return cv->frames[cv->frame].y;
  81. }
  82. /** \brief Print an ASCII or Unicode character.
  83. *
  84. * Print an ASCII or Unicode character at the given coordinates, using
  85. * the default foreground and background colour values.
  86. *
  87. * If the coordinates are outside the canvas boundaries, nothing is printed.
  88. * If a fullwidth Unicode character gets overwritten, its remaining visible
  89. * parts are replaced with spaces. If the canvas' boundaries would split the
  90. * fullwidth character in two, a space is printed instead.
  91. *
  92. * The behaviour when printing non-printable characters or invalid UTF-32
  93. * characters is undefined. To print a sequence of bytes forming an UTF-8
  94. * character instead of an UTF-32 character, use the cucul_put_str() function.
  95. *
  96. * This function never fails.
  97. *
  98. * \param cv A handle to the libcucul canvas.
  99. * \param x X coordinate.
  100. * \param y Y coordinate.
  101. * \param ch The character to print.
  102. * \return This function always returns 0.
  103. */
  104. int cucul_put_char(cucul_canvas_t *cv, int x, int y, unsigned long int ch)
  105. {
  106. uint32_t *curchar, *curattr, attr;
  107. int fullwidth;
  108. if(x >= (int)cv->width || y < 0 || y >= (int)cv->height)
  109. return 0;
  110. if(ch == CUCUL_MAGIC_FULLWIDTH)
  111. return 0;
  112. fullwidth = cucul_utf32_is_fullwidth(ch);
  113. if(x == -1 && fullwidth)
  114. {
  115. x = 0;
  116. ch = ' ';
  117. fullwidth = 0;
  118. }
  119. else if(x < 0)
  120. return 0;
  121. curchar = cv->chars + x + y * cv->width;
  122. curattr = cv->attrs + x + y * cv->width;
  123. attr = cv->curattr;
  124. /* When overwriting the right part of a fullwidth character,
  125. * replace its left part with a space. */
  126. if(x && curchar[0] == CUCUL_MAGIC_FULLWIDTH)
  127. curchar[-1] = ' ';
  128. if(fullwidth)
  129. {
  130. if(x + 1 == (int)cv->width)
  131. ch = ' ';
  132. else
  133. {
  134. /* When overwriting the left part of a fullwidth character,
  135. * replace its right part with a space. */
  136. if(x + 2 < (int)cv->width && curchar[2] == CUCUL_MAGIC_FULLWIDTH)
  137. curchar[2] = ' ';
  138. curchar[1] = CUCUL_MAGIC_FULLWIDTH;
  139. curattr[1] = attr;
  140. }
  141. }
  142. else
  143. {
  144. /* When overwriting the left part of a fullwidth character,
  145. * replace its right part with a space. */
  146. if(x + 1 != (int)cv->width && curchar[1] == CUCUL_MAGIC_FULLWIDTH)
  147. curchar[1] = ' ';
  148. }
  149. curchar[0] = ch;
  150. curattr[0] = attr;
  151. return 0;
  152. }
  153. /** \brief Get the Unicode character at the given coordinates.
  154. *
  155. * Get the ASCII or Unicode value of the character at the given
  156. * coordinates. If the value is less or equal to 127 (0x7f),
  157. * the character can be printed as ASCII. Otherise, it must be handled
  158. * as a UTF-32 value.
  159. *
  160. * If the coordinates are outside the canvas boundaries, a space (0x20)
  161. * is returned.
  162. *
  163. * A special exception is when CUCUL_MAGIC_FULLWIDTH is returned. This
  164. * value is guaranteed not to be a valid Unicode character, and indicates
  165. * that the character at the left of the requested one is a fullwidth
  166. * character.
  167. *
  168. * This function never fails.
  169. *
  170. * \param cv A handle to the libcucul canvas.
  171. * \param x X coordinate.
  172. * \param y Y coordinate.
  173. * \return This function always returns 0.
  174. */
  175. unsigned long int cucul_get_char(cucul_canvas_t const *cv, int x, int y)
  176. {
  177. if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
  178. return ' ';
  179. return (unsigned long int)cv->chars[x + y * cv->width];
  180. }
  181. /** \brief Print a string.
  182. *
  183. * Print an UTF-8 string at the given coordinates, using the default
  184. * foreground and background values. The coordinates may be outside the
  185. * canvas boundaries (eg. a negative Y coordinate) and the string will
  186. * be cropped accordingly if it is too long.
  187. *
  188. * See cucul_put_char() for more information on how fullwidth characters
  189. * are handled when overwriting each other or at the canvas' boundaries.
  190. *
  191. * This function never fails.
  192. *
  193. * \param cv A handle to the libcucul canvas.
  194. * \param x X coordinate.
  195. * \param y Y coordinate.
  196. * \param s The string to print.
  197. * \return This function always returns 0.
  198. */
  199. int cucul_put_str(cucul_canvas_t *cv, int x, int y, char const *s)
  200. {
  201. unsigned int rd;
  202. if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
  203. return 0;
  204. while(*s && x < -1)
  205. {
  206. x += cucul_utf32_is_fullwidth(cucul_utf8_to_utf32(s, &rd)) ? 2 : 1;
  207. s += rd;
  208. }
  209. while(*s && x < (int)cv->width)
  210. {
  211. uint32_t ch = cucul_utf8_to_utf32(s, &rd);
  212. cucul_put_char(cv, x, y, ch);
  213. x += cucul_utf32_is_fullwidth(ch) ? 2 : 1;
  214. s += rd;
  215. }
  216. return 0;
  217. }
  218. /** \brief Print a formated string.
  219. *
  220. * Format a string at the given coordinates, using the default foreground
  221. * and background values. The coordinates may be outside the canvas
  222. * boundaries (eg. a negative Y coordinate) and the string will be cropped
  223. * accordingly if it is too long. The syntax of the format string is the
  224. * same as for the C printf() function.
  225. *
  226. * This function never fails.
  227. *
  228. * \param cv A handle to the libcucul canvas.
  229. * \param x X coordinate.
  230. * \param y Y coordinate.
  231. * \param format The format string to print.
  232. * \param ... Arguments to the format string.
  233. * \return This function always returns 0.
  234. */
  235. int cucul_printf(cucul_canvas_t *cv, int x, int y, char const *format, ...)
  236. {
  237. char tmp[BUFSIZ];
  238. char *buf = tmp;
  239. va_list args;
  240. if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
  241. return 0;
  242. if(cv->width - x + 1 > BUFSIZ)
  243. buf = malloc(cv->width - x + 1);
  244. va_start(args, format);
  245. #if defined(HAVE_VSNPRINTF)
  246. vsnprintf(buf, cv->width - x + 1, format, args);
  247. #else
  248. vsprintf(buf, format, args);
  249. #endif
  250. buf[cv->width - x] = '\0';
  251. va_end(args);
  252. cucul_put_str(cv, x, y, buf);
  253. if(buf != tmp)
  254. free(buf);
  255. return 0;
  256. }
  257. /** \brief Clear the canvas.
  258. *
  259. * Clear the canvas using the current foreground and background colours.
  260. *
  261. * This function never fails.
  262. *
  263. * \param cv The canvas to clear.
  264. * \return This function always returns 0.
  265. */
  266. int cucul_clear_canvas(cucul_canvas_t *cv)
  267. {
  268. uint32_t attr = cv->curattr;
  269. unsigned int n;
  270. for(n = cv->width * cv->height; n--; )
  271. {
  272. cv->chars[n] = (uint32_t)' ';
  273. cv->attrs[n] = attr;
  274. }
  275. return 0;
  276. }
  277. /** \brief Set cursor handle.
  278. *
  279. * Set the canvas' handle. Blitting functions will use the handle value
  280. * to put the canvas at the proper coordinates.
  281. *
  282. * This function never fails.
  283. *
  284. * \param cv A handle to the libcucul canvas.
  285. * \param x X handle coordinate.
  286. * \param y Y handle coordinate.
  287. * \return This function always returns 0.
  288. */
  289. int cucul_set_canvas_handle(cucul_canvas_t *cv, int x, int y)
  290. {
  291. cv->frames[cv->frame].handlex = x;
  292. cv->frames[cv->frame].handley = y;
  293. return 0;
  294. }
  295. /** \brief Get X handle position.
  296. *
  297. * Retrieve the X coordinate of the canvas' handle.
  298. *
  299. * This function never fails.
  300. *
  301. * \param cv A handle to the libcucul canvas.
  302. * \return The canvas' handle's X coordinate.
  303. */
  304. int cucul_get_canvas_handle_x(cucul_canvas_t const *cv)
  305. {
  306. return cv->frames[cv->frame].handlex;
  307. }
  308. /** \brief Get Y handle position.
  309. *
  310. * Retrieve the Y coordinate of the canvas' handle.
  311. *
  312. * This function never fails.
  313. *
  314. * \param cv A handle to the libcucul canvas.
  315. * \return The canvas' handle's Y coordinate.
  316. */
  317. int cucul_get_canvas_handle_y(cucul_canvas_t const *cv)
  318. {
  319. return cv->frames[cv->frame].handley;
  320. }
  321. /** \brief Blit a canvas onto another one.
  322. *
  323. * Blit a canvas onto another one at the given coordinates.
  324. * An optional mask canvas can be used.
  325. *
  326. * If an error occurs, -1 is returned and \b errno is set accordingly:
  327. * - \c EINVAL A mask was specified but the mask size and source canvas
  328. * size do not match.
  329. *
  330. * \param dst The destination canvas.
  331. * \param x X coordinate.
  332. * \param y Y coordinate.
  333. * \param src The source canvas.
  334. * \param mask The mask canvas.
  335. * \return 0 in case of success, -1 if an error occurred.
  336. */
  337. int cucul_blit(cucul_canvas_t *dst, int x, int y,
  338. cucul_canvas_t const *src, cucul_canvas_t const *mask)
  339. {
  340. int i, j, starti, startj, endi, endj;
  341. if(mask && (src->width != mask->width || src->height != mask->height))
  342. {
  343. seterrno(EINVAL);
  344. return -1;
  345. }
  346. x -= src->frames[src->frame].handlex;
  347. y -= src->frames[src->frame].handley;
  348. starti = x < 0 ? -x : 0;
  349. startj = y < 0 ? -y : 0;
  350. endi = (x + src->width >= dst->width) ? dst->width - x : src->width;
  351. endj = (y + src->height >= dst->height) ? dst->height - y : src->height;
  352. if((unsigned int)starti > src->width || (unsigned int)startj > src->height
  353. || starti >= endi || startj >= endj)
  354. return 0;
  355. for(j = startj; j < endj; j++)
  356. {
  357. unsigned int dstix = (j + y) * dst->width + starti + x;
  358. unsigned int srcix = j * src->width + starti;
  359. int stride = endi - starti;
  360. /* FIXME: we are ignoring the mask here */
  361. if((starti + x) && dst->chars[dstix] == CUCUL_MAGIC_FULLWIDTH)
  362. dst->chars[dstix - 1] = ' ';
  363. if((unsigned int)(endi + x) < dst->width
  364. && dst->chars[dstix + stride] == CUCUL_MAGIC_FULLWIDTH)
  365. dst->chars[dstix + stride] = ' ';
  366. if(mask)
  367. {
  368. for(i = 0; i < stride; i++)
  369. {
  370. if(mask->chars[srcix + i] == (uint32_t)' ')
  371. continue;
  372. dst->chars[dstix + i] = src->chars[srcix + i];
  373. dst->attrs[dstix + i] = src->attrs[srcix + i];
  374. }
  375. }
  376. else
  377. {
  378. memcpy(dst->chars + dstix, src->chars + srcix, stride * 4);
  379. memcpy(dst->attrs + dstix, src->attrs + srcix, stride * 4);
  380. }
  381. /* Fix split fullwidth chars */
  382. if(src->chars[srcix] == CUCUL_MAGIC_FULLWIDTH)
  383. dst->chars[dstix] = ' ';
  384. if((unsigned int)endi < src->width
  385. && src->chars[endi] == CUCUL_MAGIC_FULLWIDTH)
  386. dst->chars[dstix + stride - 1] = ' ';
  387. }
  388. return 0;
  389. }
  390. /** \brief Set a canvas' new boundaries.
  391. *
  392. * Set new boundaries for a canvas. This function can be used to crop a
  393. * canvas, to expand it or for combinations of both actions. All frames
  394. * are affected by this function.
  395. *
  396. * If an error occurs, -1 is returned and \b errno is set accordingly:
  397. * - \c EBUSY The canvas is in use by a display driver and cannot be resized.
  398. * - \c ENOMEM Not enough memory for the requested canvas size. If this
  399. * happens, the canvas handle becomes invalid and should not be used.
  400. *
  401. * \param cv The canvas to crop.
  402. * \param x X coordinate of the top-left corner.
  403. * \param y Y coordinate of the top-left corner.
  404. * \param w The width of the cropped area.
  405. * \param h The height of the cropped area.
  406. * \return 0 in case of success, -1 if an error occurred.
  407. */
  408. int cucul_set_canvas_boundaries(cucul_canvas_t *cv, int x, int y,
  409. unsigned int w, unsigned int h)
  410. {
  411. cucul_canvas_t *new;
  412. unsigned int f, saved_f, framecount;
  413. if(cv->refcount)
  414. {
  415. seterrno(EBUSY);
  416. return -1;
  417. }
  418. new = cucul_create_canvas(w, h);
  419. framecount = cucul_get_frame_count(cv);
  420. saved_f = cv->frame;
  421. for(f = 0; f < framecount; f++)
  422. {
  423. if(f)
  424. cucul_create_frame(new, framecount);
  425. cucul_set_frame(cv, f);
  426. cucul_set_frame(new, f);
  427. cucul_blit(new, -x, -y, cv, NULL);
  428. free(cv->frames[f].chars);
  429. free(cv->frames[f].attrs);
  430. }
  431. free(cv->frames);
  432. cv->frames = new->frames;
  433. free(new);
  434. cucul_set_frame(cv, saved_f);
  435. return 0;
  436. }