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

Last change on this file since 1088 was 1088, checked in by Sam Hocevar, 17 years ago
  • When resizing a canvas, use the current fg/bg colours to fill holes.
  • Property svn:keywords set to Id
File size: 11.5 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 1088 2006-09-23 00:55:42Z 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 *  This function initialises internal \e libcucul structures and the backend
42 *  that will be used for subsequent graphical operations. It must be the
43 *  first \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 one of the desired canvas coordinates is zero, a default canvas size
47 *  of 80x32 is used instead.
48 *
49 *  If an error occurs, NULL is returned and \b errno is set accordingly:
50 *  - \c ENOMEM Not enough memory for the requested canvas size.
51 *
52 *  \param width The desired canvas width
53 *  \param height The desired canvas height
54 *  \return A libcucul canvas handle upon success, NULL if an error occurred.
55 */
56cucul_canvas_t * cucul_create_canvas(unsigned int width, unsigned int height)
57{
58    cucul_canvas_t *cv = malloc(sizeof(cucul_canvas_t));
59    int ret;
60
61    if(!cv)
62        goto nomem;
63
64    cv->refcount = 0;
65
66    cv->fgcolor = CUCUL_COLOR_LIGHTGRAY;
67    cv->bgcolor = CUCUL_COLOR_BLACK;
68
69    cv->width = cv->height = 0;
70    cv->chars = NULL;
71    cv->attr = NULL;
72
73    cv->frame = 0;
74    cv->framecount = 1;
75    cv->allchars = malloc(sizeof(uint32_t *));
76    if(!cv->allchars)
77    {
78        free(cv);
79        goto nomem;
80    }
81    cv->allattr = malloc(sizeof(uint32_t *));
82    if(!cv->allattr)
83    {
84        free(cv->allchars);
85        free(cv);
86        goto nomem;
87    }
88    cv->allchars[0] = NULL;
89    cv->allattr[0] = NULL;
90
91    /* Initialise to a default size. 80x32 is arbitrary but matches AAlib's
92     * default X11 window. When a graphic driver attaches to us, it can set
93     * a different size. */
94    if(width && height)
95        ret = _cucul_set_canvas_size(cv, width, height);
96    else
97        ret = _cucul_set_canvas_size(cv, 80, 32);
98
99    if(ret < 0)
100    {
101#if defined(HAVE_ERRNO_H)
102        int saved_errno = errno;
103#endif
104        free(cv->allattr);
105        free(cv->allchars);
106        free(cv);
107#if defined(HAVE_ERRNO_H)
108        errno = saved_errno;
109#endif
110        return NULL;
111    }
112
113    /* FIXME: this shouldn't happen here */
114    _cucul_init_dither();
115
116    return cv;
117
118nomem:
119#if defined(HAVE_ERRNO_H)
120    errno = ENOMEM;
121#endif
122    return NULL;
123}
124
125/** \brief Resize a canvas.
126 *
127 *  This function sets the canvas width and height, in character cells.
128 *
129 *  The contents of the canvas are preserved to the extent of the new
130 *  canvas size. Newly allocated character cells at the right and/or at
131 *  the bottom of the canvas are filled with spaces.
132 *
133 *  It is an error to try to resize the canvas if an output driver has
134 *  been attached to the canvas using caca_create_display(). You need to
135 *  remove the output driver using caca_free_display() before you can change
136 *  the  canvas size again. However, the caca output driver can cause a
137 *  canvas resize through user interaction. See the caca_event() documentation
138 *  for more about this.
139 *
140 *  If an error occurs, -1 is returned and \b errno is set accordingly:
141 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
142 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
143 *    happens, the canvas handle becomes invalid and should not be used.
144 *
145 *  \param cv A libcucul canvas
146 *  \param width The desired canvas width
147 *  \param height The desired canvas height
148 *  \return 0 in case of success, -1 if an error occurred.
149 */
150int cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width,
151                                              unsigned int height)
152{
153    if(cv->refcount)
154    {
155#if defined(HAVE_ERRNO_H)
156        errno = EBUSY;
157#endif
158        return -1;
159    }
160
161    return _cucul_set_canvas_size(cv, width, height);
162}
163
164/** \brief Get the canvas width.
165 *
166 *  This function returns the current canvas width, in character cells.
167 *
168 *  This function never fails.
169 *
170 *  \param cv A libcucul canvas
171 *  \return The canvas width.
172 */
173unsigned int cucul_get_canvas_width(cucul_canvas_t *cv)
174{
175    return cv->width;
176}
177
178/** \brief Get the canvas height.
179 *
180 *  This function returns the current canvas height, in character cells.
181 *
182 *  This function never fails.
183 *
184 *  \param cv A libcucul canvas
185 *  \return The canvas height.
186 */
187unsigned int cucul_get_canvas_height(cucul_canvas_t *cv)
188{
189    return cv->height;
190}
191
192/** \brief Translate a colour index into the colour's name.
193 *
194 *  This function translates a cucul_color enum into a human-readable
195 *  description string of the associated colour.
196 *
197 *  This function never fails.
198 *
199 *  \param color The colour value.
200 *  \return A static string containing the colour's name, or \c "unknown" if
201 *  the colour is unknown.
202 */
203char const *cucul_get_color_name(unsigned int color)
204{
205    static char const *color_names[] =
206    {
207        "black",
208        "blue",
209        "green",
210        "cyan",
211        "red",
212        "magenta",
213        "brown",
214        "light gray",
215        "dark gray",
216        "light blue",
217        "light green",
218        "light cyan",
219        "light red",
220        "light magenta",
221        "yellow",
222        "white",
223    };
224
225    if(color < 0 || color > 15)
226        return "unknown";
227
228    return color_names[color];
229}
230
231/** \brief Uninitialise \e libcucul.
232 *
233 *  This function frees all resources allocated by cucul_create_canvas(). After
234 *  cucul_free_canvas() has been called, no other \e libcucul functions may be
235 *  used unless a new call to cucul_create_canvas() is done.
236 *
237 *  If an error occurs, -1 is returned and \b errno is set accordingly:
238 *  - \c EBUSY The canvas is in use by a display driver and cannot be freed.
239 *
240 *  \param cv A libcucul canvas
241 *  \return 0 in case of success, -1 if an error occurred.
242 */
243int cucul_free_canvas(cucul_canvas_t *cv)
244{
245    unsigned int f;
246
247    if(cv->refcount)
248    {
249#if defined(HAVE_ERRNO_H)
250        errno = EBUSY;
251#endif
252        return -1;
253    }
254
255    /* FIXME: this shouldn't be here either (see above) */
256    _cucul_end_dither();
257
258    for(f = 0; f < cv->framecount; f++)
259    {
260        free(cv->allchars[f]);
261        free(cv->allattr[f]);
262    }
263
264    free(cv);
265
266    return 0;
267}
268
269/** \brief Generate a random integer within a range.
270 *
271 *  This function never fails.
272 *
273 *  \param min The lower bound of the integer range.
274 *  \param max The upper bound of the integer range.
275 *  \return A random integer comprised between \p min  and \p max - 1
276 *  (inclusive).
277 */
278int cucul_rand(int min, int max)
279{
280    static int need_init = 1;
281
282    if(need_init)
283    {
284        srand(getpid() + time(NULL));
285        need_init = 0;
286    }
287
288    return min + (int)((1.0 * (max - min)) * rand() / (RAND_MAX + 1.0));
289}
290
291/*
292 * XXX: The following functions are local.
293 */
294
295int _cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width,
296                                               unsigned int height)
297{
298    unsigned int x, y, f, old_width, old_height, new_size, old_size;
299
300    old_width = cv->width;
301    old_height = cv->height;
302    old_size = old_width * old_height;
303
304    cv->width = width;
305    cv->height = height;
306    new_size = width * height;
307
308    /* Step 1: if new area is bigger, resize the memory area now. */
309    if(new_size > old_size)
310    {
311        for(f = 0; f < cv->framecount; f++)
312        {
313            cv->allchars[f] = realloc(cv->allchars[f],
314                                      new_size * sizeof(uint32_t));
315            cv->allattr[f] = realloc(cv->allattr[f],
316                                     new_size * sizeof(uint32_t));
317            if(!cv->allchars[f] || !cv->allattr[f])
318            {
319#if defined(HAVE_ERRNO_H)
320                errno = ENOMEM;
321#endif
322                return -1;
323            }
324        }
325    }
326
327    /* Step 2: move line data if necessary. */
328    if(width == old_width)
329    {
330        /* Width did not change, which means we do not need to move data. */
331        ;
332    }
333    else if(width > old_width)
334    {
335        /* New width is bigger than old width, which means we need to
336         * copy lines starting from the bottom of the screen otherwise
337         * we will overwrite information. */
338        for(f = 0; f < cv->framecount; f++)
339        {
340            uint32_t *chars = cv->allchars[f];
341            uint32_t *attr = cv->allattr[f];
342
343            for(y = height < old_height ? height : old_height; y--; )
344            {
345                uint32_t color = (cv->bgcolor << 16) | cv->fgcolor;
346
347                for(x = old_width; x--; )
348                {
349                    chars[y * width + x] = chars[y * old_width + x];
350                    attr[y * width + x] = attr[y * old_width + x];
351                }
352
353                /* Zero the end of the line */
354                for(x = width - old_width; x--; )
355                {
356                    chars[y * width + old_width + x] = (uint32_t)' ';
357                    attr[y * width + old_width + x] = color;
358                }
359            }
360        }
361    }
362    else
363    {
364        /* New width is smaller. Copy as many lines as possible. Ignore
365         * the first line, it is already in place. */
366        unsigned int lines = height < old_height ? height : old_height;
367
368        for(f = 0; f < cv->framecount; f++)
369        {
370            uint32_t *chars = cv->allchars[f];
371            uint32_t *attr = cv->allattr[f];
372
373            for(y = 1; y < lines; y++)
374            {
375                for(x = 0; x < width; x++)
376                {
377                    chars[y * width + x] = chars[y * old_width + x];
378                    attr[y * width + x] = attr[y * old_width + x];
379                }
380            }
381        }
382    }
383
384    /* Step 3: fill the bottom of the new screen if necessary. */
385    if(height > old_height)
386    {
387        for(f = 0; f < cv->framecount; f++)
388        {
389            uint32_t *chars = cv->allchars[f];
390            uint32_t *attr = cv->allattr[f];
391            uint32_t color = (cv->bgcolor << 16) | cv->fgcolor;
392
393            /* Zero the bottom of the screen */
394            for(x = (height - old_height) * width; x--; )
395            {
396                chars[old_height * width + x] = (uint32_t)' ';
397                attr[old_height * width + x] = color;
398            }
399        }
400    }
401
402    /* Step 4: if new area is smaller, resize memory area now. */
403    if(new_size <= old_size)
404    {
405        for(f = 0; f < cv->framecount; f++)
406        {
407            cv->allchars[f] = realloc(cv->allchars[f],
408                                      new_size * sizeof(uint32_t));
409            cv->allattr[f] = realloc(cv->allattr[f],
410                                     new_size * sizeof(uint32_t));
411            if(!cv->allchars[f] || !cv->allattr[f])
412            {
413#if defined(HAVE_ERRNO_H)
414                errno = ENOMEM;
415#endif
416                return -1;
417            }
418        }
419    }
420
421    /* Reset the current frame shortcut */
422    cv->chars = cv->allchars[cv->frame];
423    cv->attr = cv->allattr[cv->frame];
424
425    return 0;
426}
427
Note: See TracBrowser for help on using the repository browser.