No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 
 

1565 líneas
40 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 bitmap dithering functions.
  16. */
  17. #include "config.h"
  18. #if !defined(__KERNEL__)
  19. # if defined(HAVE_ENDIAN_H)
  20. # include <endian.h>
  21. # endif
  22. # include <stdio.h>
  23. # include <stdlib.h>
  24. # include <limits.h>
  25. # include <string.h>
  26. #endif
  27. #include "cucul.h"
  28. #include "cucul_internals.h"
  29. #define CP437 0
  30. /*
  31. * Local variables
  32. */
  33. #if !defined(_DOXYGEN_SKIP_ME)
  34. # define LOOKUP_VAL 32
  35. # define LOOKUP_SAT 32
  36. # define LOOKUP_HUE 16
  37. #endif
  38. static uint8_t hsv_distances[LOOKUP_VAL][LOOKUP_SAT][LOOKUP_HUE];
  39. static uint16_t lookup_colors[8];
  40. static int lookup_initialised = 0;
  41. static int const hsv_palette[] =
  42. {
  43. /* weight, hue, saturation, value */
  44. 4, 0x0, 0x0, 0x0, /* black */
  45. 5, 0x0, 0x0, 0x5ff, /* 30% */
  46. 5, 0x0, 0x0, 0x9ff, /* 70% */
  47. 4, 0x0, 0x0, 0xfff, /* white */
  48. 3, 0x1000, 0xfff, 0x5ff, /* dark yellow */
  49. 2, 0x1000, 0xfff, 0xfff, /* light yellow */
  50. 3, 0x0, 0xfff, 0x5ff, /* dark red */
  51. 2, 0x0, 0xfff, 0xfff /* light red */
  52. };
  53. /* RGB palette for the new colour picker */
  54. static int const rgb_palette[] =
  55. {
  56. 0x0, 0x0, 0x0,
  57. 0x0, 0x0, 0x7ff,
  58. 0x0, 0x7ff, 0x0,
  59. 0x0, 0x7ff, 0x7ff,
  60. 0x7ff, 0x0, 0x0,
  61. 0x7ff, 0x0, 0x7ff,
  62. 0x7ff, 0x7ff, 0x0,
  63. 0xaaa, 0xaaa, 0xaaa,
  64. 0x555, 0x555, 0x555,
  65. 0x000, 0x000, 0xfff,
  66. 0x000, 0xfff, 0x000,
  67. 0x000, 0xfff, 0xfff,
  68. 0xfff, 0x000, 0x000,
  69. 0xfff, 0x000, 0xfff,
  70. 0xfff, 0xfff, 0x000,
  71. 0xfff, 0xfff, 0xfff,
  72. };
  73. static int const rgb_weight[] =
  74. {
  75. /* 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2 */
  76. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
  77. };
  78. /* List of glyphs */
  79. static uint32_t ascii_glyphs[] =
  80. {
  81. ' ', '.', ':', ';', 't', '%', 'S', 'X', '@', '8', '?'
  82. };
  83. static uint32_t shades_glyphs[] =
  84. {
  85. /* ' '. '·', '░', '▒', '?' */
  86. ' ', 0xb7, 0x2591, 0x2592, '?'
  87. };
  88. static uint32_t blocks_glyphs[] =
  89. {
  90. /* ' ', '▘', '▚', '?' */
  91. ' ', 0x2598, 0x259a, '?'
  92. };
  93. #if !defined(_DOXYGEN_SKIP_ME)
  94. enum color_mode
  95. {
  96. COLOR_MODE_MONO,
  97. COLOR_MODE_GRAY,
  98. COLOR_MODE_8,
  99. COLOR_MODE_16,
  100. COLOR_MODE_FULLGRAY,
  101. COLOR_MODE_FULL8,
  102. COLOR_MODE_FULL16
  103. };
  104. struct cucul_dither
  105. {
  106. int bpp, has_palette, has_alpha;
  107. int w, h, pitch;
  108. int rmask, gmask, bmask, amask;
  109. int rright, gright, bright, aright;
  110. int rleft, gleft, bleft, aleft;
  111. void (*get_hsv)(cucul_dither_t *, char *, int, int);
  112. int red[256], green[256], blue[256], alpha[256];
  113. /* Colour features */
  114. float gamma, brightness, contrast;
  115. int gammatab[4097];
  116. /* Dithering features */
  117. char const *antialias_name;
  118. int antialias;
  119. char const *color_name;
  120. enum color_mode color;
  121. char const *algo_name;
  122. void (*init_dither) (int);
  123. int (*get_dither) (void);
  124. void (*increment_dither) (void);
  125. char const *glyph_name;
  126. uint32_t const * glyphs;
  127. int glyph_count;
  128. int invert;
  129. };
  130. #define HSV_XRATIO 6
  131. #define HSV_YRATIO 3
  132. #define HSV_HRATIO 3
  133. #define HSV_DISTANCE(h, s, v, index) \
  134. (hsv_palette[index * 4] \
  135. * ((HSV_XRATIO * ((v) - hsv_palette[index * 4 + 3]) \
  136. * ((v) - hsv_palette[index * 4 + 3])) \
  137. + (hsv_palette[index * 4 + 3] \
  138. ? (HSV_YRATIO * ((s) - hsv_palette[index * 4 + 2]) \
  139. * ((s) - hsv_palette[index * 4 + 2])) \
  140. : 0) \
  141. + (hsv_palette[index * 4 + 2] \
  142. ? (HSV_HRATIO * ((h) - hsv_palette[index * 4 + 1]) \
  143. * ((h) - hsv_palette[index * 4 + 1])) \
  144. : 0)))
  145. #endif
  146. /*
  147. * Local prototypes
  148. */
  149. static void mask2shift(uint32_t, int *, int *);
  150. static float gammapow(float x, float y);
  151. static void get_rgba_default(cucul_dither_t const *, uint8_t *, int, int,
  152. unsigned int *);
  153. static int init_lookup(void);
  154. /* Dithering algorithms */
  155. static void init_no_dither(int);
  156. static int get_no_dither(void);
  157. static void increment_no_dither(void);
  158. static void init_fstein_dither(int);
  159. static int get_fstein_dither(void);
  160. static void increment_fstein_dither(void);
  161. static void init_ordered2_dither(int);
  162. static int get_ordered2_dither(void);
  163. static void increment_ordered2_dither(void);
  164. static void init_ordered4_dither(int);
  165. static int get_ordered4_dither(void);
  166. static void increment_ordered4_dither(void);
  167. static void init_ordered8_dither(int);
  168. static int get_ordered8_dither(void);
  169. static void increment_ordered8_dither(void);
  170. static void init_random_dither(int);
  171. static int get_random_dither(void);
  172. static void increment_random_dither(void);
  173. static inline int sq(int x)
  174. {
  175. return x * x;
  176. }
  177. static inline void rgb2hsv_default(int r, int g, int b,
  178. int *hue, int *sat, int *val)
  179. {
  180. int min, max, delta;
  181. min = r; max = r;
  182. if(min > g) min = g; if(max < g) max = g;
  183. if(min > b) min = b; if(max < b) max = b;
  184. delta = max - min; /* 0 - 0xfff */
  185. *val = max; /* 0 - 0xfff */
  186. if(delta)
  187. {
  188. *sat = 0xfff * delta / max; /* 0 - 0xfff */
  189. /* Generate *hue between 0 and 0x5fff */
  190. if( r == max )
  191. *hue = 0x1000 + 0x1000 * (g - b) / delta;
  192. else if( g == max )
  193. *hue = 0x3000 + 0x1000 * (b - r) / delta;
  194. else
  195. *hue = 0x5000 + 0x1000 * (r - g) / delta;
  196. }
  197. else
  198. {
  199. *sat = 0;
  200. *hue = 0;
  201. }
  202. }
  203. /** \brief Create an internal dither object.
  204. *
  205. * Create a dither structure from its coordinates (depth, width, height and
  206. * pitch) and pixel mask values. If the depth is 8 bits per pixel, the mask
  207. * values are ignored and the colour palette should be set using the
  208. * cucul_set_dither_palette() function. For depths greater than 8 bits per
  209. * pixel, a zero alpha mask causes the alpha values to be ignored.
  210. *
  211. * If an error occurs, NULL is returned and \b errno is set accordingly:
  212. * - \c EINVAL Requested width, height, pitch or bits per pixel value was
  213. * invalid.
  214. * - \c ENOMEM Not enough memory to allocate dither structure.
  215. *
  216. * \param bpp Bitmap depth in bits per pixel.
  217. * \param w Bitmap width in pixels.
  218. * \param h Bitmap height in pixels.
  219. * \param pitch Bitmap pitch in bytes.
  220. * \param rmask Bitmask for red values.
  221. * \param gmask Bitmask for green values.
  222. * \param bmask Bitmask for blue values.
  223. * \param amask Bitmask for alpha values.
  224. * \return Dither object upon success, NULL if an error occurred.
  225. */
  226. cucul_dither_t *cucul_create_dither(int bpp, int w, int h, int pitch,
  227. uint32_t rmask, uint32_t gmask,
  228. uint32_t bmask, uint32_t amask)
  229. {
  230. cucul_dither_t *d;
  231. int i;
  232. /* Minor sanity test */
  233. if(w < 0 || h < 0 || pitch < 0 || bpp > 32 || bpp < 8)
  234. {
  235. seterrno(EINVAL);
  236. return NULL;
  237. }
  238. d = malloc(sizeof(cucul_dither_t));
  239. if(!d)
  240. {
  241. seterrno(ENOMEM);
  242. return NULL;
  243. }
  244. if(!lookup_initialised)
  245. {
  246. /* XXX: because we do not wish to be thread-safe, there is a slight
  247. * chance that the following code will be executed twice. It is
  248. * totally harmless. */
  249. init_lookup();
  250. lookup_initialised = 1;
  251. }
  252. d->bpp = bpp;
  253. d->has_palette = 0;
  254. d->has_alpha = amask ? 1 : 0;
  255. d->w = w;
  256. d->h = h;
  257. d->pitch = pitch;
  258. d->rmask = rmask;
  259. d->gmask = gmask;
  260. d->bmask = bmask;
  261. d->amask = amask;
  262. /* Load bitmasks */
  263. if(rmask || gmask || bmask || amask)
  264. {
  265. mask2shift(rmask, &d->rright, &d->rleft);
  266. mask2shift(gmask, &d->gright, &d->gleft);
  267. mask2shift(bmask, &d->bright, &d->bleft);
  268. mask2shift(amask, &d->aright, &d->aleft);
  269. }
  270. /* In 8 bpp mode, default to a grayscale palette */
  271. if(bpp == 8)
  272. {
  273. d->has_palette = 1;
  274. d->has_alpha = 0;
  275. for(i = 0; i < 256; i++)
  276. {
  277. d->red[i] = i * 0xfff / 256;
  278. d->green[i] = i * 0xfff / 256;
  279. d->blue[i] = i * 0xfff / 256;
  280. }
  281. }
  282. /* Default gamma value */
  283. d->gamma = 1.0;
  284. for(i = 0; i < 4096; i++)
  285. d->gammatab[i] = i;
  286. /* Default colour properties */
  287. d->brightness = 1.0;
  288. d->contrast = 1.0;
  289. /* Default features */
  290. d->antialias_name = "prefilter";
  291. d->antialias = 1;
  292. d->color_name = "full16";
  293. d->color = COLOR_MODE_FULL16;
  294. d->glyph_name = "ascii";
  295. d->glyphs = ascii_glyphs;
  296. d->glyph_count = sizeof(ascii_glyphs) / sizeof(*ascii_glyphs);
  297. d->algo_name = "fstein";
  298. d->init_dither = init_fstein_dither;
  299. d->get_dither = get_fstein_dither;
  300. d->increment_dither = increment_fstein_dither;
  301. d->invert = 0;
  302. return d;
  303. }
  304. /** \brief Set the palette of an 8bpp dither object.
  305. *
  306. * Set the palette of an 8 bits per pixel bitmap. Values should be between
  307. * 0 and 4095 (0xfff).
  308. *
  309. * If an error occurs, -1 is returned and \b errno is set accordingly:
  310. * - \c EINVAL Dither bits per pixel value is not 8, or one of the pixel
  311. * values was outside the range 0 - 4095.
  312. *
  313. * \param d Dither object.
  314. * \param red Array of 256 red values.
  315. * \param green Array of 256 green values.
  316. * \param blue Array of 256 blue values.
  317. * \param alpha Array of 256 alpha values.
  318. * \return 0 in case of success, -1 if an error occurred.
  319. */
  320. int cucul_set_dither_palette(cucul_dither_t *d,
  321. uint32_t red[], uint32_t green[],
  322. uint32_t blue[], uint32_t alpha[])
  323. {
  324. int i, has_alpha = 0;
  325. if(d->bpp != 8)
  326. {
  327. seterrno(EINVAL);
  328. return -1;
  329. }
  330. for(i = 0; i < 256; i++)
  331. {
  332. if((red[i] | green[i] | blue[i] | alpha[i]) >= 0x1000)
  333. {
  334. seterrno(EINVAL);
  335. return -1;
  336. }
  337. }
  338. for(i = 0; i < 256; i++)
  339. {
  340. d->red[i] = red[i];
  341. d->green[i] = green[i];
  342. d->blue[i] = blue[i];
  343. if(alpha[i])
  344. {
  345. d->alpha[i] = alpha[i];
  346. has_alpha = 1;
  347. }
  348. }
  349. d->has_alpha = has_alpha;
  350. return 0;
  351. }
  352. /** \brief Set the brightness of a dither object.
  353. *
  354. * Set the brightness of dither.
  355. *
  356. * If an error occurs, -1 is returned and \b errno is set accordingly:
  357. * - \c EINVAL Brightness value was out of range.
  358. *
  359. * \param d Dither object.
  360. * \param brightness brightness value.
  361. * \return 0 in case of success, -1 if an error occurred.
  362. */
  363. int cucul_set_dither_brightness(cucul_dither_t *d, float brightness)
  364. {
  365. /* FIXME */
  366. d->brightness = brightness;
  367. return 0;
  368. }
  369. /** \brief Get the brightness of a dither object.
  370. *
  371. * Get the brightness of the given dither object.
  372. *
  373. * This function never fails.
  374. *
  375. * \param d Dither object.
  376. * \return Brightness value.
  377. */
  378. float cucul_get_dither_brightness(cucul_dither_t const *d)
  379. {
  380. return d->brightness;
  381. }
  382. /** \brief Set the gamma of a dither object.
  383. *
  384. * Set the gamma of the given dither object. A negative value causes
  385. * colour inversion.
  386. *
  387. * If an error occurs, -1 is returned and \b errno is set accordingly:
  388. * - \c EINVAL Gamma value was out of range.
  389. *
  390. * \param d Dither object.
  391. * \param gamma Gamma value.
  392. * \return 0 in case of success, -1 if an error occurred.
  393. */
  394. int cucul_set_dither_gamma(cucul_dither_t *d, float gamma)
  395. {
  396. /* FIXME: we don't need 4096 calls to gammapow(), we could just compute
  397. * a few of them and do linear interpolation for the rest. This will
  398. * probably speed up things a lot. */
  399. int i;
  400. if(gamma < 0.0)
  401. {
  402. d->invert = 1;
  403. gamma = -gamma;
  404. }
  405. else if(gamma == 0.0)
  406. {
  407. seterrno(EINVAL);
  408. return -1;
  409. }
  410. d->gamma = gamma;
  411. for(i = 0; i < 4096; i++)
  412. d->gammatab[i] = 4096.0 * gammapow((float)i / 4096.0, 1.0 / gamma);
  413. return 0;
  414. }
  415. /** \brief Get the gamma of a dither object.
  416. *
  417. * Get the gamma of the given dither object.
  418. *
  419. * This function never fails.
  420. *
  421. * \param d Dither object.
  422. * \return Gamma value.
  423. */
  424. float cucul_get_dither_gamma(cucul_dither_t const *d)
  425. {
  426. return d->gamma;
  427. }
  428. /** \brief Set the contrast of a dither object.
  429. *
  430. * Set the contrast of dither.
  431. *
  432. * If an error occurs, -1 is returned and \b errno is set accordingly:
  433. * - \c EINVAL Contrast value was out of range.
  434. *
  435. * \param d Dither object.
  436. * \param contrast contrast value.
  437. * \return 0 in case of success, -1 if an error occurred.
  438. */
  439. int cucul_set_dither_contrast(cucul_dither_t *d, float contrast)
  440. {
  441. /* FIXME */
  442. d->contrast = contrast;
  443. return 0;
  444. }
  445. /** \brief Get the contrast of a dither object.
  446. *
  447. * Get the contrast of the given dither object.
  448. *
  449. * This function never fails.
  450. *
  451. * \param d Dither object.
  452. * \return Contrast value.
  453. */
  454. float cucul_get_dither_contrast(cucul_dither_t const *d)
  455. {
  456. return d->contrast;
  457. }
  458. /** \brief Set dither antialiasing
  459. *
  460. * Tell the renderer whether to antialias the dither. Antialiasing smoothens
  461. * the rendered image and avoids the commonly seen staircase effect.
  462. * - \c "none": no antialiasing.
  463. * - \c "prefilter" or \c "default": simple prefilter antialiasing. This
  464. * is the default value.
  465. *
  466. * If an error occurs, -1 is returned and \b errno is set accordingly:
  467. * - \c EINVAL Invalid antialiasing mode.
  468. *
  469. * \param d Dither object.
  470. * \param str A string describing the antialiasing method that will be used
  471. * for the dithering.
  472. * \return 0 in case of success, -1 if an error occurred.
  473. */
  474. int cucul_set_dither_antialias(cucul_dither_t *d, char const *str)
  475. {
  476. if(!strcasecmp(str, "none"))
  477. {
  478. d->antialias_name = "none";
  479. d->antialias = 0;
  480. }
  481. else if(!strcasecmp(str, "prefilter") || !strcasecmp(str, "default"))
  482. {
  483. d->antialias_name = "prefilter";
  484. d->antialias = 1;
  485. }
  486. else
  487. {
  488. seterrno(EINVAL);
  489. return -1;
  490. }
  491. return 0;
  492. }
  493. /** \brief Get available antialiasing methods
  494. *
  495. * Return a list of available antialiasing methods for a given dither. The
  496. * list is a NULL-terminated array of strings, interleaving a string
  497. * containing the internal value for the antialiasing method to be used with
  498. * cucul_set_dither_antialias(), and a string containing the natural
  499. * language description for that antialiasing method.
  500. *
  501. * This function never fails.
  502. *
  503. * \param d Dither object.
  504. * \return An array of strings.
  505. */
  506. char const * const *
  507. cucul_get_dither_antialias_list(cucul_dither_t const *d)
  508. {
  509. static char const * const list[] =
  510. {
  511. "none", "No antialiasing",
  512. "prefilter", "Prefilter antialiasing",
  513. NULL, NULL
  514. };
  515. return list;
  516. }
  517. /** \brief Get current antialiasing method
  518. *
  519. * Return the given dither's current antialiasing method.
  520. *
  521. * This function never fails.
  522. *
  523. * \param d Dither object.
  524. * \return A static string.
  525. */
  526. char const * cucul_get_dither_antialias(cucul_dither_t const *d)
  527. {
  528. return d->antialias_name;
  529. }
  530. /** \brief Choose colours used for dithering
  531. *
  532. * Tell the renderer which colours should be used to render the
  533. * bitmap. Valid values for \c str are:
  534. * - \c "mono": use light gray on a black background.
  535. * - \c "gray": use white and two shades of gray on a black background.
  536. * - \c "8": use the 8 ANSI colours on a black background.
  537. * - \c "16": use the 16 ANSI colours on a black background.
  538. * - \c "fullgray": use black, white and two shades of gray for both the
  539. * characters and the background.
  540. * - \c "full8": use the 8 ANSI colours for both the characters and the
  541. * background.
  542. * - \c "full16" or \c "default": use the 16 ANSI colours for both the
  543. * characters and the background. This is the default value.
  544. *
  545. * If an error occurs, -1 is returned and \b errno is set accordingly:
  546. * - \c EINVAL Invalid colour set.
  547. *
  548. * \param d Dither object.
  549. * \param str A string describing the colour set that will be used
  550. * for the dithering.
  551. * \return 0 in case of success, -1 if an error occurred.
  552. */
  553. int cucul_set_dither_color(cucul_dither_t *d, char const *str)
  554. {
  555. if(!strcasecmp(str, "mono"))
  556. {
  557. d->color_name = "mono";
  558. d->color = COLOR_MODE_MONO;
  559. }
  560. else if(!strcasecmp(str, "gray"))
  561. {
  562. d->color_name = "gray";
  563. d->color = COLOR_MODE_GRAY;
  564. }
  565. else if(!strcasecmp(str, "8"))
  566. {
  567. d->color_name = "8";
  568. d->color = COLOR_MODE_8;
  569. }
  570. else if(!strcasecmp(str, "16"))
  571. {
  572. d->color_name = "16";
  573. d->color = COLOR_MODE_16;
  574. }
  575. else if(!strcasecmp(str, "fullgray"))
  576. {
  577. d->color_name = "fullgray";
  578. d->color = COLOR_MODE_FULLGRAY;
  579. }
  580. else if(!strcasecmp(str, "full8"))
  581. {
  582. d->color_name = "full8";
  583. d->color = COLOR_MODE_FULL8;
  584. }
  585. else if(!strcasecmp(str, "full16") || !strcasecmp(str, "default"))
  586. {
  587. d->color_name = "full16";
  588. d->color = COLOR_MODE_FULL16;
  589. }
  590. else
  591. {
  592. seterrno(EINVAL);
  593. return -1;
  594. }
  595. return 0;
  596. }
  597. /** \brief Get available colour modes
  598. *
  599. * Return a list of available colour modes for a given dither. The list
  600. * is a NULL-terminated array of strings, interleaving a string containing
  601. * the internal value for the colour mode, to be used with
  602. * cucul_set_dither_color(), and a string containing the natural
  603. * language description for that colour mode.
  604. *
  605. * This function never fails.
  606. *
  607. * \param d Dither object.
  608. * \return An array of strings.
  609. */
  610. char const * const *
  611. cucul_get_dither_color_list(cucul_dither_t const *d)
  612. {
  613. static char const * const list[] =
  614. {
  615. "mono", "white on black",
  616. "gray", "grayscale on black",
  617. "8", "8 colours on black",
  618. "16", "16 colours on black",
  619. "fullgray", "full grayscale",
  620. "full8", "full 8 colours",
  621. "full16", "full 16 colours",
  622. NULL, NULL
  623. };
  624. return list;
  625. }
  626. /** \brief Get current colour mode
  627. *
  628. * Return the given dither's current colour mode.
  629. *
  630. * This function never fails.
  631. *
  632. * \param d Dither object.
  633. * \return A static string.
  634. */
  635. char const * cucul_get_dither_color(cucul_dither_t const *d)
  636. {
  637. return d->color_name;
  638. }
  639. /** \brief Choose characters used for dithering
  640. *
  641. * Tell the renderer which characters should be used to render the
  642. * dither. Valid values for \c str are:
  643. * - \c "ascii" or \c "default": use only ASCII characters. This is the
  644. * default value.
  645. * - \c "shades": use Unicode characters "U+2591 LIGHT SHADE", "U+2592
  646. * MEDIUM SHADE" and "U+2593 DARK SHADE". These characters are also
  647. * present in the CP437 codepage available on DOS and VGA.
  648. * - \c "blocks": use Unicode quarter-cell block combinations. These
  649. * characters are only found in the Unicode set.
  650. *
  651. * If an error occurs, -1 is returned and \b errno is set accordingly:
  652. * - \c EINVAL Invalid character set.
  653. *
  654. * \param d Dither object.
  655. * \param str A string describing the characters that need to be used
  656. * for the dithering.
  657. * \return 0 in case of success, -1 if an error occurred.
  658. */
  659. int cucul_set_dither_charset(cucul_dither_t *d, char const *str)
  660. {
  661. if(!strcasecmp(str, "shades"))
  662. {
  663. d->glyph_name = "shades";
  664. d->glyphs = shades_glyphs;
  665. d->glyph_count = sizeof(shades_glyphs) / sizeof(*shades_glyphs);
  666. }
  667. else if(!strcasecmp(str, "blocks"))
  668. {
  669. d->glyph_name = "blocks";
  670. d->glyphs = blocks_glyphs;
  671. d->glyph_count = sizeof(blocks_glyphs) / sizeof(*blocks_glyphs);
  672. }
  673. else if(!strcasecmp(str, "ascii") || !strcasecmp(str, "default"))
  674. {
  675. d->glyph_name = "ascii";
  676. d->glyphs = ascii_glyphs;
  677. d->glyph_count = sizeof(ascii_glyphs) / sizeof(*ascii_glyphs);
  678. }
  679. else
  680. {
  681. seterrno(EINVAL);
  682. return -1;
  683. }
  684. return 0;
  685. }
  686. /** \brief Get available dither character sets
  687. *
  688. * Return a list of available character sets for a given dither. The list
  689. * is a NULL-terminated array of strings, interleaving a string containing
  690. * the internal value for the character set, to be used with
  691. * cucul_set_dither_charset(), and a string containing the natural
  692. * language description for that character set.
  693. *
  694. * This function never fails.
  695. *
  696. * \param d Dither object.
  697. * \return An array of strings.
  698. */
  699. char const * const * cucul_get_dither_charset_list(cucul_dither_t const *d)
  700. {
  701. static char const * const list[] =
  702. {
  703. "ascii", "plain ASCII",
  704. "shades", "CP437 shades",
  705. "blocks", "Unicode blocks",
  706. NULL, NULL
  707. };
  708. return list;
  709. }
  710. /** \brief Get current character set
  711. *
  712. * Return the given dither's current character set.
  713. *
  714. * This function never fails.
  715. *
  716. * \param d Dither object.
  717. * \return A static string.
  718. */
  719. char const * cucul_get_dither_charset(cucul_dither_t const *d)
  720. {
  721. return d->glyph_name;
  722. }
  723. /** \brief Set dithering algorithm
  724. *
  725. * Tell the renderer which dithering algorithm should be used. Dithering is
  726. * necessary because the picture being rendered has usually far more colours
  727. * than the available palette. Valid values for \c str are:
  728. * - \c "none": no dithering is used, the nearest matching colour is used.
  729. * - \c "ordered2": use a 2x2 Bayer matrix for dithering.
  730. * - \c "ordered4": use a 4x4 Bayer matrix for dithering.
  731. * - \c "ordered8": use a 8x8 Bayer matrix for dithering.
  732. * - \c "random": use random dithering.
  733. * - \c "fstein": use Floyd-Steinberg dithering. This is the default value.
  734. *
  735. * If an error occurs, -1 is returned and \b errno is set accordingly:
  736. * - \c EINVAL Unknown dithering mode.
  737. *
  738. * \param d Dither object.
  739. * \param str A string describing the algorithm that needs to be used
  740. * for the dithering.
  741. * \return 0 in case of success, -1 if an error occurred.
  742. */
  743. int cucul_set_dither_algorithm(cucul_dither_t *d, char const *str)
  744. {
  745. if(!strcasecmp(str, "none"))
  746. {
  747. d->algo_name = "none";
  748. d->init_dither = init_no_dither;
  749. d->get_dither = get_no_dither;
  750. d->increment_dither = increment_no_dither;
  751. }
  752. else if(!strcasecmp(str, "ordered2"))
  753. {
  754. d->algo_name = "ordered2";
  755. d->init_dither = init_ordered2_dither;
  756. d->get_dither = get_ordered2_dither;
  757. d->increment_dither = increment_ordered2_dither;
  758. }
  759. else if(!strcasecmp(str, "ordered4"))
  760. {
  761. d->algo_name = "ordered4";
  762. d->init_dither = init_ordered4_dither;
  763. d->get_dither = get_ordered4_dither;
  764. d->increment_dither = increment_ordered4_dither;
  765. }
  766. else if(!strcasecmp(str, "ordered8"))
  767. {
  768. d->algo_name = "ordered8";
  769. d->init_dither = init_ordered8_dither;
  770. d->get_dither = get_ordered8_dither;
  771. d->increment_dither = increment_ordered8_dither;
  772. }
  773. else if(!strcasecmp(str, "random"))
  774. {
  775. d->algo_name = "random";
  776. d->init_dither = init_random_dither;
  777. d->get_dither = get_random_dither;
  778. d->increment_dither = increment_random_dither;
  779. }
  780. else if(!strcasecmp(str, "fstein") || !strcasecmp(str, "default"))
  781. {
  782. d->algo_name = "fstein";
  783. d->init_dither = init_fstein_dither;
  784. d->get_dither = get_fstein_dither;
  785. d->increment_dither = increment_fstein_dither;
  786. }
  787. else
  788. {
  789. seterrno(EINVAL);
  790. return -1;
  791. }
  792. return 0;
  793. }
  794. /** \brief Get dithering algorithms
  795. *
  796. * Return a list of available dithering algorithms for a given dither. The
  797. * list is a NULL-terminated array of strings, interleaving a string
  798. * containing the internal value for the dithering algorithm, to be used
  799. * with cucul_set_dither_dithering(), and a string containing the natural
  800. * language description for that algorithm.
  801. *
  802. * This function never fails.
  803. *
  804. * \param d Dither object.
  805. * \return An array of strings.
  806. */
  807. char const * const * cucul_get_dither_algorithm_list(cucul_dither_t const *d)
  808. {
  809. static char const * const list[] =
  810. {
  811. "none", "no dithering",
  812. "ordered2", "2x2 ordered dithering",
  813. "ordered4", "4x4 ordered dithering",
  814. "ordered8", "8x8 ordered dithering",
  815. "random", "random dithering",
  816. "fstein", "Floyd-Steinberg dithering",
  817. NULL, NULL
  818. };
  819. return list;
  820. }
  821. /** \brief Get current dithering algorithm
  822. *
  823. * Return the given dither's current dithering algorithm.
  824. *
  825. * This function never fails.
  826. *
  827. * \param d Dither object.
  828. * \return A static string.
  829. */
  830. char const * cucul_get_dither_algorithm(cucul_dither_t const *d)
  831. {
  832. return d->algo_name;
  833. }
  834. /** \brief Dither a bitmap on the canvas.
  835. *
  836. * Dither a bitmap at the given coordinates. The dither can be of any size
  837. * and will be stretched to the text area.
  838. *
  839. * This function never fails.
  840. *
  841. * \param cv A handle to the libcucul canvas.
  842. * \param x X coordinate of the upper-left corner of the drawing area.
  843. * \param y Y coordinate of the upper-left corner of the drawing area.
  844. * \param w Width of the drawing area.
  845. * \param h Height of the drawing area.
  846. * \param d Dither object to be drawn.
  847. * \param pixels Bitmap's pixels.
  848. * \return This function always returns 0.
  849. */
  850. int cucul_dither_bitmap(cucul_canvas_t *cv, int x, int y, int w, int h,
  851. cucul_dither_t const *d, void *pixels)
  852. {
  853. int *floyd_steinberg, *fs_r, *fs_g, *fs_b;
  854. uint32_t savedattr;
  855. int fs_length;
  856. int x1, y1, x2, y2, pitch, deltax, deltay, dchmax;
  857. if(!d || !pixels)
  858. return 0;
  859. savedattr = cucul_get_attr(cv, -1, -1);
  860. x1 = x; x2 = x + w - 1;
  861. y1 = y; y2 = y + h - 1;
  862. /* FIXME: do not overwrite arguments */
  863. w = d->w;
  864. h = d->h;
  865. pitch = d->pitch;
  866. deltax = x2 - x1 + 1;
  867. deltay = y2 - y1 + 1;
  868. dchmax = d->glyph_count;
  869. fs_length = ((int)cv->width <= x2 ? (int)cv->width : x2) + 1;
  870. floyd_steinberg = malloc(3 * (fs_length + 2) * sizeof(int));
  871. memset(floyd_steinberg, 0, 3 * (fs_length + 2) * sizeof(int));
  872. fs_r = floyd_steinberg + 1;
  873. fs_g = fs_r + fs_length + 2;
  874. fs_b = fs_g + fs_length + 2;
  875. for(y = y1 > 0 ? y1 : 0; y <= y2 && y <= (int)cv->height; y++)
  876. {
  877. int remain_r = 0, remain_g = 0, remain_b = 0;
  878. for(x = x1 > 0 ? x1 : 0, d->init_dither(y);
  879. x <= x2 && x <= (int)cv->width;
  880. x++)
  881. {
  882. unsigned int rgba[4];
  883. int error[3];
  884. int i, ch = 0, distmin;
  885. int fg_r = 0, fg_g = 0, fg_b = 0, bg_r, bg_g, bg_b;
  886. int fromx, fromy, tox, toy, myx, myy, dots, dist;
  887. int outfg = 0, outbg = 0;
  888. uint32_t outch;
  889. rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
  890. /* First get RGB */
  891. if(d->antialias)
  892. {
  893. fromx = (x - x1) * w / deltax;
  894. fromy = (y - y1) * h / deltay;
  895. tox = (x - x1 + 1) * w / deltax;
  896. toy = (y - y1 + 1) * h / deltay;
  897. /* We want at least one pixel */
  898. if(tox == fromx) tox++;
  899. if(toy == fromy) toy++;
  900. dots = 0;
  901. for(myx = fromx; myx < tox; myx++)
  902. for(myy = fromy; myy < toy; myy++)
  903. {
  904. dots++;
  905. get_rgba_default(d, pixels, myx, myy, rgba);
  906. }
  907. /* Normalize */
  908. rgba[0] /= dots;
  909. rgba[1] /= dots;
  910. rgba[2] /= dots;
  911. rgba[3] /= dots;
  912. }
  913. else
  914. {
  915. fromx = (x - x1) * w / deltax;
  916. fromy = (y - y1) * h / deltay;
  917. tox = (x - x1 + 1) * w / deltax;
  918. toy = (y - y1 + 1) * h / deltay;
  919. /* tox and toy can overflow the canvas, but they cannot overflow
  920. * when averaged with fromx and fromy because these are guaranteed
  921. * to be within the pixel boundaries. */
  922. myx = (fromx + tox) / 2;
  923. myy = (fromy + toy) / 2;
  924. get_rgba_default(d, pixels, myx, myy, rgba);
  925. }
  926. if(d->has_alpha && rgba[3] < 0x800)
  927. {
  928. remain_r = remain_g = remain_b = 0;
  929. fs_r[x] = 0;
  930. fs_g[x] = 0;
  931. fs_b[x] = 0;
  932. continue;
  933. }
  934. /* XXX: OMG HAX */
  935. if(d->init_dither == init_fstein_dither)
  936. {
  937. rgba[0] += remain_r;
  938. rgba[1] += remain_g;
  939. rgba[2] += remain_b;
  940. }
  941. else
  942. {
  943. rgba[0] += (d->get_dither() - 0x80) * 4;
  944. rgba[1] += (d->get_dither() - 0x80) * 4;
  945. rgba[2] += (d->get_dither() - 0x80) * 4;
  946. }
  947. distmin = INT_MAX;
  948. for(i = 0; i < 16; i++)
  949. {
  950. dist = sq(rgba[0] - rgb_palette[i * 3])
  951. + sq(rgba[1] - rgb_palette[i * 3 + 1])
  952. + sq(rgba[2] - rgb_palette[i * 3 + 2]);
  953. dist *= rgb_weight[i];
  954. if(dist < distmin)
  955. {
  956. outbg = i;
  957. distmin = dist;
  958. }
  959. }
  960. bg_r = rgb_palette[outbg * 3];
  961. bg_g = rgb_palette[outbg * 3 + 1];
  962. bg_b = rgb_palette[outbg * 3 + 2];
  963. /* FIXME: we currently only honour "full16" */
  964. if(d->color == COLOR_MODE_FULL16)
  965. {
  966. distmin = INT_MAX;
  967. for(i = 0; i < 16; i++)
  968. {
  969. if(i == outbg)
  970. continue;
  971. dist = sq(rgba[0] - rgb_palette[i * 3])
  972. + sq(rgba[1] - rgb_palette[i * 3 + 1])
  973. + sq(rgba[2] - rgb_palette[i * 3 + 2]);
  974. dist *= rgb_weight[i];
  975. if(dist < distmin)
  976. {
  977. outfg = i;
  978. distmin = dist;
  979. }
  980. }
  981. fg_r = rgb_palette[outfg * 3];
  982. fg_g = rgb_palette[outfg * 3 + 1];
  983. fg_b = rgb_palette[outfg * 3 + 2];
  984. distmin = INT_MAX;
  985. for(i = 0; i < dchmax - 1; i++)
  986. {
  987. int newr = i * fg_r + ((2*dchmax-1) - i) * bg_r;
  988. int newg = i * fg_g + ((2*dchmax-1) - i) * bg_g;
  989. int newb = i * fg_b + ((2*dchmax-1) - i) * bg_b;
  990. dist = abs(rgba[0] * (2*dchmax-1) - newr)
  991. + abs(rgba[1] * (2*dchmax-1) - newg)
  992. + abs(rgba[2] * (2*dchmax-1) - newb);
  993. if(dist < distmin)
  994. {
  995. ch = i;
  996. distmin = dist;
  997. }
  998. }
  999. outch = d->glyphs[ch];
  1000. /* XXX: OMG HAX */
  1001. if(d->init_dither == init_fstein_dither)
  1002. {
  1003. error[0] = rgba[0] - (fg_r * ch + bg_r * ((2*dchmax-1) - ch)) / (2*dchmax-1);
  1004. error[1] = rgba[1] - (fg_g * ch + bg_g * ((2*dchmax-1) - ch)) / (2*dchmax-1);
  1005. error[2] = rgba[2] - (fg_b * ch + bg_b * ((2*dchmax-1) - ch)) / (2*dchmax-1);
  1006. }
  1007. }
  1008. else
  1009. {
  1010. unsigned int lum = rgba[0];
  1011. if(rgba[1] > lum) lum = rgba[1];
  1012. if(rgba[2] > lum) lum = rgba[2];
  1013. outfg = outbg;
  1014. outbg = CUCUL_BLACK;
  1015. ch = lum * dchmax / 0x1000;
  1016. if(ch < 0)
  1017. ch = 0;
  1018. else if(ch > (int)(dchmax - 1))
  1019. ch = dchmax - 1;
  1020. outch = d->glyphs[ch];
  1021. /* XXX: OMG HAX */
  1022. if(d->init_dither == init_fstein_dither)
  1023. {
  1024. error[0] = rgba[0] - bg_r * ch / (dchmax-1);
  1025. error[1] = rgba[1] - bg_g * ch / (dchmax-1);
  1026. error[2] = rgba[2] - bg_b * ch / (dchmax-1);
  1027. }
  1028. }
  1029. /* XXX: OMG HAX */
  1030. if(d->init_dither == init_fstein_dither)
  1031. {
  1032. remain_r = fs_r[x+1] + 7 * error[0] / 16;
  1033. remain_g = fs_g[x+1] + 7 * error[1] / 16;
  1034. remain_b = fs_b[x+1] + 7 * error[2] / 16;
  1035. fs_r[x-1] += 3 * error[0] / 16;
  1036. fs_g[x-1] += 3 * error[1] / 16;
  1037. fs_b[x-1] += 3 * error[2] / 16;
  1038. fs_r[x] = 5 * error[0] / 16;
  1039. fs_g[x] = 5 * error[1] / 16;
  1040. fs_b[x] = 5 * error[2] / 16;
  1041. fs_r[x+1] = 1 * error[0] / 16;
  1042. fs_g[x+1] = 1 * error[1] / 16;
  1043. fs_b[x+1] = 1 * error[2] / 16;
  1044. }
  1045. if(d->invert)
  1046. {
  1047. outfg = 15 - outfg;
  1048. outbg = 15 - outbg;
  1049. }
  1050. /* Now output the character */
  1051. cucul_set_color_ansi(cv, outfg, outbg);
  1052. cucul_put_char(cv, x, y, outch);
  1053. d->increment_dither();
  1054. }
  1055. /* end loop */
  1056. }
  1057. free(floyd_steinberg);
  1058. cucul_set_attr(cv, savedattr);
  1059. return 0;
  1060. }
  1061. /** \brief Free the memory associated with a dither.
  1062. *
  1063. * Free the memory allocated by cucul_create_dither().
  1064. *
  1065. * This function never fails.
  1066. *
  1067. * \param d Dither object.
  1068. * \return This function always returns 0.
  1069. */
  1070. int cucul_free_dither(cucul_dither_t *d)
  1071. {
  1072. if(!d)
  1073. return 0;
  1074. free(d);
  1075. return 0;
  1076. }
  1077. /*
  1078. * XXX: The following functions are local.
  1079. */
  1080. /* Convert a mask, eg. 0x0000ff00, to shift values, eg. 8 and -4. */
  1081. static void mask2shift(uint32_t mask, int *right, int *left)
  1082. {
  1083. int rshift = 0, lshift = 0;
  1084. if(!mask)
  1085. {
  1086. *right = *left = 0;
  1087. return;
  1088. }
  1089. while(!(mask & 1))
  1090. {
  1091. mask >>= 1;
  1092. rshift++;
  1093. }
  1094. *right = rshift;
  1095. while(mask & 1)
  1096. {
  1097. mask >>= 1;
  1098. lshift++;
  1099. }
  1100. *left = 12 - lshift;
  1101. }
  1102. /* Compute x^y without relying on the math library */
  1103. static float gammapow(float x, float y)
  1104. {
  1105. #ifdef HAVE_FLDLN2
  1106. register double logx;
  1107. register long double v, e;
  1108. #else
  1109. register float tmp, t, t2, r;
  1110. int i;
  1111. #endif
  1112. if(x == 0.0)
  1113. return y == 0.0 ? 1.0 : 0.0;
  1114. #ifdef HAVE_FLDLN2
  1115. /* FIXME: this can be optimised by directly calling fyl2x for x and y */
  1116. asm volatile("fldln2; fxch; fyl2x"
  1117. : "=t" (logx) : "0" (x) : "st(1)");
  1118. asm volatile("fldl2e\n\t"
  1119. "fmul %%st(1)\n\t"
  1120. "fst %%st(1)\n\t"
  1121. "frndint\n\t"
  1122. "fxch\n\t"
  1123. "fsub %%st(1)\n\t"
  1124. "f2xm1\n\t"
  1125. : "=t" (v), "=u" (e) : "0" (y * logx));
  1126. v += 1.0;
  1127. asm volatile("fscale"
  1128. : "=t" (v) : "0" (v), "u" (e));
  1129. return v;
  1130. #else
  1131. /* Compute ln(x) for x ∈ ]0,1]
  1132. * ln(x) = 2 * (t + t^3/3 + t^5/5 + ...) with t = (x-1)/(x+1)
  1133. * The convergence is a bit slow, especially when x is near 0. */
  1134. t = (x - 1.0) / (x + 1.0);
  1135. t2 = t * t;
  1136. tmp = r = t;
  1137. for(i = 3; i < 20; i += 2)
  1138. {
  1139. r *= t2;
  1140. tmp += r / i;
  1141. }
  1142. /* Compute -y*ln(x) */
  1143. tmp = - y * 2.0 * tmp;
  1144. /* Compute x^-y as e^t where t = -y*ln(x):
  1145. * e^t = 1 + t/1! + t^2/2! + t^3/3! + t^4/4! + t^5/5! ...
  1146. * The convergence is quite faster here, thanks to the factorial. */
  1147. r = t = tmp;
  1148. tmp = 1.0 + t;
  1149. for(i = 2; i < 16; i++)
  1150. {
  1151. r = r * t / i;
  1152. tmp += r;
  1153. }
  1154. /* Return x^y as 1/(x^-y) */
  1155. return 1.0 / tmp;
  1156. #endif
  1157. }
  1158. static void get_rgba_default(cucul_dither_t const *d, uint8_t *pixels,
  1159. int x, int y, unsigned int *rgba)
  1160. {
  1161. uint32_t bits;
  1162. pixels += (d->bpp / 8) * x + d->pitch * y;
  1163. switch(d->bpp / 8)
  1164. {
  1165. case 4:
  1166. bits = *(uint32_t *)pixels;
  1167. break;
  1168. case 3:
  1169. {
  1170. #if defined(HAVE_ENDIAN_H)
  1171. if(__BYTE_ORDER == __BIG_ENDIAN)
  1172. #else
  1173. /* This is compile-time optimised with at least -O1 or -Os */
  1174. uint32_t const tmp = 0x12345678;
  1175. if(*(uint8_t const *)&tmp == 0x12)
  1176. #endif
  1177. bits = ((uint32_t)pixels[0] << 16) |
  1178. ((uint32_t)pixels[1] << 8) |
  1179. ((uint32_t)pixels[2]);
  1180. else
  1181. bits = ((uint32_t)pixels[2] << 16) |
  1182. ((uint32_t)pixels[1] << 8) |
  1183. ((uint32_t)pixels[0]);
  1184. break;
  1185. }
  1186. case 2:
  1187. bits = *(uint16_t *)pixels;
  1188. break;
  1189. case 1:
  1190. default:
  1191. bits = pixels[0];
  1192. break;
  1193. }
  1194. if(d->has_palette)
  1195. {
  1196. rgba[0] += d->gammatab[d->red[bits]];
  1197. rgba[1] += d->gammatab[d->green[bits]];
  1198. rgba[2] += d->gammatab[d->blue[bits]];
  1199. rgba[3] += d->alpha[bits];
  1200. }
  1201. else
  1202. {
  1203. rgba[0] += d->gammatab[((bits & d->rmask) >> d->rright) << d->rleft];
  1204. rgba[1] += d->gammatab[((bits & d->gmask) >> d->gright) << d->gleft];
  1205. rgba[2] += d->gammatab[((bits & d->bmask) >> d->bright) << d->bleft];
  1206. rgba[3] += ((bits & d->amask) >> d->aright) << d->aleft;
  1207. }
  1208. }
  1209. /*
  1210. * No dithering
  1211. */
  1212. static void init_no_dither(int line)
  1213. {
  1214. ;
  1215. }
  1216. static int get_no_dither(void)
  1217. {
  1218. return 0x80;
  1219. }
  1220. static void increment_no_dither(void)
  1221. {
  1222. return;
  1223. }
  1224. /*
  1225. * Floyd-Steinberg dithering
  1226. */
  1227. static void init_fstein_dither(int line)
  1228. {
  1229. ;
  1230. }
  1231. static int get_fstein_dither(void)
  1232. {
  1233. return 0x80;
  1234. }
  1235. static void increment_fstein_dither(void)
  1236. {
  1237. return;
  1238. }
  1239. /*
  1240. * Ordered 2 dithering
  1241. */
  1242. static int const *ordered2_table;
  1243. static int ordered2_index;
  1244. static void init_ordered2_dither(int line)
  1245. {
  1246. static int const dither2x2[] =
  1247. {
  1248. 0x00, 0x80,
  1249. 0xc0, 0x40,
  1250. };
  1251. ordered2_table = dither2x2 + (line % 2) * 2;
  1252. ordered2_index = 0;
  1253. }
  1254. static int get_ordered2_dither(void)
  1255. {
  1256. return ordered2_table[ordered2_index];
  1257. }
  1258. static void increment_ordered2_dither(void)
  1259. {
  1260. ordered2_index = (ordered2_index + 1) % 2;
  1261. }
  1262. /*
  1263. * Ordered 4 dithering
  1264. */
  1265. /*static int dither4x4[] = { 5, 0, 1, 6,
  1266. -1, -6, -5, 2,
  1267. -2, -7, -8, 3,
  1268. 4, -3, -4, -7};*/
  1269. static int const *ordered4_table;
  1270. static int ordered4_index;
  1271. static void init_ordered4_dither(int line)
  1272. {
  1273. static int const dither4x4[] =
  1274. {
  1275. 0x00, 0x80, 0x20, 0xa0,
  1276. 0xc0, 0x40, 0xe0, 0x60,
  1277. 0x30, 0xb0, 0x10, 0x90,
  1278. 0xf0, 0x70, 0xd0, 0x50
  1279. };
  1280. ordered4_table = dither4x4 + (line % 4) * 4;
  1281. ordered4_index = 0;
  1282. }
  1283. static int get_ordered4_dither(void)
  1284. {
  1285. return ordered4_table[ordered4_index];
  1286. }
  1287. static void increment_ordered4_dither(void)
  1288. {
  1289. ordered4_index = (ordered4_index + 1) % 4;
  1290. }
  1291. /*
  1292. * Ordered 8 dithering
  1293. */
  1294. static int const *ordered8_table;
  1295. static int ordered8_index;
  1296. static void init_ordered8_dither(int line)
  1297. {
  1298. static int const dither8x8[] =
  1299. {
  1300. 0x00, 0x80, 0x20, 0xa0, 0x08, 0x88, 0x28, 0xa8,
  1301. 0xc0, 0x40, 0xe0, 0x60, 0xc8, 0x48, 0xe8, 0x68,
  1302. 0x30, 0xb0, 0x10, 0x90, 0x38, 0xb8, 0x18, 0x98,
  1303. 0xf0, 0x70, 0xd0, 0x50, 0xf8, 0x78, 0xd8, 0x58,
  1304. 0x0c, 0x8c, 0x2c, 0xac, 0x04, 0x84, 0x24, 0xa4,
  1305. 0xcc, 0x4c, 0xec, 0x6c, 0xc4, 0x44, 0xe4, 0x64,
  1306. 0x3c, 0xbc, 0x1c, 0x9c, 0x34, 0xb4, 0x14, 0x94,
  1307. 0xfc, 0x7c, 0xdc, 0x5c, 0xf4, 0x74, 0xd4, 0x54,
  1308. };
  1309. ordered8_table = dither8x8 + (line % 8) * 8;
  1310. ordered8_index = 0;
  1311. }
  1312. static int get_ordered8_dither(void)
  1313. {
  1314. return ordered8_table[ordered8_index];
  1315. }
  1316. static void increment_ordered8_dither(void)
  1317. {
  1318. ordered8_index = (ordered8_index + 1) % 8;
  1319. }
  1320. /*
  1321. * Random dithering
  1322. */
  1323. static void init_random_dither(int line)
  1324. {
  1325. ;
  1326. }
  1327. static int get_random_dither(void)
  1328. {
  1329. return cucul_rand(0x00, 0x100);
  1330. }
  1331. static void increment_random_dither(void)
  1332. {
  1333. return;
  1334. }
  1335. /*
  1336. * Lookup tables
  1337. */
  1338. static int init_lookup(void)
  1339. {
  1340. int v, s, h;
  1341. /* These ones are constant */
  1342. lookup_colors[0] = CUCUL_BLACK;
  1343. lookup_colors[1] = CUCUL_DARKGRAY;
  1344. lookup_colors[2] = CUCUL_LIGHTGRAY;
  1345. lookup_colors[3] = CUCUL_WHITE;
  1346. /* These ones will be overwritten */
  1347. lookup_colors[4] = CUCUL_MAGENTA;
  1348. lookup_colors[5] = CUCUL_LIGHTMAGENTA;
  1349. lookup_colors[6] = CUCUL_RED;
  1350. lookup_colors[7] = CUCUL_LIGHTRED;
  1351. for(v = 0; v < LOOKUP_VAL; v++)
  1352. for(s = 0; s < LOOKUP_SAT; s++)
  1353. for(h = 0; h < LOOKUP_HUE; h++)
  1354. {
  1355. int i, distbg, distfg, dist;
  1356. int val, sat, hue;
  1357. uint8_t outbg, outfg;
  1358. val = 0xfff * v / (LOOKUP_VAL - 1);
  1359. sat = 0xfff * s / (LOOKUP_SAT - 1);
  1360. hue = 0xfff * h / (LOOKUP_HUE - 1);
  1361. /* Initialise distances to the distance between pure black HSV
  1362. * coordinates and our white colour (3) */
  1363. outbg = outfg = 3;
  1364. distbg = distfg = HSV_DISTANCE(0, 0, 0, 3);
  1365. /* Calculate distances to eight major colour values and store the
  1366. * two nearest points in our lookup table. */
  1367. for(i = 0; i < 8; i++)
  1368. {
  1369. dist = HSV_DISTANCE(hue, sat, val, i);
  1370. if(dist <= distbg)
  1371. {
  1372. outfg = outbg;
  1373. distfg = distbg;
  1374. outbg = i;
  1375. distbg = dist;
  1376. }
  1377. else if(dist <= distfg)
  1378. {
  1379. outfg = i;
  1380. distfg = dist;
  1381. }
  1382. }
  1383. hsv_distances[v][s][h] = (outfg << 4) | outbg;
  1384. }
  1385. return 0;
  1386. }