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

Last change on this file since 708 was 708, checked in by Sam Hocevar, 15 years ago
  • Added cucul_get_export_list(). Returns a list of known export formats.
  • Property svn:keywords set to Id
File size: 14.7 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 708 2006-04-01 14:36:59Z 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, char const *format)
321{
322    struct cucul_export *ex;
323
324    ex = malloc(sizeof(struct cucul_export));
325
326    if(!strcasecmp("ansi", format))
327        _cucul_get_ansi(qq, ex);
328    else if(!strcasecmp("html", format))
329        _cucul_get_html(qq, ex);
330    else if(!strcasecmp("html3", format))
331        _cucul_get_html3(qq, ex);
332    else if(!strcasecmp("irc", format))
333        _cucul_get_irc(qq, ex);
334    else if(!strcasecmp("ps", format))
335        _cucul_get_ps(qq, ex);
336    else if(!strcasecmp("svg", format))
337        _cucul_get_svg(qq, ex);
338    else
339    {
340        free(ex);
341        return NULL;
342    }
343
344    return ex;
345}
346
347/**
348 * \brief Get available export formats
349 *
350 * Return a list of available export formats. The list is a NULL-terminated
351 * array of strings, interleaving a string containing the internal value for
352 * the export format, to be used with \e cucul_export(), and a string
353 * containing the natural language description for that export format.
354 *
355 * \return An array of strings.
356 */
357char const * const * cucul_get_export_list(void)
358{
359    static char const * const list[] =
360    {
361        "ansi", "ANSI",
362        "html", "HTML",
363        "html3", "backwards-compatible HTML",
364        "irc", "IRC (mIRC colours)",
365        "ps", "PostScript",
366        "svg", "SVG",
367        NULL, NULL
368    };
369
370    return list;
371}
372
373void cucul_free_export(struct cucul_export *ex)
374{
375    free(ex->buffer);
376    free(ex);
377}
378
379/*
380 * XXX: The following functions are local.
381 */
382
383static void cucul_read_environment(cucul_t * qq)
384{
385    /* FIXME: if strcasecmp isn't available, use strcmp */
386#if defined(HAVE_GETENV) && defined(HAVE_STRCASECMP)
387    char *var;
388#endif
389
390    cucul_set_feature(qq, CUCUL_BACKGROUND);
391    cucul_set_feature(qq, CUCUL_ANTIALIASING);
392    cucul_set_feature(qq, CUCUL_DITHERING);
393
394#if defined(HAVE_GETENV) && defined(HAVE_STRCASECMP)
395    if((var = getenv("CUCUL_BACKGROUND")) && *var)
396    {
397        if(!strcasecmp("black", var))
398            cucul_set_feature(qq, CUCUL_BACKGROUND_BLACK);
399        else if(!strcasecmp("solid", var))
400            cucul_set_feature(qq, CUCUL_BACKGROUND_SOLID);
401    }
402
403    if((var = getenv("CUCUL_ANTIALIASING")) && *var)
404    {
405        if(!strcasecmp("none", var))
406            cucul_set_feature(qq, CUCUL_ANTIALIASING_NONE);
407        else if(!strcasecmp("prefilter", var))
408            cucul_set_feature(qq, CUCUL_ANTIALIASING_PREFILTER);
409    }
410
411    if((var = getenv("CUCUL_DITHERING")) && *var)
412    {
413        if(!strcasecmp("none", var))
414            cucul_set_feature(qq, CUCUL_DITHERING_NONE);
415        else if(!strcasecmp("ordered2", var))
416            cucul_set_feature(qq, CUCUL_DITHERING_ORDERED2);
417        else if(!strcasecmp("ordered4", var))
418            cucul_set_feature(qq, CUCUL_DITHERING_ORDERED4);
419        else if(!strcasecmp("ordered8", var))
420            cucul_set_feature(qq, CUCUL_DITHERING_ORDERED8);
421        else if(!strcasecmp("random", var))
422            cucul_set_feature(qq, CUCUL_DITHERING_RANDOM);
423        else if(!strcasecmp("fstein", var))
424            cucul_set_feature(qq, CUCUL_DITHERING_FSTEIN);
425    }
426#endif
427}
428
429void _cucul_set_size(cucul_t *qq, unsigned int width, unsigned int height)
430{
431    unsigned int x, y, old_width, old_height, new_size, old_size;
432
433    old_width = qq->width;
434    old_height = qq->height;
435    old_size = old_width * old_height;
436
437    qq->width = width;
438    qq->height = height;
439    new_size = width * height;
440
441    /* Step 1: if new area is bigger, resize the memory area now. */
442    if(new_size > old_size)
443    {
444        qq->chars = realloc(qq->chars, new_size * sizeof(uint32_t));
445        qq->attr = realloc(qq->attr, new_size * sizeof(uint8_t));
446    }
447
448    /* Step 2: move line data if necessary. */
449    if(width == old_width)
450    {
451        /* Width did not change, which means we do not need to move data. */
452        ;
453    }
454    else if(width > old_width)
455    {
456        /* New width is bigger than old width, which means we need to
457         * copy lines starting from the bottom of the screen otherwise
458         * we will overwrite information. */
459        for(y = height < old_height ? height : old_height; y--; )
460        {
461            for(x = old_width; x--; )
462            {
463                qq->chars[y * width + x] = qq->chars[y * old_width + x];
464                qq->attr[y * width + x] = qq->attr[y * old_width + x];
465            }
466
467            /* Zero the end of the line */
468            for(x = width - old_width; x--; )
469                qq->chars[y * width + old_width + x] = (uint32_t)' ';
470            memset(qq->attr + y * width + old_width, 0,
471                   width - old_width);
472        }
473    }
474    else
475    {
476        /* New width is smaller. Copy as many lines as possible. Ignore
477         * the first line, it is already in place. */
478        unsigned int lines = height < old_height ? height : old_height;
479
480        for(y = 1; y < lines; y++)
481        {
482            for(x = 0; x < width; x++)
483            {
484                qq->chars[y * width + x] = qq->chars[y * old_width + x];
485                qq->attr[y * width + x] = qq->attr[y * old_width + x];
486            }
487        }
488    }
489
490    /* Step 3: fill the bottom of the new screen if necessary. */
491    if(height > old_height)
492    {
493        /* Zero the bottom of the screen */
494        for(x = (height - old_height) * width; x--; )
495            qq->chars[old_height * width + x] = (uint32_t)' ';
496        memset(qq->attr + old_height * width, 0,
497               (height - old_height) * width);
498    }
499
500    /* Step 4: if new area is smaller, resize memory area now. */
501    if(new_size <= old_size)
502    {
503        qq->chars = realloc(qq->chars, new_size * sizeof(uint32_t));
504        qq->attr = realloc(qq->attr, new_size * sizeof(uint8_t));
505    }
506
507    /* Recompute the scratch line and the empty line */
508    if(width != old_width)
509    {
510        qq->empty_line = realloc(qq->empty_line, width + 1);
511        memset(qq->empty_line, ' ', width);
512        qq->empty_line[width] = '\0';
513
514        qq->scratch_line = realloc(qq->scratch_line, width + 1);
515    }
516}
517
Note: See TracBrowser for help on using the repository browser.