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.
 
 
 
 
 
 

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