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.
 
 
 
 
 
 

536 lines
17 KiB

  1. /*
  2. * libcaca Colour ASCII-Art library
  3. * Copyright (c) 2002-2012 Sam Hocevar <sam@hocevar.net>
  4. * All Rights Reserved
  5. *
  6. * This library is free software. It comes without any warranty, to
  7. * the extent permitted by applicable law. You can redistribute it
  8. * and/or modify it under the terms of the Do What the Fuck You Want
  9. * to Public License, Version 2, as published by Sam Hocevar. See
  10. * http://www.wtfpl.net/ for more details.
  11. */
  12. /*
  13. * This file contains font handling functions.
  14. */
  15. #include "config.h"
  16. #if !defined(__KERNEL__)
  17. # if defined(HAVE_ENDIAN_H)
  18. # include <endian.h>
  19. # endif
  20. # include <stdio.h>
  21. # include <stdlib.h>
  22. # include <string.h>
  23. #endif
  24. #include "caca.h"
  25. #include "caca_internals.h"
  26. /* Internal fonts */
  27. #include "mono9.data"
  28. #include "monobold12.data"
  29. /* Helper structures for font loading */
  30. #if !defined(_DOXYGEN_SKIP_ME)
  31. struct font_header
  32. {
  33. uint32_t control_size, data_size;
  34. uint16_t version, blocks;
  35. uint32_t glyphs;
  36. uint16_t bpp, width, height, maxwidth, maxheight, flags;
  37. };
  38. struct block_info
  39. {
  40. uint32_t start, stop, index;
  41. };
  42. struct glyph_info
  43. {
  44. uint16_t width, height;
  45. uint32_t data_offset;
  46. };
  47. struct caca_font
  48. {
  49. struct font_header header;
  50. struct block_info *block_list;
  51. uint32_t *user_block_list;
  52. struct glyph_info *glyph_list;
  53. uint8_t *font_data;
  54. uint8_t *private;
  55. };
  56. #endif
  57. #define DECLARE_UNPACKGLYPH(bpp) \
  58. static inline void \
  59. unpack_glyph ## bpp(uint8_t *glyph, uint8_t *packed_data, int n) \
  60. { \
  61. int i; \
  62. \
  63. for(i = 0; i < n; i++) \
  64. { \
  65. uint8_t pixel = packed_data[i / (8 / bpp)]; \
  66. pixel >>= bpp * ((8 / bpp) - 1 - (i % (8 / bpp))); \
  67. pixel %= (1 << bpp); \
  68. pixel *= 0xff / ((1 << bpp) - 1); \
  69. *glyph++ = pixel; \
  70. } \
  71. }
  72. DECLARE_UNPACKGLYPH(4)
  73. DECLARE_UNPACKGLYPH(2)
  74. DECLARE_UNPACKGLYPH(1)
  75. /** \brief Load a font from memory for future use.
  76. *
  77. * This function loads a font and returns a handle to its internal
  78. * structure. The handle can then be used with caca_render_canvas()
  79. * for bitmap output.
  80. *
  81. * Internal fonts can also be loaded: if \c size is set to 0, \c data must
  82. * be a string containing the internal font name.
  83. *
  84. * If \c size is non-zero, the \c size bytes of memory at address \c data
  85. * are loaded as a font. This memory are must not be freed by the calling
  86. * program until the font handle has been freed with caca_free_font().
  87. *
  88. * If an error occurs, NULL is returned and \b errno is set accordingly:
  89. * - \c ENOENT Requested built-in font does not exist.
  90. * - \c EINVAL Invalid font data in memory area.
  91. * - \c ENOMEM Not enough memory to allocate font structure.
  92. *
  93. * \param data The memory area containing the font or its name.
  94. * \param size The size of the memory area, or 0 if the font name is given.
  95. * \return A font handle or NULL in case of error.
  96. */
  97. caca_font_t *caca_load_font(void const *data, size_t size)
  98. {
  99. caca_font_t *f;
  100. int i;
  101. if(size == 0)
  102. {
  103. if(!strcasecmp(data, "Monospace 9"))
  104. return caca_load_font(mono9_data, mono9_size);
  105. if(!strcasecmp(data, "Monospace Bold 12"))
  106. return caca_load_font(monobold12_data, monobold12_size);
  107. seterrno(ENOENT);
  108. return NULL;
  109. }
  110. if(size < sizeof(struct font_header))
  111. {
  112. debug("font error: data size %i < header size %i",
  113. size, (int)sizeof(struct font_header));
  114. seterrno(EINVAL);
  115. return NULL;
  116. }
  117. f = malloc(sizeof(caca_font_t));
  118. if(!f)
  119. {
  120. seterrno(ENOMEM);
  121. return NULL;
  122. }
  123. f->private = (void *)(uintptr_t)data;
  124. memcpy(&f->header, f->private + 4, sizeof(struct font_header));
  125. f->header.control_size = hton32(f->header.control_size);
  126. f->header.data_size = hton32(f->header.data_size);
  127. f->header.version = hton16(f->header.version);
  128. f->header.blocks = hton16(f->header.blocks);
  129. f->header.glyphs = hton32(f->header.glyphs);
  130. f->header.bpp = hton16(f->header.bpp);
  131. f->header.width = hton16(f->header.width);
  132. f->header.height = hton16(f->header.height);
  133. f->header.maxwidth = hton16(f->header.maxwidth);
  134. f->header.maxheight = hton16(f->header.maxheight);
  135. f->header.flags = hton16(f->header.flags);
  136. if(size != 4 + f->header.control_size + f->header.data_size
  137. || (f->header.bpp != 8 && f->header.bpp != 4 &&
  138. f->header.bpp != 2 && f->header.bpp != 1)
  139. || (f->header.flags & 1) == 0)
  140. {
  141. #if defined DEBUG
  142. if(size != 4 + f->header.control_size + f->header.data_size)
  143. debug("font error: data size %i < expected size %i",
  144. size, 4 + f->header.control_size + f->header.data_size);
  145. else if(f->header.bpp != 8 && f->header.bpp != 4 &&
  146. f->header.bpp != 2 && f->header.bpp != 1)
  147. debug("font error: invalid bpp %i", f->header.bpp);
  148. else if((f->header.flags & 1) == 0)
  149. debug("font error: invalid flags %.04x", f->header.flags);
  150. #endif
  151. free(f);
  152. seterrno(EINVAL);
  153. return NULL;
  154. }
  155. f->block_list = malloc(f->header.blocks * sizeof(struct block_info));
  156. if(!f->block_list)
  157. {
  158. free(f);
  159. seterrno(ENOMEM);
  160. return NULL;
  161. }
  162. f->user_block_list = malloc((f->header.blocks + 1)
  163. * 2 * sizeof(uint32_t));
  164. if(!f->user_block_list)
  165. {
  166. free(f->block_list);
  167. free(f);
  168. seterrno(ENOMEM);
  169. return NULL;
  170. }
  171. memcpy(f->block_list,
  172. f->private + 4 + sizeof(struct font_header),
  173. f->header.blocks * sizeof(struct block_info));
  174. for(i = 0; i < f->header.blocks; i++)
  175. {
  176. f->block_list[i].start = hton32(f->block_list[i].start);
  177. f->block_list[i].stop = hton32(f->block_list[i].stop);
  178. f->block_list[i].index = hton32(f->block_list[i].index);
  179. if(f->block_list[i].start > f->block_list[i].stop
  180. || (i > 0 && f->block_list[i].start < f->block_list[i - 1].stop)
  181. || f->block_list[i].index >= f->header.glyphs)
  182. {
  183. #if defined DEBUG
  184. if(f->block_list[i].start > f->block_list[i].stop)
  185. debug("font error: block %i has start %i > stop %i",
  186. i, f->block_list[i].start, f->block_list[i].stop);
  187. else if(i > 0 && f->block_list[i].start < f->block_list[i - 1].stop)
  188. debug("font error: block %i has start %i < previous stop %i",
  189. f->block_list[i].start, f->block_list[i - 1].stop);
  190. else if(f->block_list[i].index >= f->header.glyphs)
  191. debug("font error: block %i has index >= glyph count %i",
  192. f->block_list[i].index, f->header.glyphs);
  193. #endif
  194. free(f->user_block_list);
  195. free(f->block_list);
  196. free(f);
  197. seterrno(EINVAL);
  198. return NULL;
  199. }
  200. f->user_block_list[i * 2] = f->block_list[i].start;
  201. f->user_block_list[i * 2 + 1] = f->block_list[i].stop;
  202. }
  203. f->user_block_list[i * 2] = 0;
  204. f->user_block_list[i * 2 + 1] = 0;
  205. f->glyph_list = malloc(f->header.glyphs * sizeof(struct glyph_info));
  206. if(!f->glyph_list)
  207. {
  208. free(f->user_block_list);
  209. free(f->block_list);
  210. free(f);
  211. seterrno(ENOMEM);
  212. return NULL;
  213. }
  214. memcpy(f->glyph_list,
  215. f->private + 4 + sizeof(struct font_header)
  216. + f->header.blocks * sizeof(struct block_info),
  217. f->header.glyphs * sizeof(struct glyph_info));
  218. for(i = 0; i < (int)f->header.glyphs; i++)
  219. {
  220. f->glyph_list[i].width = hton16(f->glyph_list[i].width);
  221. f->glyph_list[i].height = hton16(f->glyph_list[i].height);
  222. f->glyph_list[i].data_offset = hton32(f->glyph_list[i].data_offset);
  223. if(f->glyph_list[i].data_offset >= f->header.data_size
  224. || f->glyph_list[i].data_offset
  225. + (f->glyph_list[i].width * f->glyph_list[i].height *
  226. f->header.bpp + 7) / 8 > f->header.data_size
  227. || f->glyph_list[i].width > f->header.maxwidth
  228. || f->glyph_list[i].height > f->header.maxheight)
  229. {
  230. #if defined DEBUG
  231. if(f->glyph_list[i].data_offset >= f->header.data_size)
  232. debug("font error: glyph %i has data start %i > "
  233. "data end %i",
  234. f->glyph_list[i].data_offset, f->header.data_size);
  235. else if(f->glyph_list[i].data_offset
  236. + (f->glyph_list[i].width * f->glyph_list[i].height *
  237. f->header.bpp + 7) / 8 > f->header.data_size)
  238. debug("font error: glyph %i has data end %i > "
  239. "data end %i", f->glyph_list[i].data_offset
  240. + (f->glyph_list[i].width * f->glyph_list[i].height *
  241. f->header.bpp + 7) / 8, f->header.data_size);
  242. else if(f->glyph_list[i].width > f->header.maxwidth)
  243. debug("font error: glyph %i has width %i > max width %i",
  244. f->glyph_list[i].width, f->header.maxwidth);
  245. else if(f->glyph_list[i].height > f->header.maxheight)
  246. debug("font error: glyph %i has height %i > max height %i",
  247. f->glyph_list[i].height, f->header.maxheight);
  248. #endif
  249. free(f->glyph_list);
  250. free(f->user_block_list);
  251. free(f->block_list);
  252. free(f);
  253. seterrno(EINVAL);
  254. return NULL;
  255. }
  256. }
  257. f->font_data = f->private + 4 + f->header.control_size;
  258. return f;
  259. }
  260. /** \brief Get available builtin fonts
  261. *
  262. * Return a list of available builtin fonts. The list is a NULL-terminated
  263. * array of strings.
  264. *
  265. * This function never fails.
  266. *
  267. * \return An array of strings.
  268. */
  269. char const * const * caca_get_font_list(void)
  270. {
  271. static char const * const list[] =
  272. {
  273. "Monospace 9",
  274. "Monospace Bold 12",
  275. NULL
  276. };
  277. return list;
  278. }
  279. /** \brief Get a font's standard glyph width.
  280. *
  281. * Return the standard value for the current font's glyphs. Most glyphs in
  282. * the font will have this width, except fullwidth characters.
  283. *
  284. * This function never fails.
  285. *
  286. * \param f The font, as returned by caca_load_font()
  287. * \return The standard glyph width.
  288. */
  289. int caca_get_font_width(caca_font_t const *f)
  290. {
  291. return f->header.width;
  292. }
  293. /** \brief Get a font's standard glyph height.
  294. *
  295. * Returns the standard value for the current font's glyphs. Most glyphs in
  296. * the font will have this height.
  297. *
  298. * This function never fails.
  299. *
  300. * \param f The font, as returned by caca_load_font()
  301. * \return The standard glyph height.
  302. */
  303. int caca_get_font_height(caca_font_t const *f)
  304. {
  305. return f->header.height;
  306. }
  307. /** \brief Get a font's list of supported glyphs.
  308. *
  309. * This function returns the list of Unicode blocks supported by the
  310. * given font. The list is a zero-terminated list of indices. Here is
  311. * an example:
  312. *
  313. * \code
  314. * {
  315. * 0x0000, 0x0080, // Basic latin: A, B, C, a, b, c
  316. * 0x0080, 0x0100, // Latin-1 supplement: "A, 'e, ^u
  317. * 0x0530, 0x0590, // Armenian
  318. * 0x0000, 0x0000, // END
  319. * };
  320. * \endcode
  321. *
  322. * This function never fails.
  323. *
  324. * \param f The font, as returned by caca_load_font()
  325. * \return The list of Unicode blocks supported by the font.
  326. */
  327. uint32_t const *caca_get_font_blocks(caca_font_t const *f)
  328. {
  329. return (uint32_t const *)f->user_block_list;
  330. }
  331. /** \brief Free a font structure.
  332. *
  333. * This function frees all data allocated by caca_load_font(). The
  334. * font structure is no longer usable by other libcaca functions. Once
  335. * this function has returned, the memory area that was given to
  336. * caca_load_font() can be freed.
  337. *
  338. * This function never fails.
  339. *
  340. * \param f The font, as returned by caca_load_font()
  341. * \return This function always returns 0.
  342. */
  343. int caca_free_font(caca_font_t *f)
  344. {
  345. free(f->glyph_list);
  346. free(f->user_block_list);
  347. free(f->block_list);
  348. free(f);
  349. return 0;
  350. }
  351. /** \brief Render the canvas onto an image buffer.
  352. *
  353. * This function renders the given canvas on an image buffer using a specific
  354. * font. The pixel format is fixed (32-bit ARGB, 8 bits for each component).
  355. *
  356. * The required image width can be computed using
  357. * caca_get_canvas_width() and caca_get_font_width(). The required
  358. * height can be computed using caca_get_canvas_height() and
  359. * caca_get_font_height().
  360. *
  361. * Glyphs that do not fit in the image buffer are currently not rendered at
  362. * all. They may be cropped instead in future versions.
  363. *
  364. * If an error occurs, -1 is returned and \b errno is set accordingly:
  365. * - \c EINVAL Specified width, height or pitch is invalid.
  366. *
  367. * \param cv The canvas to render
  368. * \param f The font, as returned by caca_load_font()
  369. * \param buf The image buffer
  370. * \param width The width (in pixels) of the image buffer
  371. * \param height The height (in pixels) of the image buffer
  372. * \param pitch The pitch (in bytes) of an image buffer line.
  373. * \return 0 in case of success, -1 if an error occurred.
  374. */
  375. int caca_render_canvas(caca_canvas_t const *cv, caca_font_t const *f,
  376. void *buf, int width, int height, int pitch)
  377. {
  378. uint8_t *glyph = NULL;
  379. int x, y, xmax, ymax;
  380. if(width < 0 || height < 0 || pitch < 0)
  381. {
  382. seterrno(EINVAL);
  383. return -1;
  384. }
  385. if(f->header.bpp != 8)
  386. glyph = malloc(f->header.width * 2 * f->header.height);
  387. if(width < cv->width * f->header.width)
  388. xmax = width / f->header.width;
  389. else
  390. xmax = cv->width;
  391. if(height < cv->height * f->header.height)
  392. ymax = height / f->header.height;
  393. else
  394. ymax = cv->height;
  395. for(y = 0; y < ymax; y++)
  396. {
  397. for(x = 0; x < xmax; x++)
  398. {
  399. uint8_t argb[8];
  400. int starty = y * f->header.height;
  401. int startx = x * f->header.width;
  402. uint32_t ch = cv->chars[y * cv->width + x];
  403. uint32_t attr = cv->attrs[y * cv->width + x];
  404. int b, i, j;
  405. struct glyph_info *g;
  406. /* Find the Unicode block where our glyph lies */
  407. for(b = 0; b < f->header.blocks; b++)
  408. {
  409. if(ch < f->block_list[b].start)
  410. {
  411. b = f->header.blocks;
  412. break;
  413. }
  414. if(ch < f->block_list[b].stop)
  415. break;
  416. }
  417. /* Glyph not in font? Skip it. */
  418. if(b == f->header.blocks)
  419. continue;
  420. g = &f->glyph_list[f->block_list[b].index
  421. + ch - f->block_list[b].start];
  422. caca_attr_to_argb64(attr, argb);
  423. /* Step 1: unpack glyph */
  424. switch(f->header.bpp)
  425. {
  426. case 8:
  427. glyph = f->font_data + g->data_offset;
  428. break;
  429. case 4:
  430. unpack_glyph4(glyph, f->font_data + g->data_offset,
  431. g->width * g->height);
  432. break;
  433. case 2:
  434. unpack_glyph2(glyph, f->font_data + g->data_offset,
  435. g->width * g->height);
  436. break;
  437. case 1:
  438. unpack_glyph1(glyph, f->font_data + g->data_offset,
  439. g->width * g->height);
  440. break;
  441. }
  442. /* Step 2: render glyph using colour attribute */
  443. for(j = 0; j < g->height; j++)
  444. {
  445. uint8_t *line = buf;
  446. line += (starty + j) * pitch + 4 * startx;
  447. for(i = 0; i < g->width; i++)
  448. {
  449. uint8_t *pixel = line + 4 * i;
  450. uint32_t p, q, t;
  451. p = glyph[j * g->width + i];
  452. q = 0xff - p;
  453. for(t = 0; t < 4; t++)
  454. pixel[t] = (((q * argb[t]) + (p * argb[4 + t])) / 0xf);
  455. }
  456. }
  457. }
  458. }
  459. if(f->header.bpp != 8)
  460. free(glyph);
  461. return 0;
  462. }
  463. /*
  464. * XXX: The following functions are aliases.
  465. */
  466. cucul_font_t *cucul_load_font(void const *, size_t) CACA_ALIAS(caca_load_font);
  467. char const * const * cucul_get_font_list(void) CACA_ALIAS(caca_get_font_list);
  468. int cucul_get_font_width(cucul_font_t const *) CACA_ALIAS(caca_get_font_width);
  469. int cucul_get_font_height(cucul_font_t const *)
  470. CACA_ALIAS(caca_get_font_height);
  471. uint32_t const *cucul_get_font_blocks(cucul_font_t const *)
  472. CACA_ALIAS(caca_get_font_blocks);
  473. int cucul_render_canvas(cucul_canvas_t const *, cucul_font_t const *,
  474. void *, int, int, int) CACA_ALIAS(caca_render_canvas);
  475. int cucul_free_font(cucul_font_t *) CACA_ALIAS(caca_free_font);