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

Last change on this file since 1383 was 1383, checked in by Sam Hocevar, 14 years ago
  • Fix a bug in cucul_put_attr. This function was simply not working.
  • 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 1383 2006-11-12 23:09:17Z 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(attr < 0x00000010)
139        curattr[0] = (curattr[0] & 0xfffffff0) | attr;
140    else
141        curattr[0] = attr;
142
143    if(x && curchar[0] == CUCUL_MAGIC_FULLWIDTH)
144        curattr[-1] = curattr[0];
145    else if(x + 1 < (int)cv->width && curchar[1] == CUCUL_MAGIC_FULLWIDTH)
146        curattr[1] = curattr[0];
147
148    return 0;
149}
150
151/** \brief Set the default colour pair for text (ANSI version).
152 *
153 *  Set the default ANSI colour pair for text drawing. String functions such
154 *  as caca_printf() and graphical primitive functions such as caca_draw_line()
155 *  will use these attributes.
156 *
157 *  Color values are those defined in cucul.h, such as CUCUL_RED
158 *  or CUCUL_TRANSPARENT.
159 *
160 *  If an error occurs, 0 is returned and \b errno is set accordingly:
161 *  - \c EINVAL At least one of the colour values is invalid.
162 *
163 *  \param cv A handle to the libcucul canvas.
164 *  \param fg The requested ANSI foreground colour.
165 *  \param bg The requested ANSI background colour.
166 *  \return 0 in case of success, -1 if an error occurred.
167 */
168int cucul_set_color_ansi(cucul_canvas_t *cv, unsigned char fg, unsigned char bg)
169{
170    uint32_t attr;
171
172    if(fg > 0x20 || bg > 0x20)
173    {
174        seterrno(EINVAL);
175        return -1;
176    }
177
178    attr = ((uint32_t)(bg | 0x40) << 18) | ((uint32_t)(fg | 0x40) << 4);
179    cv->curattr = (cv->curattr & 0x0000000f) | attr;
180
181    return 0;
182}
183
184/** \brief Set the default colour pair for text (truecolor version).
185 *
186 *  Set the default ARGB colour pair for text drawing. String functions such
187 *  as caca_printf() and graphical primitive functions such as caca_draw_line()
188 *  will use these attributes.
189 *
190 *  Colors are 16-bit ARGB values, each component being coded on 4 bits. For
191 *  instance, 0xf088 is solid dark cyan (A=15 R=0 G=8 B=8), and 0x8fff is
192 *  white with 50% alpha (A=8 R=15 G=15 B=15).
193 *
194 *  If an error occurs, 0 is returned and \b errno is set accordingly:
195 *  - \c EINVAL At least one of the colour values is invalid.
196 *
197 *  \param cv A handle to the libcucul canvas.
198 *  \param fg The requested ARGB foreground colour.
199 *  \param bg The requested ARGB background colour.
200 *  \return 0 in case of success, -1 if an error occurred.
201 */
202int cucul_set_color_argb(cucul_canvas_t *cv, unsigned int fg, unsigned int bg)
203{
204    uint32_t attr;
205
206    if(fg > 0xffff || bg > 0xffff)
207    {
208        seterrno(EINVAL);
209        return -1;
210    }
211
212    if(fg < 0x100)
213        fg += 0x100;
214
215    if(bg < 0x100)
216        bg += 0x100;
217
218    fg = ((fg >> 1) & 0x7ff) | ((fg >> 13) << 11);
219    bg = ((bg >> 1) & 0x7ff) | ((bg >> 13) << 11);
220
221    attr = ((uint32_t)bg << 18) | ((uint32_t)fg << 4);
222    cv->curattr = (cv->curattr & 0x0000000f) | attr;
223
224    return 0;
225}
226
227/** \brief Get DOS ANSI information from attribute.
228 *
229 *  Get the ANSI colour pair for a given attribute. The returned value is
230 *  an 8-bit value whose higher 4 bits are the background colour and lower
231 *  4 bits are the foreground colour.
232 *
233 *  If the attribute has ARGB colours, the nearest colour is used. Special
234 *  attributes such as \e CUCUL_DEFAULT and \e CUCUL_TRANSPARENT are not
235 *  handled and are both replaced with \e CUCUL_LIGHTGRAY for the foreground
236 *  colour and \e CUCUL_BLACK for the background colour.
237 *
238 *  This function never fails. If the attribute value is outside the expected
239 *  32-bit range, higher order bits are simply ignored.
240 *
241 *  \param attr The requested attribute value.
242 *  \return The corresponding DOS ANSI value.
243 */
244unsigned char cucul_attr_to_ansi(unsigned long int attr)
245{
246    uint8_t fg = nearest_ansi((attr >> 4) & 0x3fff);
247    uint8_t bg = nearest_ansi(attr >> 18);
248
249    return (fg < 0x10 ? fg : CUCUL_LIGHTGRAY)
250            | ((bg < 0x10 ? bg : CUCUL_BLACK) << 4);
251}
252
253/** \brief Get ANSI foreground information from attribute.
254 *
255 *  Get the ANSI foreground colour value for a given attribute. The returned
256 *  value is either one of the \e CUCUL_RED, \e CUCUL_BLACK etc. predefined
257 *  colours, or the special value \e CUCUL_DEFAULT meaning the media's
258 *  default foreground value, or the special value \e CUCUL_TRANSPARENT.
259 *
260 *  If the attribute has ARGB colours, the nearest colour is returned.
261 *
262 *  This function never fails. If the attribute value is outside the expected
263 *  32-bit range, higher order bits are simply ignored.
264 *
265 *  \param attr The requested attribute value.
266 *  \return The corresponding ANSI foreground value.
267 */
268unsigned char cucul_attr_to_ansi_fg(unsigned long int attr)
269{
270    return nearest_ansi(((uint16_t)attr >> 4) & 0x3fff);
271}
272
273/** \brief Get ANSI background information from attribute.
274 *
275 *  Get the ANSI background colour value for a given attribute. The returned
276 *  value is either one of the \e CUCUL_RED, \e CUCUL_BLACK etc. predefined
277 *  colours, or the special value \e CUCUL_DEFAULT meaning the media's
278 *  default background value, or the special value \e CUCUL_TRANSPARENT.
279 *
280 *  If the attribute has ARGB colours, the nearest colour is returned.
281 *
282 *  This function never fails. If the attribute value is outside the expected
283 *  32-bit range, higher order bits are simply ignored.
284 *
285 *  \param attr The requested attribute value.
286 *  \return The corresponding ANSI background value.
287 */
288unsigned char cucul_attr_to_ansi_bg(unsigned long int attr)
289{
290    return nearest_ansi(attr >> 18);
291}
292
293/*
294 * XXX: the following functions are local
295 */
296
297/* RGB colours for the ANSI palette. There is no real standard, so we
298 * use the same values as gnome-terminal. The 7th colour (brown) is a bit
299 * special: 0xfa50 instead of 0xfaa0. */
300static const uint16_t ansitab16[16] =
301{
302    0xf000, 0xf00a, 0xf0a0, 0xf0aa, 0xfa00, 0xfa0a, 0xfa50, 0xfaaa,
303    0xf555, 0xf55f, 0xf5f5, 0xf5ff, 0xff55, 0xff5f, 0xfff5, 0xffff,
304};
305
306/* Same table, except on 14 bits (3-4-4-3) */
307static const uint16_t ansitab14[16] =
308{
309    0x3800, 0x3805, 0x3850, 0x3855, 0x3d00, 0x3d05, 0x3d28, 0x3d55,
310    0x3aaa, 0x3aaf, 0x3afa, 0x3aff, 0x3faa, 0x3faf, 0x3ffa, 0x3fff,
311};
312
313static uint8_t nearest_ansi(uint16_t argb14)
314{
315    unsigned int i, best, dist;
316
317    if(argb14 < (0x10 | 0x40))
318        return argb14 ^ 0x40;
319
320    if(argb14 == (CUCUL_DEFAULT | 0x40) || argb14 == (CUCUL_TRANSPARENT | 0x40))
321        return argb14 ^ 0x40;
322
323    if(argb14 < 0x0fff) /* too transparent */
324        return CUCUL_TRANSPARENT;
325
326    best = CUCUL_DEFAULT;
327    dist = 0x3fff;
328    for(i = 0; i < 16; i++)
329    {
330        unsigned int d = 0;
331        int a, b;
332
333        a = (ansitab14[i] >> 7) & 0xf;
334        b = (argb14 >> 7) & 0xf;
335        d += (a - b) * (a - b);
336
337        a = (ansitab14[i] >> 3) & 0xf;
338        b = (argb14 >> 3) & 0xf;
339        d += (a - b) * (a - b);
340
341        a = (ansitab14[i] << 1) & 0xf;
342        b = (argb14 << 1) & 0xf;
343        d += (a - b) * (a - b);
344
345        if(d < dist)
346        {
347            dist = d;
348            best = i;
349        }
350    }
351
352    return best;
353}
354
355uint16_t _cucul_attr_to_rgb12fg(uint32_t attr)
356{
357    uint16_t fg = (attr >> 4) & 0x3fff;
358
359    if(fg < (0x10 | 0x40))
360        return ansitab16[fg ^ 0x40] & 0x0fff;
361
362    if(fg == (CUCUL_DEFAULT | 0x40))
363        return ansitab16[CUCUL_LIGHTGRAY] & 0x0fff;
364
365    if(fg == (CUCUL_TRANSPARENT | 0x40))
366        return ansitab16[CUCUL_LIGHTGRAY] & 0x0fff;
367
368    return (fg << 1) & 0x0fff;
369}
370
371uint16_t _cucul_attr_to_rgb12bg(uint32_t attr)
372{
373    uint16_t bg = attr >> 18;
374
375    if(bg < (0x10 | 0x40))
376        return ansitab16[bg ^ 0x40] & 0x0fff;
377
378    if(bg == (CUCUL_DEFAULT | 0x40))
379        return ansitab16[CUCUL_BLACK] & 0x0fff;
380
381    if(bg == (CUCUL_TRANSPARENT | 0x40))
382        return ansitab16[CUCUL_BLACK] & 0x0fff;
383
384    return (bg << 1) & 0x0fff;
385}
386
387#define RGB12TO24(i) \
388   (((uint32_t)((i & 0xf00) >> 8) * 0x110000) \
389  | ((uint32_t)((i & 0x0f0) >> 4) * 0x001100) \
390  | ((uint32_t)(i & 0x00f) * 0x000011))
391
392uint32_t _cucul_attr_to_rgb24fg(uint32_t attr)
393{
394    return RGB12TO24(_cucul_attr_to_rgb12fg(attr));
395}
396
397uint32_t _cucul_attr_to_rgb24bg(uint32_t attr)
398{
399    return RGB12TO24(_cucul_attr_to_rgb12bg(attr));
400}
401
402void _cucul_attr_to_argb4(uint32_t attr, uint8_t argb[8])
403{
404    uint16_t fg = (attr >> 4) & 0x3fff;
405    uint16_t bg = attr >> 18;
406
407    if(bg < (0x10 | 0x40))
408        bg = ansitab16[bg ^ 0x40];
409    else if(bg == (CUCUL_DEFAULT | 0x40))
410        bg = ansitab16[CUCUL_BLACK];
411    else if(bg == (CUCUL_TRANSPARENT | 0x40))
412        bg = 0x0fff;
413    else
414        bg = ((bg << 2) & 0xf000) | ((bg << 1) & 0x0fff);
415
416    argb[0] = bg >> 12;
417    argb[1] = (bg >> 8) & 0xf;
418    argb[2] = (bg >> 4) & 0xf;
419    argb[3] = bg & 0xf;
420
421    if(fg < (0x10 | 0x40))
422        fg = ansitab16[fg ^ 0x40];
423    else if(fg == (CUCUL_DEFAULT | 0x40))
424        fg = ansitab16[CUCUL_LIGHTGRAY];
425    else if(fg == (CUCUL_TRANSPARENT | 0x40))
426        fg = 0x0fff;
427    else
428        fg = ((fg << 2) & 0xf000) | ((fg << 1) & 0x0fff);
429
430    argb[4] = fg >> 12;
431    argb[5] = (fg >> 8) & 0xf;
432    argb[6] = (fg >> 4) & 0xf;
433    argb[7] = fg & 0xf;
434}
435
Note: See TracBrowser for help on using the repository browser.