Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 
 

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