source: libcaca/trunk/cucul/attr.c @ 2057

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