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

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