source: libcaca/trunk/cucul/cucul.c @ 568

Last change on this file since 568 was 568, checked in by Sam Hocevar, 14 years ago
  • Added glue code to compile libcaca without a libc and build applications as multiboot kernels.
  • Property svn:keywords set to Id
File size: 12.1 KB
Line 
1/*
2 *  libcucul      Unicode canvas library
3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the Do What The Fuck You Want To
8 *  Public License, Version 2, as published by Sam Hocevar. See
9 *  http://sam.zoy.org/wtfpl/COPYING for more details.
10 */
11
12/** \file cucul.c
13 *  \version \$Id: cucul.c 568 2006-03-09 12:47:37Z sam $
14 *  \author Sam Hocevar <sam@zoy.org>
15 *  \brief Main \e libcucul functions
16 *
17 *  This file contains the main functions used by \e libcucul applications
18 *  to initialise a drawing context.
19 */
20
21#include "config.h"
22
23#if !defined(__KERNEL__)
24#   include <stdlib.h>
25#   include <string.h>
26#endif
27
28#include "cucul.h"
29#include "cucul_internals.h"
30
31static void cucul_read_environment(cucul_t *);
32void _cucul_set_size(cucul_t *, unsigned int, unsigned int);
33
34/** \brief Initialise \e libcucul.
35 *
36 *  This function initialises internal \e libcucul structures and the backend
37 *  that will be used for subsequent graphical operations. It must be the
38 *  first \e libcucul function to be called in a function. cucul_end() should
39 *  be called at the end of the program to free all allocated resources.
40 *
41 *  \return 0 upon success, a non-zero value if an error occurs.
42 */
43cucul_t * cucul_init(void)
44{
45    cucul_t *qq = malloc(sizeof(cucul_t));
46    int i;
47
48    cucul_read_environment(qq);
49
50    qq->fgcolor = CUCUL_COLOR_LIGHTGRAY;
51    qq->bgcolor = CUCUL_COLOR_BLACK;
52
53    /* Initialise to a default size. When a graphic driver attaches to
54     * us, we'll adapt to its size. */
55    qq->width = 80;
56    qq->height = 32;
57
58    qq->chars = malloc(qq->width * qq->height * sizeof(uint32_t));
59    qq->attr = malloc(qq->width * qq->height * sizeof(uint8_t));
60
61    for(i = qq->width * qq->height; i--; )
62        qq->chars[i] = (uint32_t)' ';
63    memset(qq->attr, 0, qq->width * qq->height * sizeof(uint8_t));
64
65    qq->empty_line = malloc(qq->width + 1);
66    memset(qq->empty_line, ' ', qq->width);
67    qq->empty_line[qq->width] = '\0';
68
69    qq->scratch_line = malloc(qq->width + 1);
70
71    if(_cucul_init_bitmap())
72    {
73        free(qq);
74        return NULL;
75    }
76
77    qq->ansi_buffer = NULL;
78   
79    return qq;
80}
81
82/** \brief Set the canvas size.
83 *
84 *  This function sets the canvas width and height, in character cells.
85 *
86 *  It is an error to try to resize the canvas if an output driver has
87 *  been attached to the canvas using caca_attach(). You need to remove
88 *  the output driver using caca_detach() before you can change the
89 *  canvas size again.
90 *
91 *  However, the caca output driver can cause a canvas resize through
92 *  user interaction. See the caca_event() documentation for more about
93 *  this.
94 *
95 *  \param width The desired canvas width
96 *  \param height The desired canvas height
97 */
98void cucul_set_size(cucul_t *qq, unsigned int width, unsigned int height)
99{
100    if(qq->refcount)
101        return;
102
103    _cucul_set_size(qq, width, height);
104}
105
106/** \brief Get the canvas width.
107 *
108 *  This function returns the current canvas width, in character cells.
109 *
110 *  \return The canvas width.
111 */
112unsigned int cucul_get_width(cucul_t *qq)
113{
114    return qq->width;
115}
116
117/** \brief Get the canvas height.
118 *
119 *  This function returns the current canvas height, in character cells.
120 *
121 *  \return The canvas height.
122 */
123unsigned int cucul_get_height(cucul_t *qq)
124{
125    return qq->height;
126}
127
128/** \brief Translate a colour index into the colour's name.
129 *
130 *  This function translates a cucul_color enum into a human-readable
131 *  description string of the associated colour.
132 *
133 *  \param color The colour value.
134 *  \return A static string containing the colour's name.
135 */
136char const *cucul_get_color_name(enum cucul_color color)
137{
138    static char const *color_names[] =
139    {
140        "black",
141        "blue",
142        "green",
143        "cyan",
144        "red",
145        "magenta",
146        "brown",
147        "light gray",
148        "dark gray",
149        "light blue",
150        "light green",
151        "light cyan",
152        "light red",
153        "light magenta",
154        "yellow",
155        "white",
156    };
157
158    if(color < 0 || color > 15)
159        return "unknown";
160
161    return color_names[color];
162}
163
164/** \brief Get the current value of a feature.
165 *
166 *  This function retrieves the value of an internal \e libcucul feature. A
167 *  generic feature value is expected, such as CUCUL_ANTIALIASING.
168 *
169 *  \param feature The requested feature.
170 *  \return The current value of the feature or CUCUL_FEATURE_UNKNOWN if an
171 *          error occurred..
172 */
173enum cucul_feature cucul_get_feature(cucul_t *qq, enum cucul_feature feature)
174{
175    switch(feature)
176    {
177        case CUCUL_BACKGROUND:
178            return qq->background;
179        case CUCUL_ANTIALIASING:
180            return qq->antialiasing;
181        case CUCUL_DITHERING:
182            return qq->dithering;
183
184        default:
185            return CUCUL_FEATURE_UNKNOWN;
186    }
187}
188
189/** \brief Set a feature.
190 *
191 *  This function sets an internal \e libcucul feature such as the antialiasing
192 *  or dithering modes. If a specific feature such as CUCUL_DITHERING_RANDOM,
193 *  cucul_set_feature() will set it immediately. If a generic feature is given
194 *  instead, such as CUCUL_DITHERING, the default value will be used instead.
195 *
196 *  \param feature The requested feature.
197 */
198void cucul_set_feature(cucul_t *qq, enum cucul_feature feature)
199{
200    switch(feature)
201    {
202        case CUCUL_BACKGROUND:
203            feature = CUCUL_BACKGROUND_SOLID;
204        case CUCUL_BACKGROUND_BLACK:
205        case CUCUL_BACKGROUND_SOLID:
206            qq->background = feature;
207            break;
208
209        case CUCUL_ANTIALIASING:
210            feature = CUCUL_ANTIALIASING_PREFILTER;
211        case CUCUL_ANTIALIASING_NONE:
212        case CUCUL_ANTIALIASING_PREFILTER:
213            qq->antialiasing = feature;
214            break;
215
216        case CUCUL_DITHERING:
217            feature = CUCUL_DITHERING_FSTEIN;
218        case CUCUL_DITHERING_NONE:
219        case CUCUL_DITHERING_ORDERED2:
220        case CUCUL_DITHERING_ORDERED4:
221        case CUCUL_DITHERING_ORDERED8:
222        case CUCUL_DITHERING_RANDOM:
223        case CUCUL_DITHERING_FSTEIN:
224            qq->dithering = feature;
225            break;
226
227        case CUCUL_FEATURE_UNKNOWN:
228            break;
229    }
230}
231
232/** \brief Translate a feature value into the feature's name.
233 *
234 *  This function translates a cucul_feature enum into a human-readable
235 *  description string of the associated feature.
236 *
237 *  \param feature The feature value.
238 *  \return A static string containing the feature's name.
239 */
240char const *cucul_get_feature_name(enum cucul_feature feature)
241{
242    switch(feature)
243    {
244        case CUCUL_BACKGROUND_BLACK: return "black background";
245        case CUCUL_BACKGROUND_SOLID: return "solid background";
246
247        case CUCUL_ANTIALIASING_NONE:      return "no antialiasing";
248        case CUCUL_ANTIALIASING_PREFILTER: return "prefilter antialiasing";
249
250        case CUCUL_DITHERING_NONE:     return "no dithering";
251        case CUCUL_DITHERING_ORDERED2: return "2x2 ordered dithering";
252        case CUCUL_DITHERING_ORDERED4: return "4x4 ordered dithering";
253        case CUCUL_DITHERING_ORDERED8: return "8x8 ordered dithering";
254        case CUCUL_DITHERING_RANDOM:   return "random dithering";
255        case CUCUL_DITHERING_FSTEIN:   return "Floyd-Steinberg dithering";
256
257        default: return "unknown";
258    }
259}
260
261/** \brief Uninitialise \e libcucul.
262 *
263 *  This function frees all resources allocated by cucul_init(). After
264 *  cucul_end() has been called, no other \e libcucul functions may be used
265 *  unless a new call to cucul_init() is done.
266 */
267void cucul_end(cucul_t *qq)
268{
269    _cucul_end_bitmap();
270
271    free(qq->empty_line);
272    free(qq->scratch_line);
273
274    free(qq->chars);
275    free(qq->attr);
276
277    if(qq->ansi_buffer)
278        free(qq->ansi_buffer);
279
280    free(qq);
281}
282
283/*
284 * XXX: The following functions are local.
285 */
286
287static void cucul_read_environment(cucul_t * qq)
288{
289    /* FIXME: if strcasecmp isn't available, use strcmp */
290#if defined(HAVE_GETENV) && defined(HAVE_STRCASECMP)
291    char *var;
292#endif
293
294    cucul_set_feature(qq, CUCUL_BACKGROUND);
295    cucul_set_feature(qq, CUCUL_ANTIALIASING);
296    cucul_set_feature(qq, CUCUL_DITHERING);
297
298#if defined(HAVE_GETENV) && defined(HAVE_STRCASECMP)
299    if((var = getenv("CUCUL_BACKGROUND")) && *var)
300    {
301        if(!strcasecmp("black", var))
302            cucul_set_feature(qq, CUCUL_BACKGROUND_BLACK);
303        else if(!strcasecmp("solid", var))
304            cucul_set_feature(qq, CUCUL_BACKGROUND_SOLID);
305    }
306
307    if((var = getenv("CUCUL_ANTIALIASING")) && *var)
308    {
309        if(!strcasecmp("none", var))
310            cucul_set_feature(qq, CUCUL_ANTIALIASING_NONE);
311        else if(!strcasecmp("prefilter", var))
312            cucul_set_feature(qq, CUCUL_ANTIALIASING_PREFILTER);
313    }
314
315    if((var = getenv("CUCUL_DITHERING")) && *var)
316    {
317        if(!strcasecmp("none", var))
318            cucul_set_feature(qq, CUCUL_DITHERING_NONE);
319        else if(!strcasecmp("ordered2", var))
320            cucul_set_feature(qq, CUCUL_DITHERING_ORDERED2);
321        else if(!strcasecmp("ordered4", var))
322            cucul_set_feature(qq, CUCUL_DITHERING_ORDERED4);
323        else if(!strcasecmp("ordered8", var))
324            cucul_set_feature(qq, CUCUL_DITHERING_ORDERED8);
325        else if(!strcasecmp("random", var))
326            cucul_set_feature(qq, CUCUL_DITHERING_RANDOM);
327        else if(!strcasecmp("fstein", var))
328            cucul_set_feature(qq, CUCUL_DITHERING_FSTEIN);
329    }
330#endif
331}
332
333void _cucul_set_size(cucul_t *qq, unsigned int width, unsigned int height)
334{
335    unsigned int x, y, old_width, old_height, new_size, old_size;
336
337    old_width = qq->width;
338    old_height = qq->height;
339    old_size = old_width * old_height;
340
341    qq->width = width;
342    qq->height = height;
343    new_size = width * height;
344
345    /* Step 1: if new area is bigger, resize the memory area now. */
346    if(new_size > old_size)
347    {
348        qq->chars = realloc(qq->chars, new_size * sizeof(uint32_t));
349        qq->attr = realloc(qq->attr, new_size * sizeof(uint8_t));
350    }
351
352    /* Step 2: move line data if necessary. */
353    if(width == old_width)
354    {
355        /* Width did not change, which means we do not need to move data. */
356        ;
357    }
358    else if(width > old_width)
359    {
360        /* New width is bigger than old width, which means we need to
361         * copy lines starting from the bottom of the screen otherwise
362         * we will overwrite information. */
363        for(y = height < old_height ? height : old_height; y--; )
364        {
365            for(x = old_width; x--; )
366            {
367                qq->chars[y * width + x] = qq->chars[y * old_width + x];
368                qq->attr[y * width + x] = qq->attr[y * old_width + x];
369            }
370
371            /* Zero the end of the line */
372            for(x = width - old_width; x--; )
373                qq->chars[y * width + old_width + x] = (uint32_t)' ';
374            memset(qq->attr + y * width + old_width, 0,
375                   width - old_width);
376        }
377    }
378    else
379    {
380        /* New width is smaller. Copy as many lines as possible. Ignore
381         * the first line, it is already in place. */
382        unsigned int lines = height < old_height ? height : old_height;
383
384        for(y = 1; y < lines; y++)
385        {
386            for(x = 0; x < width; x++)
387            {
388                qq->chars[y * width + x] = qq->chars[y * old_width + x];
389                qq->attr[y * width + x] = qq->attr[y * old_width + x];
390            }
391        }
392    }
393
394    /* Step 3: fill the bottom of the new screen if necessary. */
395    if(height > old_height)
396    {
397        /* Zero the bottom of the screen */
398        for(x = (height - old_height) * width; x--; )
399            qq->chars[old_height * width + x] = (uint32_t)' ';
400        memset(qq->attr + old_height * width, 0,
401               (height - old_height) * width);
402    }
403
404    /* Step 4: if new area is smaller, resize memory area now. */
405    if(new_size <= old_size)
406    {
407        qq->chars = realloc(qq->chars, new_size * sizeof(uint32_t));
408        qq->attr = realloc(qq->attr, new_size * sizeof(uint8_t));
409    }
410
411    /* Recompute the scratch line and the empty line */
412    if(width != old_width)
413    {
414        qq->empty_line = realloc(qq->empty_line, width + 1);
415        memset(qq->empty_line, ' ', width);
416        qq->empty_line[width] = '\0';
417
418        qq->scratch_line = realloc(qq->scratch_line, width + 1);
419    }
420}
421
Note: See TracBrowser for help on using the repository browser.