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

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