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

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