source: libcaca/trunk/src/cucul.c @ 527

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