Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 
 
 

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