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

Last change on this file since 1254 was 1254, checked in by Sam Hocevar, 16 years ago
  • Use the LSB of alpha and blue components to encode additional flags such as bold or underline. No higher level support for these yet, but at least everything is consistent.
  • Created cucul_set_attr_ansi() and cucul_set_attr_argb() which are bound to replace cucul_set_color() and cucul_set_truecolor().
  • Property svn:keywords set to Id
File size: 11.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 *  $Id: cucul.c 1254 2006-10-28 23:53:46Z 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#include "common.h"
21
22#if !defined(__KERNEL__)
23#   include <stdio.h>
24#   include <stdlib.h>
25#   include <string.h>
26#   include <time.h>
27#   if defined(HAVE_ERRNO_H)
28#       include <errno.h>
29#   endif
30#   include <sys/types.h>
31#   if defined(HAVE_UNISTD_H)
32#       include <unistd.h>
33#   endif
34#endif
35
36#include "cucul.h"
37#include "cucul_internals.h"
38
39/** \brief Initialise a \e libcucul canvas.
40 *
41 *  Initialise internal \e libcucul structures and the backend that will
42 *  be used for subsequent graphical operations. It must be the first
43 *  \e libcucul function to be called in a function. cucul_free_canvas()
44 *  should be called at the end of the program to free all allocated resources.
45 *
46 *  If an error occurs, NULL is returned and \b errno is set accordingly:
47 *  - \c ENOMEM Not enough memory for the requested canvas size.
48 *
49 *  \param width The desired canvas width
50 *  \param height The desired canvas height
51 *  \return A libcucul canvas handle upon success, NULL if an error occurred.
52 */
53cucul_canvas_t * cucul_create_canvas(unsigned int width, unsigned int height)
54{
55    cucul_canvas_t *cv = malloc(sizeof(cucul_canvas_t));
56
57    if(!cv)
58        goto nomem;
59
60    cv->refcount = 0;
61
62    cv->curattr = (CUCUL_COLOR_DEFAULT << 20) | (CUCUL_COLOR_TRANSPARENT < 4);
63
64    cv->width = cv->height = 0;
65    cv->chars = NULL;
66    cv->attrs = NULL;
67
68    cv->frame = 0;
69    cv->framecount = 1;
70    cv->allchars = malloc(sizeof(uint32_t *));
71    if(!cv->allchars)
72    {
73        free(cv);
74        goto nomem;
75    }
76    cv->allattrs = malloc(sizeof(uint32_t *));
77    if(!cv->allattrs)
78    {
79        free(cv->allchars);
80        free(cv);
81        goto nomem;
82    }
83    cv->allchars[0] = NULL;
84    cv->allattrs[0] = NULL;
85
86    if(_cucul_set_canvas_size(cv, width, height) < 0)
87    {
88#if defined(HAVE_ERRNO_H)
89        int saved_errno = errno;
90#endif
91        free(cv->allattrs);
92        free(cv->allchars);
93        free(cv);
94#if defined(HAVE_ERRNO_H)
95        errno = saved_errno;
96#endif
97        return NULL;
98    }
99
100    /* FIXME: this shouldn't happen here */
101    _cucul_init_dither();
102
103    return cv;
104
105nomem:
106#if defined(HAVE_ERRNO_H)
107    errno = ENOMEM;
108#endif
109    return NULL;
110}
111
112/** \brief Resize a canvas.
113 *
114 *  Set the canvas' width and height, in character cells.
115 *
116 *  The contents of the canvas are preserved to the extent of the new
117 *  canvas size. Newly allocated character cells at the right and/or at
118 *  the bottom of the canvas are filled with spaces.
119 *
120 *  It is an error to try to resize the canvas if an output driver has
121 *  been attached to the canvas using caca_create_display(). You need to
122 *  remove the output driver using caca_free_display() before you can change
123 *  the  canvas size again. However, the caca output driver can cause a
124 *  canvas resize through user interaction. See the caca_event() documentation
125 *  for more about this.
126 *
127 *  If an error occurs, -1 is returned and \b errno is set accordingly:
128 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
129 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
130 *    happens, the canvas handle becomes invalid and should not be used.
131 *
132 *  \param cv A libcucul canvas
133 *  \param width The desired canvas width
134 *  \param height The desired canvas height
135 *  \return 0 in case of success, -1 if an error occurred.
136 */
137int cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width,
138                                              unsigned int height)
139{
140    if(cv->refcount)
141    {
142#if defined(HAVE_ERRNO_H)
143        errno = EBUSY;
144#endif
145        return -1;
146    }
147
148    return _cucul_set_canvas_size(cv, width, height);
149}
150
151/** \brief Get the canvas width.
152 *
153 *  Return the current canvas' width, in character cells.
154 *
155 *  This function never fails.
156 *
157 *  \param cv A libcucul canvas
158 *  \return The canvas width.
159 */
160unsigned int cucul_get_canvas_width(cucul_canvas_t *cv)
161{
162    return cv->width;
163}
164
165/** \brief Get the canvas height.
166 *
167 *  Returns the current canvas' height, in character cells.
168 *
169 *  This function never fails.
170 *
171 *  \param cv A libcucul canvas
172 *  \return The canvas height.
173 */
174unsigned int cucul_get_canvas_height(cucul_canvas_t *cv)
175{
176    return cv->height;
177}
178
179/** \brief Translate a colour index into the colour's name.
180 *
181 *  Translate a cucul_color enum into a human-readable string describing
182 *  the corresponding colour.
183 *
184 *  This function never fails.
185 *
186 *  \param color The colour value.
187 *  \return A static string containing the colour's name, or \c "unknown" if
188 *  the colour is unknown.
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 *  Free all resources allocated by cucul_create_canvas(). After
221 *  this function has been called, no other \e libcucul functions may be
222 *  used unless a new call to cucul_create_canvas() is done.
223 *
224 *  If an error occurs, -1 is returned and \b errno is set accordingly:
225 *  - \c EBUSY The canvas is in use by a display driver and cannot be freed.
226 *
227 *  \param cv A libcucul canvas
228 *  \return 0 in case of success, -1 if an error occurred.
229 */
230int cucul_free_canvas(cucul_canvas_t *cv)
231{
232    unsigned int f;
233
234    if(cv->refcount)
235    {
236#if defined(HAVE_ERRNO_H)
237        errno = EBUSY;
238#endif
239        return -1;
240    }
241
242    /* FIXME: this shouldn't be here either (see above) */
243    _cucul_end_dither();
244
245    for(f = 0; f < cv->framecount; f++)
246    {
247        free(cv->allchars[f]);
248        free(cv->allattrs[f]);
249    }
250
251    free(cv->allchars);
252    free(cv->allattrs);
253    free(cv);
254
255    return 0;
256}
257
258/** \brief Generate a random integer within a range.
259 *
260 *  Generate a random integer within the given range.
261 *
262 *  This function never fails.
263 *
264 *  \param min The lower bound of the integer range.
265 *  \param max The upper bound of the integer range.
266 *  \return A random integer comprised between \p min  and \p max - 1
267 *  (inclusive).
268 */
269int cucul_rand(int min, int max)
270{
271    static int need_init = 1;
272
273    if(need_init)
274    {
275        srand(getpid() + time(NULL));
276        need_init = 0;
277    }
278
279    return min + (int)((1.0 * (max - min)) * rand() / (RAND_MAX + 1.0));
280}
281
282/*
283 * XXX: The following functions are local.
284 */
285
286int _cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width,
287                                               unsigned int height)
288{
289    unsigned int x, y, f, old_width, old_height, new_size, old_size;
290
291    old_width = cv->width;
292    old_height = cv->height;
293    old_size = old_width * old_height;
294
295    cv->width = width;
296    cv->height = height;
297    new_size = width * height;
298
299    /* Step 1: if new area is bigger, resize the memory area now. */
300    if(new_size > old_size)
301    {
302        for(f = 0; f < cv->framecount; f++)
303        {
304            cv->allchars[f] = realloc(cv->allchars[f],
305                                      new_size * sizeof(uint32_t));
306            cv->allattrs[f] = realloc(cv->allattrs[f],
307                                     new_size * sizeof(uint32_t));
308            if(!cv->allchars[f] || !cv->allattrs[f])
309            {
310#if defined(HAVE_ERRNO_H)
311                errno = ENOMEM;
312#endif
313                return -1;
314            }
315        }
316    }
317
318    /* Step 2: move line data if necessary. */
319    if(width == old_width)
320    {
321        /* Width did not change, which means we do not need to move data. */
322        ;
323    }
324    else if(width > old_width)
325    {
326        /* New width is bigger than old width, which means we need to
327         * copy lines starting from the bottom of the screen otherwise
328         * we will overwrite information. */
329        for(f = 0; f < cv->framecount; f++)
330        {
331            uint32_t *chars = cv->allchars[f];
332            uint32_t *attrs = cv->allattrs[f];
333
334            for(y = height < old_height ? height : old_height; y--; )
335            {
336                uint32_t attr = cv->curattr;
337
338                for(x = old_width; x--; )
339                {
340                    chars[y * width + x] = chars[y * old_width + x];
341                    attrs[y * width + x] = attrs[y * old_width + x];
342                }
343
344                /* Zero the end of the line */
345                for(x = width - old_width; x--; )
346                {
347                    chars[y * width + old_width + x] = (uint32_t)' ';
348                    attrs[y * width + old_width + x] = attr;
349                }
350            }
351        }
352    }
353    else
354    {
355        /* New width is smaller. Copy as many lines as possible. Ignore
356         * the first line, it is already in place. */
357        unsigned int lines = height < old_height ? height : old_height;
358
359        for(f = 0; f < cv->framecount; f++)
360        {
361            uint32_t *chars = cv->allchars[f];
362            uint32_t *attrs = cv->allattrs[f];
363
364            for(y = 1; y < lines; y++)
365            {
366                for(x = 0; x < width; x++)
367                {
368                    chars[y * width + x] = chars[y * old_width + x];
369                    attrs[y * width + x] = attrs[y * old_width + x];
370                }
371            }
372        }
373    }
374
375    /* Step 3: fill the bottom of the new screen if necessary. */
376    if(height > old_height)
377    {
378        for(f = 0; f < cv->framecount; f++)
379        {
380            uint32_t *chars = cv->allchars[f];
381            uint32_t *attrs = cv->allattrs[f];
382            uint32_t attr = cv->curattr;
383
384            /* Zero the bottom of the screen */
385            for(x = (height - old_height) * width; x--; )
386            {
387                chars[old_height * width + x] = (uint32_t)' ';
388                attrs[old_height * width + x] = attr;
389            }
390        }
391    }
392
393    /* Step 4: if new area is smaller, resize memory area now. */
394    if(new_size <= old_size)
395    {
396        for(f = 0; f < cv->framecount; f++)
397        {
398            cv->allchars[f] = realloc(cv->allchars[f],
399                                      new_size * sizeof(uint32_t));
400            cv->allattrs[f] = realloc(cv->allattrs[f],
401                                     new_size * sizeof(uint32_t));
402            if(!cv->allchars[f] || !cv->allattrs[f])
403            {
404#if defined(HAVE_ERRNO_H)
405                errno = ENOMEM;
406#endif
407                return -1;
408            }
409        }
410    }
411
412    /* Reset the current frame shortcut */
413    cv->chars = cv->allchars[cv->frame];
414    cv->attrs = cv->allattrs[cv->frame];
415
416    return 0;
417}
418
Note: See TracBrowser for help on using the repository browser.