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.

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