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

Last change on this file since 777 was 777, checked in by Sam Hocevar, 15 years ago
  • Replaced "struct cucul_*" and "struct caca_*" types with opaque typedefs such as cucul_dither_t instead of struct cucul_dither.
  • Made cucul_buffer_t an opaque structure and implemented the two getters cucul_get_buffer_data() and cucul_get_buffer_size().
  • Documented all missing functions and function parameters.
  • Property svn:keywords set to Id
File size: 12.3 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 *  $Id: cucul.c 777 2006-04-16 18:28:47Z sam $
7 *
8 *  This library is free software; you can redistribute it and/or
9 *  modify it under the terms of the Do What The Fuck You Want To
10 *  Public License, Version 2, as published by Sam Hocevar. See
11 *  http://sam.zoy.org/wtfpl/COPYING for more details.
12 */
13
14/*
15 *  This file contains the main functions used by \e libcucul applications
16 *  to initialise a drawing context.
17 */
18
19#include "config.h"
20
21#if !defined(__KERNEL__)
22#   include <stdio.h>
23#   include <stdlib.h>
24#   include <string.h>
25#endif
26
27#include "cucul.h"
28#include "cucul_internals.h"
29
30/** \brief Initialise a \e libcucul canvas.
31 *
32 *  This function initialises internal \e libcucul structures and the backend
33 *  that will be used for subsequent graphical operations. It must be the
34 *  first \e libcucul function to be called in a function. cucul_free() should
35 *  be called at the end of the program to free all allocated resources.
36 *
37 *  If one of the desired canvas coordinates is zero, a default canvas size
38 *  of 80x32 is used instead.
39 *
40 *  \param width The desired canvas width
41 *  \param height The desired canvas height
42 *  \return A libcucul canvas handle upon success, NULL if an error occurred.
43 */
44cucul_t * cucul_create(unsigned int width, unsigned int height)
45{
46    cucul_t *qq = malloc(sizeof(cucul_t));
47
48    qq->refcount = 0;
49
50    qq->fgcolor = CUCUL_COLOR_LIGHTGRAY;
51    qq->bgcolor = CUCUL_COLOR_BLACK;
52
53    qq->width = qq->height = 0;
54    qq->chars = NULL;
55    qq->attr = NULL;
56    qq->empty_line = qq->scratch_line = NULL;
57
58    /* Initialise to a default size. 80x32 is arbitrary but matches AAlib's
59     * default X11 window. When a graphic driver attaches to us, it can set
60     * a different size. */
61    if(width && height)
62        _cucul_set_size(qq, width, height);
63    else
64        _cucul_set_size(qq, 80, 32);
65
66    if(_cucul_init_dither())
67    {
68        free(qq);
69        return NULL;
70    }
71
72    return qq;
73}
74
75/** \brief Load a memory area into a canvas.
76 *
77 *  This function loads a memory area containing an exported canvas into
78 *  a new \e libcucul canvas.
79 *
80 *  \param data The memory area to be loaded into a canvas.
81 *  \param size The length of the memory area.
82 *  \return A libcucul canvas, or NULL in case of error.
83 */
84cucul_t *cucul_load(void *data, unsigned int size)
85{
86    cucul_t *qq;
87    uint8_t *buf = (uint8_t *)data;
88    unsigned int width, height, n;
89
90    if(size < 12)
91        return NULL;
92
93    if(buf[0] != 'C' || buf[1] != 'A' || buf[2] != 'C' || buf[3] != 'A')
94        return NULL;
95
96    width = ((uint32_t)buf[4] << 24) | ((uint32_t)buf[5] << 16)
97          | ((uint32_t)buf[6] << 8) | (uint32_t)buf[7];
98    height = ((uint32_t)buf[8] << 24) | ((uint32_t)buf[9] << 16)
99           | ((uint32_t)buf[10] << 8) | (uint32_t)buf[11];
100
101    if(!width || !height)
102        return NULL;
103
104    if(size != 12 + width * height * 8 + 4)
105        return NULL;
106
107    if(buf[size - 4] != 'A' || buf[size - 3] != 'C'
108        || buf[size - 2] != 'A' || buf[size - 1] != 'C')
109        return NULL;
110
111    qq = cucul_create(width, height);
112
113    if(!qq)
114        return NULL;
115
116    for(n = height * width; n--; )
117    {
118        qq->chars[n] = ((uint32_t)buf[12 + 8 * n] << 24)
119                     | ((uint32_t)buf[13 + 8 * n] << 16)
120                     | ((uint32_t)buf[14 + 8 * n] << 8)
121                     | (uint32_t)buf[15 + 8 * n];
122        qq->attr[n] = ((uint32_t)buf[16 + 8 * n] << 24)
123                    | ((uint32_t)buf[17 + 8 * n] << 16)
124                    | ((uint32_t)buf[18 + 8 * n] << 8)
125                    | (uint32_t)buf[19 + 8 * n];
126    }
127
128    return qq;
129}
130
131/** \brief Resize a canvas.
132 *
133 *  This function sets the canvas width and height, in character cells.
134 *
135 *  The contents of the canvas are preserved to the extent of the new
136 *  canvas size. Newly allocated character cells at the right and/or at
137 *  the bottom of the canvas are filled with spaces.
138 *
139 *  It is an error to try to resize the canvas if an output driver has
140 *  been attached to the canvas using caca_attach(). You need to remove
141 *  the output driver using caca_detach() before you can change the
142 *  canvas size again. However, the caca output driver can cause a canvas
143 *  resize through user interaction. See the caca_event() documentation
144 *  for more about this.
145 *
146 *  \param qq A libcucul canvas
147 *  \param width The desired canvas width
148 *  \param height The desired canvas height
149 */
150void cucul_set_size(cucul_t *qq, unsigned int width, unsigned int height)
151{
152    if(qq->refcount)
153        return;
154
155    _cucul_set_size(qq, width, height);
156}
157
158/** \brief Get the canvas width.
159 *
160 *  This function returns the current canvas width, in character cells.
161 *
162 *  \param qq A libcucul canvas
163 *  \return The canvas width.
164 */
165unsigned int cucul_get_width(cucul_t *qq)
166{
167    return qq->width;
168}
169
170/** \brief Get the canvas height.
171 *
172 *  This function returns the current canvas height, in character cells.
173 *
174 *  \param qq A libcucul canvas
175 *  \return The canvas height.
176 */
177unsigned int cucul_get_height(cucul_t *qq)
178{
179    return qq->height;
180}
181
182/** \brief Translate a colour index into the colour's name.
183 *
184 *  This function translates a cucul_color enum into a human-readable
185 *  description string of the associated colour.
186 *
187 *  \param color The colour value.
188 *  \return A static string containing the colour's name.
189 */
190char const *cucul_get_color_name(unsigned int color)
191{
192    static char const *color_names[] =
193    {
194        "black",
195        "blue",
196        "green",
197        "cyan",
198        "red",
199        "magenta",
200        "brown",
201        "light gray",
202        "dark gray",
203        "light blue",
204        "light green",
205        "light cyan",
206        "light red",
207        "light magenta",
208        "yellow",
209        "white",
210    };
211
212    if(color < 0 || color > 15)
213        return "unknown";
214
215    return color_names[color];
216}
217
218/** \brief Uninitialise \e libcucul.
219 *
220 *  This function frees all resources allocated by cucul_create(). After
221 *  cucul_free() has been called, no other \e libcucul functions may be used
222 *  unless a new call to cucul_create() is done.
223 *
224 *  \param qq A libcucul canvas
225 */
226void cucul_free(cucul_t *qq)
227{
228    _cucul_end_dither();
229
230    free(qq->empty_line);
231    free(qq->scratch_line);
232
233    free(qq->chars);
234    free(qq->attr);
235
236    free(qq);
237}
238
239/** \brief Export a canvas into a foreign format.
240 *
241 *  This function exports a libcucul canvas into various foreign formats such
242 *  as ANSI art, HTML, IRC colours, etc. One should use cucul_get_buffer_data()
243 *  and cucul_get_buffer_size() to access the buffer contents. The allocated
244 *  data is valid until cucul_free_buffer() is called.
245 *
246 *  Valid values for \e format are:
247 *
248 *  \li \e "ansi": export ANSI art (CP437 charset with ANSI colour codes).
249 *
250 *  \li \e "html": export an HTML page with CSS information.
251 *
252 *  \li \e "html3": export an HTML table that should be compatible with
253 *      most navigators, including textmode ones.
254 *
255 *  \li \e "irc": export UTF-8 text with mIRC colour codes.
256 *
257 *  \li \e "ps": export a PostScript document.
258 *
259 *  \li \e "svg": export an SVG document.
260 *
261 *  \param qq A libcucul canvas
262 *  \param format A string describing the requested output format.
263 */
264cucul_buffer_t * cucul_create_export(cucul_t *qq, char const *format)
265{
266    cucul_buffer_t *ex;
267
268    ex = malloc(sizeof(cucul_buffer_t));
269
270    if(!strcasecmp("ansi", format))
271        _cucul_get_ansi(qq, ex);
272    else if(!strcasecmp("html", format))
273        _cucul_get_html(qq, ex);
274    else if(!strcasecmp("html3", format))
275        _cucul_get_html3(qq, ex);
276    else if(!strcasecmp("irc", format))
277        _cucul_get_irc(qq, ex);
278    else if(!strcasecmp("ps", format))
279        _cucul_get_ps(qq, ex);
280    else if(!strcasecmp("svg", format))
281        _cucul_get_svg(qq, ex);
282    else
283    {
284        free(ex);
285        return NULL;
286    }
287
288    return ex;
289}
290
291/** \brief Get available export formats
292 *
293 *  Return a list of available export formats. The list is a NULL-terminated
294 *  array of strings, interleaving a string containing the internal value for
295 *  the export format, to be used with \e cucul_create_export(), and a string
296 *  containing the natural language description for that export format.
297 *
298 *  \return An array of strings.
299 */
300char const * const * cucul_get_export_list(void)
301{
302    static char const * const list[] =
303    {
304        "ansi", "ANSI",
305        "html", "HTML",
306        "html3", "backwards-compatible HTML",
307        "irc", "IRC (mIRC colours)",
308        "ps", "PostScript",
309        "svg", "SVG",
310        NULL, NULL
311    };
312
313    return list;
314}
315
316/** \brief Get the buffer size.
317 *
318 *  This function returns the length (in bytes) of the memory area stored
319 *  in the given \e libcucul buffer.
320 *
321 *  \param buf A \e libcucul buffer
322 *  \return The buffer data length.
323 */
324unsigned long int cucul_get_buffer_size(cucul_buffer_t *buf)
325{
326    return buf->size;
327}
328
329/** \brief Get the buffer data.
330 *
331 *  This function returns a pointer to the memory area stored in the given
332 *  \e libcucul buffer.
333 *
334 *  \param buf A \e libcucul buffer
335 *  \return A pointer to the buffer memory area.
336 */
337void * cucul_get_buffer_data(cucul_buffer_t *buf)
338{
339    return buf->data;
340}
341
342/** \brief Free a buffer.
343 *
344 *  This function frees the structures associated with the given
345 *  \e libcucul buffer.
346 *
347 *  \param buf A \e libcucul buffer
348 */
349void cucul_free_buffer(cucul_buffer_t *buf)
350{
351    free(buf->data);
352    free(buf);
353}
354
355/*
356 * XXX: The following functions are local.
357 */
358
359void _cucul_set_size(cucul_t *qq, unsigned int width, unsigned int height)
360{
361    unsigned int x, y, old_width, old_height, new_size, old_size;
362
363    old_width = qq->width;
364    old_height = qq->height;
365    old_size = old_width * old_height;
366
367    qq->width = width;
368    qq->height = height;
369    new_size = width * height;
370
371    /* Step 1: if new area is bigger, resize the memory area now. */
372    if(new_size > old_size)
373    {
374        qq->chars = realloc(qq->chars, new_size * sizeof(uint32_t));
375        qq->attr = realloc(qq->attr, new_size * sizeof(uint32_t));
376    }
377
378    /* Step 2: move line data if necessary. */
379    if(width == old_width)
380    {
381        /* Width did not change, which means we do not need to move data. */
382        ;
383    }
384    else if(width > old_width)
385    {
386        /* New width is bigger than old width, which means we need to
387         * copy lines starting from the bottom of the screen otherwise
388         * we will overwrite information. */
389        for(y = height < old_height ? height : old_height; y--; )
390        {
391            for(x = old_width; x--; )
392            {
393                qq->chars[y * width + x] = qq->chars[y * old_width + x];
394                qq->attr[y * width + x] = qq->attr[y * old_width + x];
395            }
396
397            /* Zero the end of the line */
398            for(x = width - old_width; x--; )
399                qq->chars[y * width + old_width + x] = (uint32_t)' ';
400            memset(qq->attr + y * width + old_width, 0,
401                   (width - old_width) * 4);
402        }
403    }
404    else
405    {
406        /* New width is smaller. Copy as many lines as possible. Ignore
407         * the first line, it is already in place. */
408        unsigned int lines = height < old_height ? height : old_height;
409
410        for(y = 1; y < lines; y++)
411        {
412            for(x = 0; x < width; x++)
413            {
414                qq->chars[y * width + x] = qq->chars[y * old_width + x];
415                qq->attr[y * width + x] = qq->attr[y * old_width + x];
416            }
417        }
418    }
419
420    /* Step 3: fill the bottom of the new screen if necessary. */
421    if(height > old_height)
422    {
423        /* Zero the bottom of the screen */
424        for(x = (height - old_height) * width; x--; )
425            qq->chars[old_height * width + x] = (uint32_t)' ';
426        memset(qq->attr + old_height * width, 0,
427               (height - old_height) * width * 4);
428    }
429
430    /* Step 4: if new area is smaller, resize memory area now. */
431    if(new_size <= old_size)
432    {
433        qq->chars = realloc(qq->chars, new_size * sizeof(uint32_t));
434        qq->attr = realloc(qq->attr, new_size * sizeof(uint32_t));
435    }
436
437    /* Recompute the scratch line and the empty line */
438    if(width != old_width)
439    {
440        qq->empty_line = realloc(qq->empty_line, width + 1);
441        memset(qq->empty_line, ' ', width);
442        qq->empty_line[width] = '\0';
443
444        qq->scratch_line = realloc(qq->scratch_line, width + 1);
445    }
446}
447
Note: See TracBrowser for help on using the repository browser.