25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

446 lines
13 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 functions for attribute management and colourspace
  15. * conversions.
  16. */
  17. #include "config.h"
  18. #include "common.h"
  19. #if defined(HAVE_ERRNO_H)
  20. # include <errno.h>
  21. #endif
  22. #include "cucul.h"
  23. #include "cucul_internals.h"
  24. static uint8_t nearest_ansi(uint16_t);
  25. /** \brief Get the text attribute at the given coordinates.
  26. *
  27. * Get the internal \e libcucul attribute value of the character at the
  28. * given coordinates. The attribute value has 32 significant bits,
  29. * organised as follows from MSB to LSB:
  30. * - 3 bits for the background alpha
  31. * - 4 bits for the background red component
  32. * - 4 bits for the background green component
  33. * - 3 bits for the background blue component
  34. * - 3 bits for the foreground alpha
  35. * - 4 bits for the foreground red component
  36. * - 4 bits for the foreground green component
  37. * - 3 bits for the foreground blue component
  38. * - 4 bits for the bold, italics, underline and blink flags
  39. *
  40. * If the coordinates are outside the canvas boundaries, the current
  41. * attribute is returned.
  42. *
  43. * This function never fails.
  44. *
  45. * \param cv A handle to the libcucul canvas.
  46. * \param x X coordinate.
  47. * \param y Y coordinate.
  48. * \return The requested attribute.
  49. */
  50. unsigned long int cucul_get_attr(cucul_canvas_t *cv, int x, int y)
  51. {
  52. if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
  53. return (unsigned long int)cv->curattr;
  54. return (unsigned long int)cv->attrs[x + y * cv->width];
  55. }
  56. /** \brief Set the default character attribute.
  57. *
  58. * Set the default character attribute for drawing. Attributes define
  59. * foreground and background colour, transparency, bold, italics and
  60. * underline styles, as well as blink. String functions such as
  61. * caca_printf() and graphical primitive functions such as caca_draw_line()
  62. * will use this attribute.
  63. *
  64. * The value of \e attr is either:
  65. * - a 32-bit integer as returned by cucul_get_attr(), in which case it
  66. * also contains colour information,
  67. * - a combination (bitwise OR) of style values (\e CUCUL_UNDERLINE,
  68. * \e CUCUL_BLINK, \e CUCUL_BOLD and \e CUCUL_ITALICS), in which case
  69. * setting the attribute does not modify the current colour information.
  70. *
  71. * To retrieve the current attribute value, use cucul_get_attr(-1,-1).
  72. *
  73. * If an error occurs, -1 is returned and \b errno is set accordingly:
  74. * - \c EINVAL The attribute value is out of the 32-bit range.
  75. *
  76. * \param cv A handle to the libcucul canvas.
  77. * \param attr The requested attribute value.
  78. * \return 0 in case of success, -1 if an error occurred.
  79. */
  80. int cucul_set_attr(cucul_canvas_t *cv, unsigned long int attr)
  81. {
  82. if(sizeof(unsigned long int) > sizeof(uint32_t) && attr > 0xffffffff)
  83. {
  84. #if defined(HAVE_ERRNO_H)
  85. errno = EINVAL;
  86. #endif
  87. return -1;
  88. }
  89. if(attr < 0x00000010)
  90. attr = (cv->curattr & 0xfffffff0) | attr;
  91. cv->curattr = attr;
  92. return 0;
  93. }
  94. /** \brief Set the character attribute at the given coordinates.
  95. *
  96. * Set the character attribute, without changing the character's value. If
  97. * the character at the given coordinates is a fullwidth character, both
  98. * cells' attributes are replaced.
  99. *
  100. * The value of \e attr is either:
  101. * - a 32-bit integer as returned by cucul_get_attr(), in which case it
  102. * also contains colour information,
  103. * - a combination (bitwise OR) of style values (\e CUCUL_UNDERLINE,
  104. * \e CUCUL_BLINK, \e CUCUL_BOLD and \e CUCUL_ITALICS), in which case
  105. * setting the attribute does not modify the current colour information.
  106. *
  107. * If an error occurs, -1 is returned and \b errno is set accordingly:
  108. * - \c EINVAL The attribute value is out of the 32-bit range.
  109. *
  110. * \param cv A handle to the libcucul canvas.
  111. * \param x X coordinate.
  112. * \param y Y coordinate.
  113. * \param attr The requested attribute value.
  114. * \return 0 in case of success, -1 if an error occurred.
  115. */
  116. int cucul_put_attr(cucul_canvas_t *cv, int x, int y, unsigned long int attr)
  117. {
  118. uint32_t *curattr, *curchar;
  119. if(sizeof(unsigned long int) > sizeof(uint32_t) && attr > 0xffffffff)
  120. {
  121. #if defined(HAVE_ERRNO_H)
  122. errno = EINVAL;
  123. #endif
  124. return -1;
  125. }
  126. if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
  127. return 0;
  128. curchar = cv->chars + x + y * cv->width;
  129. curattr = cv->attrs + x + y * cv->width;
  130. if(curattr[0] < 0x00000010)
  131. curattr[0] = (cv->curattr & 0xfffffff0) | curattr[0];
  132. if(x && curchar[0] == CUCUL_MAGIC_FULLWIDTH)
  133. curattr[-1] = curattr[0];
  134. else if(x + 1 < (int)cv->width && curchar[1] == CUCUL_MAGIC_FULLWIDTH)
  135. curattr[1] = curattr[0];
  136. return 0;
  137. }
  138. /** \brief Set the default colour pair for text (ANSI version).
  139. *
  140. * Set the default ANSI colour pair for text drawing. String functions such
  141. * as caca_printf() and graphical primitive functions such as caca_draw_line()
  142. * will use these attributes.
  143. *
  144. * Color values are those defined in cucul.h, such as CUCUL_RED
  145. * or CUCUL_TRANSPARENT.
  146. *
  147. * If an error occurs, 0 is returned and \b errno is set accordingly:
  148. * - \c EINVAL At least one of the colour values is invalid.
  149. *
  150. * \param cv A handle to the libcucul canvas.
  151. * \param fg The requested ANSI foreground colour.
  152. * \param bg The requested ANSI background colour.
  153. * \return 0 in case of success, -1 if an error occurred.
  154. */
  155. int cucul_set_color_ansi(cucul_canvas_t *cv, unsigned char fg, unsigned char bg)
  156. {
  157. uint32_t attr;
  158. if(fg > 0x20 || bg > 0x20)
  159. {
  160. #if defined(HAVE_ERRNO_H)
  161. errno = EINVAL;
  162. #endif
  163. return -1;
  164. }
  165. attr = ((uint32_t)(bg | 0x40) << 18) | ((uint32_t)(fg | 0x40) << 4);
  166. cv->curattr = (cv->curattr & 0x0000000f) | attr;
  167. return 0;
  168. }
  169. /** \brief Set the default colour pair for text (truecolor version).
  170. *
  171. * Set the default ARGB colour pair for text drawing. String functions such
  172. * as caca_printf() and graphical primitive functions such as caca_draw_line()
  173. * will use these attributes.
  174. *
  175. * Colors are 16-bit ARGB values, each component being coded on 4 bits. For
  176. * instance, 0xf088 is solid dark cyan (A=15 R=0 G=8 B=8), and 0x8fff is
  177. * white with 50% alpha (A=8 R=15 G=15 B=15).
  178. *
  179. * If an error occurs, 0 is returned and \b errno is set accordingly:
  180. * - \c EINVAL At least one of the colour values is invalid.
  181. *
  182. * \param cv A handle to the libcucul canvas.
  183. * \param fg The requested ARGB foreground colour.
  184. * \param bg The requested ARGB background colour.
  185. * \return 0 in case of success, -1 if an error occurred.
  186. */
  187. int cucul_set_color_argb(cucul_canvas_t *cv, unsigned int fg, unsigned int bg)
  188. {
  189. uint32_t attr;
  190. if(fg > 0xffff || bg > 0xffff)
  191. {
  192. #if defined(HAVE_ERRNO_H)
  193. errno = EINVAL;
  194. #endif
  195. return -1;
  196. }
  197. if(fg < 0x100)
  198. fg += 0x100;
  199. if(bg < 0x100)
  200. bg += 0x100;
  201. fg = ((fg >> 1) & 0x7ff) | ((fg >> 13) << 11);
  202. bg = ((bg >> 1) & 0x7ff) | ((bg >> 13) << 11);
  203. attr = ((uint32_t)bg << 18) | ((uint32_t)fg << 4);
  204. cv->curattr = (cv->curattr & 0x0000000f) | attr;
  205. return 0;
  206. }
  207. /** \brief Get DOS ANSI information from attribute.
  208. *
  209. * Get the ANSI colour pair for a given attribute. The returned value is
  210. * an 8-bit value whose higher 4 bits are the background colour and lower
  211. * 4 bits are the foreground colour.
  212. *
  213. * If the attribute has ARGB colours, the nearest colour is used. Special
  214. * attributes such as \e CUCUL_DEFAULT and \e CUCUL_TRANSPARENT are not
  215. * handled and are both replaced with \e CUCUL_LIGHTGRAY for the foreground
  216. * colour and \e CUCUL_BLACK for the background colour.
  217. *
  218. * This function never fails. If the attribute value is outside the expected
  219. * 32-bit range, higher order bits are simply ignored.
  220. *
  221. * \param attr The requested attribute value.
  222. * \return The corresponding DOS ANSI value.
  223. */
  224. unsigned char cucul_attr_to_ansi(unsigned long int attr)
  225. {
  226. uint8_t fg = nearest_ansi((attr >> 4) & 0x3fff);
  227. uint8_t bg = nearest_ansi(attr >> 18);
  228. return (fg < 0x10 ? fg : CUCUL_LIGHTGRAY)
  229. | ((bg < 0x10 ? bg : CUCUL_BLACK) << 4);
  230. }
  231. /** \brief Get ANSI foreground information from attribute.
  232. *
  233. * Get the ANSI foreground colour value for a given attribute. The returned
  234. * value is either one of the \e CUCUL_RED, \e CUCUL_BLACK etc. predefined
  235. * colours, or the special value \e CUCUL_DEFAULT meaning the media's
  236. * default foreground value, or the special value \e CUCUL_TRANSPARENT.
  237. *
  238. * If the attribute has ARGB colours, the nearest colour is returned.
  239. *
  240. * This function never fails. If the attribute value is outside the expected
  241. * 32-bit range, higher order bits are simply ignored.
  242. *
  243. * \param attr The requested attribute value.
  244. * \return The corresponding ANSI foreground value.
  245. */
  246. unsigned char cucul_attr_to_ansi_fg(unsigned long int attr)
  247. {
  248. return nearest_ansi(((uint16_t)attr >> 4) & 0x3fff);
  249. }
  250. /** \brief Get ANSI background information from attribute.
  251. *
  252. * Get the ANSI background colour value for a given attribute. The returned
  253. * value is either one of the \e CUCUL_RED, \e CUCUL_BLACK etc. predefined
  254. * colours, or the special value \e CUCUL_DEFAULT meaning the media's
  255. * default background value, or the special value \e CUCUL_TRANSPARENT.
  256. *
  257. * If the attribute has ARGB colours, the nearest colour is returned.
  258. *
  259. * This function never fails. If the attribute value is outside the expected
  260. * 32-bit range, higher order bits are simply ignored.
  261. *
  262. * \param attr The requested attribute value.
  263. * \return The corresponding ANSI background value.
  264. */
  265. unsigned char cucul_attr_to_ansi_bg(unsigned long int attr)
  266. {
  267. return nearest_ansi(attr >> 18);
  268. }
  269. /*
  270. * XXX: the following functions are local
  271. */
  272. /* RGB colours for the ANSI palette. There is no real standard, so we
  273. * use the same values as gnome-terminal. The 7th colour (brown) is a bit
  274. * special: 0xfa50 instead of 0xfaa0. */
  275. static const uint16_t ansitab16[16] =
  276. {
  277. 0xf000, 0xf00a, 0xf0a0, 0xf0aa, 0xfa00, 0xfa0a, 0xfa50, 0xfaaa,
  278. 0xf555, 0xf55f, 0xf5f5, 0xf5ff, 0xff55, 0xff5f, 0xfff5, 0xffff,
  279. };
  280. /* Same table, except on 14 bits (3-4-4-3) */
  281. static const uint16_t ansitab14[16] =
  282. {
  283. 0x3800, 0x3805, 0x3850, 0x3855, 0x3d00, 0x3d05, 0x3d28, 0x3d55,
  284. 0x3aaa, 0x3aaf, 0x3afa, 0x3aff, 0x3faa, 0x3faf, 0x3ffa, 0x3fff,
  285. };
  286. static uint8_t nearest_ansi(uint16_t argb14)
  287. {
  288. unsigned int i, best, dist;
  289. if(argb14 < (0x10 | 0x40))
  290. return argb14 ^ 0x40;
  291. if(argb14 == (CUCUL_DEFAULT | 0x40) || argb14 == (CUCUL_TRANSPARENT | 0x40))
  292. return argb14 ^ 0x40;
  293. if(argb14 < 0x0fff) /* too transparent */
  294. return CUCUL_TRANSPARENT;
  295. best = CUCUL_DEFAULT;
  296. dist = 0x3fff;
  297. for(i = 0; i < 16; i++)
  298. {
  299. unsigned int d = 0;
  300. int a, b;
  301. a = (ansitab14[i] >> 7) & 0xf;
  302. b = (argb14 >> 7) & 0xf;
  303. d += (a - b) * (a - b);
  304. a = (ansitab14[i] >> 3) & 0xf;
  305. b = (argb14 >> 3) & 0xf;
  306. d += (a - b) * (a - b);
  307. a = (ansitab14[i] << 1) & 0xf;
  308. b = (argb14 << 1) & 0xf;
  309. d += (a - b) * (a - b);
  310. if(d < dist)
  311. {
  312. dist = d;
  313. best = i;
  314. }
  315. }
  316. return best;
  317. }
  318. uint16_t _cucul_attr_to_rgb12fg(uint32_t attr)
  319. {
  320. uint16_t fg = (attr >> 4) & 0x3fff;
  321. if(fg < (0x10 | 0x40))
  322. return ansitab16[fg ^ 0x40] & 0x0fff;
  323. if(fg == (CUCUL_DEFAULT | 0x40))
  324. return ansitab16[CUCUL_LIGHTGRAY] & 0x0fff;
  325. if(fg == (CUCUL_TRANSPARENT | 0x40))
  326. return ansitab16[CUCUL_LIGHTGRAY] & 0x0fff;
  327. return (fg << 1) & 0x0fff;
  328. }
  329. uint16_t _cucul_attr_to_rgb12bg(uint32_t attr)
  330. {
  331. uint16_t bg = attr >> 18;
  332. if(bg < (0x10 | 0x40))
  333. return ansitab16[bg ^ 0x40] & 0x0fff;
  334. if(bg == (CUCUL_DEFAULT | 0x40))
  335. return ansitab16[CUCUL_BLACK] & 0x0fff;
  336. if(bg == (CUCUL_TRANSPARENT | 0x40))
  337. return ansitab16[CUCUL_BLACK] & 0x0fff;
  338. return (bg << 1) & 0x0fff;
  339. }
  340. #define RGB12TO24(i) \
  341. (((uint32_t)((i & 0xf00) >> 8) * 0x110000) \
  342. | ((uint32_t)((i & 0x0f0) >> 4) * 0x001100) \
  343. | ((uint32_t)(i & 0x00f) * 0x000011))
  344. uint32_t _cucul_attr_to_rgb24fg(uint32_t attr)
  345. {
  346. return RGB12TO24(_cucul_attr_to_rgb12fg(attr));
  347. }
  348. uint32_t _cucul_attr_to_rgb24bg(uint32_t attr)
  349. {
  350. return RGB12TO24(_cucul_attr_to_rgb12bg(attr));
  351. }
  352. void _cucul_attr_to_argb4(uint32_t attr, uint8_t argb[8])
  353. {
  354. uint16_t fg = (attr >> 4) & 0x3fff;
  355. uint16_t bg = attr >> 18;
  356. if(bg < (0x10 | 0x40))
  357. bg = ansitab16[bg ^ 0x40];
  358. else if(bg == (CUCUL_DEFAULT | 0x40))
  359. bg = ansitab16[CUCUL_BLACK];
  360. else if(bg == (CUCUL_TRANSPARENT | 0x40))
  361. bg = 0x0fff;
  362. else
  363. bg = ((bg << 2) & 0xf000) | ((bg << 1) & 0x0fff);
  364. argb[0] = bg >> 12;
  365. argb[1] = (bg >> 8) & 0xf;
  366. argb[2] = (bg >> 4) & 0xf;
  367. argb[3] = bg & 0xf;
  368. if(fg < (0x10 | 0x40))
  369. fg = ansitab16[fg ^ 0x40];
  370. else if(fg == (CUCUL_DEFAULT | 0x40))
  371. fg = ansitab16[CUCUL_LIGHTGRAY];
  372. else if(fg == (CUCUL_TRANSPARENT | 0x40))
  373. fg = 0x0fff;
  374. else
  375. fg = ((fg << 2) & 0xf000) | ((fg << 1) & 0x0fff);
  376. argb[4] = fg >> 12;
  377. argb[5] = (fg >> 8) & 0xf;
  378. argb[6] = (fg >> 4) & 0xf;
  379. argb[7] = fg & 0xf;
  380. }