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

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