source: libcaca/trunk/caca/canvas.c @ 3443

Last change on this file since 3443 was 3443, checked in by Sam Hocevar, 11 years ago

Set up the architecture for dirty rectangles.

Dirty rectangles are an upcoming optimisation that will tell the output
drivers which portion of the canvas has been really modified since the
last blit.

  • Property svn:keywords set to Id
File size: 19.6 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: canvas.c 3443 2009-05-13 06:40:48Z sam $
7 *
8 *  This library is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 *  This file contains the main functions used by \e libcaca applications
17 *  to initialise a drawing context.
18 */
19
20#include "config.h"
21
22#if !defined(__KERNEL__)
23#   include <stdio.h>
24#   include <stdlib.h>
25#   include <string.h>
26#   include <time.h>
27#   include <sys/types.h>
28#   if defined(HAVE_UNISTD_H)
29#       include <unistd.h>
30#   endif
31#endif
32
33#include "caca.h"
34#include "caca_internals.h"
35
36static int caca_resize(caca_canvas_t *, int, int);
37
38/** \brief Initialise a \e libcaca canvas.
39 *
40 *  Initialise internal \e libcaca structures and the backend that will
41 *  be used for subsequent graphical operations. It must be the first
42 *  \e libcaca function to be called in a function. caca_free_canvas()
43 *  should be called at the end of the program to free all allocated resources.
44 *
45 *  Both the cursor and the canvas' handle are initialised at the top-left
46 *  corner.
47 *
48 *  If an error occurs, NULL is returned and \b errno is set accordingly:
49 *  - \c EINVAL Specified width or height is invalid.
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 libcaca canvas handle upon success, NULL if an error occurred.
55 */
56caca_canvas_t * caca_create_canvas(int width, int height)
57{
58    caca_canvas_t *cv;
59
60    if(width < 0 || height < 0)
61    {
62        seterrno(EINVAL);
63        return NULL;
64    }
65
66    cv = malloc(sizeof(caca_canvas_t));
67
68    if(!cv)
69        goto nomem;
70
71    cv->refcount = 0;
72    cv->autoinc = 0;
73    cv->resize_callback = NULL;
74    cv->resize_data = NULL;
75
76    cv->frame = 0;
77    cv->framecount = 1;
78    cv->frames = malloc(sizeof(struct caca_frame));
79    if(!cv->frames)
80    {
81        free(cv);
82        goto nomem;
83    }
84
85    cv->frames[0].width = cv->frames[0].height = 0;
86    cv->frames[0].chars = NULL;
87    cv->frames[0].attrs = NULL;
88    cv->frames[0].x = cv->frames[0].y = 0;
89    cv->frames[0].handlex = cv->frames[0].handley = 0;
90    cv->frames[0].curattr = 0;
91    cv->frames[0].name = strdup("frame#00000000");
92    cv->frames[0].dirty_xmin = 0;
93    cv->frames[0].dirty_xmax = -1;
94    cv->frames[0].dirty_ymin = 0;
95    cv->frames[0].dirty_ymax = -1;
96
97    _caca_load_frame_info(cv);
98    caca_set_color_ansi(cv, CACA_DEFAULT, CACA_TRANSPARENT);
99
100    cv->ff = NULL;
101
102    if(caca_resize(cv, width, height) < 0)
103    {
104        int saved_errno = geterrno();
105        free(cv->frames[0].name);
106        free(cv->frames);
107        free(cv);
108        seterrno(saved_errno);
109        return NULL;
110    }
111
112    return cv;
113
114nomem:
115    seterrno(ENOMEM);
116    return NULL;
117}
118
119/** \brief Manage a canvas.
120 *
121 *  Lock a canvas to prevent it from being resized. If non-NULL,
122 *  the \e callback function pointer will be called upon each
123 *  \e caca_set_canvas_size call and if the returned value is zero, the
124 *  canvas resize request will be denied.
125 *
126 *  This function is only useful for display drivers such as the \e libcaca
127 *  library.
128 *
129 *  If an error occurs, -1 is returned and \b errno is set accordingly:
130 *  - \c EBUSY The canvas is already being managed.
131 *
132 *  \param cv A libcaca canvas.
133 *  \param callback An optional callback function pointer.
134 *  \param p The argument to be passed to \e callback.
135 *  \return 0 in case of success, -1 if an error occurred.
136 */
137int caca_manage_canvas(caca_canvas_t *cv, int (*callback)(void *), void *p)
138{
139    if(cv->refcount)
140    {
141        seterrno(EBUSY);
142        return -1;
143    }
144
145    cv->resize_callback = callback;
146    cv->resize_data = p;
147    cv->refcount = 1;
148
149    return 0;
150}
151
152/** \brief unmanage a canvas.
153 *
154 *  unlock a canvas previously locked by caca_manage_canvas(). for safety
155 *  reasons, the callback and callback data arguments must be the same as for
156 *  the caca_manage_canvas() call.
157 *
158 *  this function is only useful for display drivers such as the \e libcaca
159 *  library.
160 *
161 *  if an error occurs, -1 is returned and \b errno is set accordingly:
162 *  - \c einval the canvas is not managed, or the callback arguments do
163 *              not match.
164 *
165 *  \param cv a libcaca canvas.
166 *  \param callback the \e callback argument previously passed to
167 *                  caca_manage_canvas().
168 *  \param p the \e p argument previously passed to caca_manage_canvas().
169 *  \return 0 in case of success, -1 if an error occurred.
170 */
171int caca_unmanage_canvas(caca_canvas_t *cv, int (*callback)(void *), void *p)
172{
173    if(!cv->refcount
174        || cv->resize_callback != callback || cv->resize_data != p)
175    {
176        seterrno(EINVAL);
177        return -1;
178    }
179
180    cv->refcount = 0;
181
182    return 0;
183}
184
185/** \brief Resize a canvas.
186 *
187 *  Set the canvas' width and height, in character cells.
188 *
189 *  The contents of the canvas are preserved to the extent of the new
190 *  canvas size. Newly allocated character cells at the right and/or at
191 *  the bottom of the canvas are filled with spaces.
192 *
193 *  If as a result of the resize the cursor coordinates fall outside the
194 *  new canvas boundaries, they are readjusted. For instance, if the
195 *  current X cursor coordinate is 11 and the requested width is 10, the
196 *  new X cursor coordinate will be 10.
197 *
198 *  It is an error to try to resize the canvas if an output driver has
199 *  been attached to the canvas using caca_create_display(). You need to
200 *  remove the output driver using caca_free_display() before you can change
201 *  the canvas size again. However, the caca output driver can cause a
202 *  canvas resize through user interaction. See the caca_event() documentation
203 *  for more about this.
204 *
205 *  If an error occurs, -1 is returned and \b errno is set accordingly:
206 *  - \c EINVAL Specified width or height is invalid.
207 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
208 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
209 *    happens, the canvas handle becomes invalid and should not be used.
210 *
211 *  \param cv A libcaca canvas.
212 *  \param width The desired canvas width.
213 *  \param height The desired canvas height.
214 *  \return 0 in case of success, -1 if an error occurred.
215 */
216int caca_set_canvas_size(caca_canvas_t *cv, int width, int height)
217{
218    if(width < 0 || height < 0)
219    {
220        seterrno(EINVAL);
221        return -1;
222    }
223
224    if(cv->refcount && cv->resize_callback
225        && !cv->resize_callback(cv->resize_data))
226    {
227        seterrno(EBUSY);
228        return -1;
229    }
230
231    return caca_resize(cv, width, height);
232}
233
234/** \brief Get the canvas width.
235 *
236 *  Return the current canvas' width, in character cells.
237 *
238 *  This function never fails.
239 *
240 *  \param cv A libcaca canvas.
241 *  \return The canvas width.
242 */
243int caca_get_canvas_width(caca_canvas_t const *cv)
244{
245    return cv->width;
246}
247
248/** \brief Get the canvas height.
249 *
250 *  Returns the current canvas' height, in character cells.
251 *
252 *  This function never fails.
253 *
254 *  \param cv A libcaca canvas.
255 *  \return The canvas height.
256 */
257int caca_get_canvas_height(caca_canvas_t const *cv)
258{
259    return cv->height;
260}
261
262/** \brief Get the canvas character array.
263 *
264 *  Return the current canvas' internal character array. The array elements
265 *  consist in native endian 32-bit Unicode values as returned by
266 *  caca_get_char().
267 *
268 *  This function is only useful for display drivers such as the \e libcaca
269 *  library.
270 *
271 *  This function never fails.
272 *
273 *  \param cv A libcaca canvas.
274 *  \return The canvas character array.
275 */
276uint8_t const * caca_get_canvas_chars(caca_canvas_t const *cv)
277{
278    return (uint8_t const *)cv->chars;
279}
280
281/** \brief Get the canvas attribute array.
282 *
283 *  Returns the current canvas' internal attribute array. The array elements
284 *  consist in native endian 32-bit attribute values as returned by
285 *  caca_get_attr().
286 *
287 *  This function is only useful for display drivers such as the \e libcaca
288 *  library.
289 *
290 *  This function never fails.
291 *
292 *  \param cv A libcaca canvas.
293 *  \return The canvas attribute array.
294 */
295uint8_t const * caca_get_canvas_attrs(caca_canvas_t const *cv)
296{
297    return (uint8_t const *)cv->attrs;
298}
299
300/** \brief Get a canvas's dirty rectangle.
301 *
302 *  Get the canvas's dirty rectangle coordinates. The dirty rectangle is
303 *  the smallest area containing all the cells that have changed since it
304 *  was last reset.
305 *
306 *  The dirty rectangle is used internally by display drivers to optimise
307 *  rendering by avoiding to redraw the whole screen. Once the display driver
308 *  has rendered the canvas, it resets the dirty rectangle.
309 *
310 *  Values such that \b xmin > \b xmax or \b ymin > \b ymax indicate that
311 *  the dirty rectangle is empty. It means that the canvas's contents have
312 *  not changed since the dirty rectangle was last reset.
313 *
314 *  FIXME: having only one dirty rectangle instead of a list of rectangles
315 *  is a severe limitation, but the potential gain does not yet look to be
316 *  worth the implementation complexity of a multiple-rectangle scheme.
317 *
318 *  This function never fails.
319 *
320 *  \param cv A libcaca canvas.
321 *  \param xmin A pointer to an integer where the leftmost edge of the
322 *              dirty rectangle will be stored.
323 *  \param xmax A pointer to an integer where the rightmost edge of the
324 *              dirty rectangle will be stored.
325 *  \param ymin A pointer to an integer where the topmost edge of the
326 *              dirty rectangle will be stored.
327 *  \param ymax A pointer to an integer where the bottommost edge of the
328 *              dirty rectangle will be stored.
329 *  \return This function always returns 0.
330 */
331int caca_get_dirty_rectangle(caca_canvas_t *cv, int *xmin, int *xmax,
332                             int *ymin, int *ymax)
333{
334    *xmin = cv->frames[cv->frame].dirty_xmin;
335    *xmax = cv->frames[cv->frame].dirty_xmax;
336    *ymin = cv->frames[cv->frame].dirty_ymin;
337    *ymax = cv->frames[cv->frame].dirty_ymax;
338
339    return 0;
340}
341
342/** \brief Add a dirty rectangle to the canvas's dirty rectangle.
343 *
344 *  Add an invalidating zone to the canvas's dirty rectangle. For more
345 *  information about the dirty rectangle, see caca_get_dirty_rectangle().
346 *
347 *  This function may be useful to force refresh of a given zone of the
348 *  canvas even if the dirty rectangle tracking indicates that it is
349 *  unchanged.
350 *
351 *  Values such that \b xmin > \b xmax or \b ymin > \b ymax indicate that
352 *  the dirty rectangle is empty. They will be silently ignored.
353 *
354 *  This function never fails.
355 *
356 *  \param cv A libcaca canvas.
357 *  \param xmin The leftmost edge of the additional dirty rectangle.
358 *  \param xmax The rightmost edge of the additional dirty rectangle.
359 *  \param ymin The topmost edge of the additional dirty rectangle.
360 *  \param ymax The bottommost edge of the additional dirty rectangle.
361 *  \return This function always returns 0.
362 */
363int caca_add_dirty_rectangle(caca_canvas_t *cv, int xmin, int xmax,
364                             int ymin, int ymax)
365{
366    /* Ignore empty rectangles. */
367    if(xmin > xmax || ymin > ymax)
368        return 0;
369
370    /* Ignore out-of-bounds rectangles. */
371    if(xmax < 0 || xmin >= cv->width || ymax < 0 || ymin >= cv->height)
372        return 0;
373
374    if(xmin < cv->frames[cv->frame].dirty_xmin)
375        cv->frames[cv->frame].dirty_xmin = xmin;
376
377    if(xmax > cv->frames[cv->frame].dirty_xmax)
378        cv->frames[cv->frame].dirty_xmax = xmax;
379
380    if(ymin < cv->frames[cv->frame].dirty_ymin)
381        cv->frames[cv->frame].dirty_ymin = ymin;
382
383    if(ymax > cv->frames[cv->frame].dirty_ymax)
384        cv->frames[cv->frame].dirty_ymax = ymax;
385
386    return 0;
387}
388
389/** \brief Set a canvas's dirty rectangle.
390 *
391 *  Set the canvas's dirty rectangle coordinates. For more information
392 *  about the dirty rectangle, see caca_get_dirty_rectangle().
393 *
394 *  Values such that \b xmin > \b xmax or \b ymin > \b ymax indicate that
395 *  the dirty rectangle is empty.
396 *
397 *  This function never fails.
398 *
399 *  \param cv A libcaca canvas.
400 *  \param xmin The leftmost edge of the desired dirty rectangle.
401 *  \param xmax The rightmost edge of the desired dirty rectangle.
402 *  \param ymin The topmost edge of the desired dirty rectangle.
403 *  \param ymax The bottommost edge of the desired dirty rectangle.
404 *  \return This function always returns 0.
405 */
406int caca_set_dirty_rectangle(caca_canvas_t *cv, int xmin, int xmax,
407                             int ymin, int ymax)
408{
409    /* Normalise values indicating an empty or out-of-bounds rectangle. */
410    if(xmin > xmax || ymin > ymax ||
411        xmax < 0 || xmin >= cv->width || ymax < 0 || ymin >= cv->height)
412    {
413        xmin = cv->width;
414        xmax = -1;
415        ymin = cv->height;
416        ymax = -1;
417    }
418
419    cv->frames[cv->frame].dirty_xmin = xmin;
420    cv->frames[cv->frame].dirty_xmax = xmax;
421    cv->frames[cv->frame].dirty_ymin = ymin;
422    cv->frames[cv->frame].dirty_ymax = ymax;
423
424    return 0;
425}
426
427/** \brief Free a \e libcaca canvas.
428 *
429 *  Free all resources allocated by caca_create_canvas(). The canvas
430 *  pointer becomes invalid and must no longer be used unless a new call
431 *  to caca_create_canvas() is made.
432 *
433 *  If an error occurs, -1 is returned and \b errno is set accordingly:
434 *  - \c EBUSY The canvas is in use by a display driver and cannot be freed.
435 *
436 *  \param cv A libcaca canvas.
437 *  \return 0 in case of success, -1 if an error occurred.
438 */
439int caca_free_canvas(caca_canvas_t *cv)
440{
441    int f;
442
443    if(cv->refcount)
444    {
445        seterrno(EBUSY);
446        return -1;
447    }
448
449    for(f = 0; f < cv->framecount; f++)
450    {
451        free(cv->frames[f].chars);
452        free(cv->frames[f].attrs);
453        free(cv->frames[f].name);
454    }
455
456    caca_canvas_set_figfont(cv, NULL);
457
458    free(cv->frames);
459    free(cv);
460
461    return 0;
462}
463
464/** \brief Generate a random integer within a range.
465 *
466 *  Generate a random integer within the given range.
467 *
468 *  This function never fails.
469 *
470 *  \param min The lower bound of the integer range.
471 *  \param max The upper bound of the integer range.
472 *  \return A random integer comprised between \p min  and \p max - 1
473 *  (inclusive).
474 */
475int caca_rand(int min, int max)
476{
477    static int need_init = 1;
478
479    if(need_init)
480    {
481        srand(getpid() + time(NULL));
482        need_init = 0;
483    }
484
485    return min + (int)((1.0 * (max - min)) * rand() / (RAND_MAX + 1.0));
486}
487
488
489/*
490 * XXX: The following functions are local.
491 */
492
493int caca_resize(caca_canvas_t *cv, int width, int height)
494{
495    int x, y, f, old_width, old_height, new_size, old_size;
496
497    old_width = cv->width;
498    old_height = cv->height;
499    old_size = old_width * old_height;
500
501    _caca_save_frame_info(cv);
502
503    cv->width = width;
504    cv->height = height;
505    new_size = width * height;
506
507    /* Step 1: if new area is bigger, resize the memory area now. */
508    if(new_size > old_size)
509    {
510        for(f = 0; f < cv->framecount; f++)
511        {
512            cv->frames[f].chars = realloc(cv->frames[f].chars,
513                                          new_size * sizeof(uint32_t));
514            cv->frames[f].attrs = realloc(cv->frames[f].attrs,
515                                          new_size * sizeof(uint32_t));
516            if(new_size && (!cv->frames[f].chars || !cv->frames[f].attrs))
517            {
518                seterrno(ENOMEM);
519                return -1;
520            }
521        }
522    }
523
524    /* Step 2: move line data if necessary. */
525    if(width == old_width)
526    {
527        /* Width did not change, which means we do not need to move data. */
528        ;
529    }
530    else if(width > old_width)
531    {
532        /* New width is bigger than old width, which means we need to
533         * copy lines starting from the bottom of the screen otherwise
534         * we will overwrite information. */
535        for(f = 0; f < cv->framecount; f++)
536        {
537            uint32_t *chars = cv->frames[f].chars;
538            uint32_t *attrs = cv->frames[f].attrs;
539
540            for(y = height < old_height ? height : old_height; y--; )
541            {
542                uint32_t attr = cv->frames[f].curattr;
543
544                for(x = old_width; x--; )
545                {
546                    chars[y * width + x] = chars[y * old_width + x];
547                    attrs[y * width + x] = attrs[y * old_width + x];
548                }
549
550                /* Zero the end of the line */
551                for(x = width - old_width; x--; )
552                {
553                    chars[y * width + old_width + x] = (uint32_t)' ';
554                    attrs[y * width + old_width + x] = attr;
555                }
556            }
557        }
558    }
559    else
560    {
561        /* New width is smaller. Copy as many lines as possible. Ignore
562         * the first line, it is already in place. */
563        int lines = height < old_height ? height : old_height;
564
565        for(f = 0; f < cv->framecount; f++)
566        {
567            uint32_t *chars = cv->frames[f].chars;
568            uint32_t *attrs = cv->frames[f].attrs;
569
570            for(y = 1; y < lines; y++)
571            {
572                for(x = 0; x < width; x++)
573                {
574                    chars[y * width + x] = chars[y * old_width + x];
575                    attrs[y * width + x] = attrs[y * old_width + x];
576                }
577            }
578        }
579    }
580
581    /* Step 3: fill the bottom of the new screen if necessary. */
582    if(height > old_height)
583    {
584        for(f = 0; f < cv->framecount; f++)
585        {
586            uint32_t *chars = cv->frames[f].chars;
587            uint32_t *attrs = cv->frames[f].attrs;
588            uint32_t attr = cv->frames[f].curattr;
589
590            /* Zero the bottom of the screen */
591            for(x = (height - old_height) * width; x--; )
592            {
593                chars[old_height * width + x] = (uint32_t)' ';
594                attrs[old_height * width + x] = attr;
595            }
596        }
597    }
598
599    /* Step 4: if new area is smaller, resize memory area now. */
600    if(new_size < old_size)
601    {
602        for(f = 0; f < cv->framecount; f++)
603        {
604            cv->frames[f].chars = realloc(cv->frames[f].chars,
605                                          new_size * sizeof(uint32_t));
606            cv->frames[f].attrs = realloc(cv->frames[f].attrs,
607                                          new_size * sizeof(uint32_t));
608            if(new_size && (!cv->frames[f].chars || !cv->frames[f].attrs))
609            {
610                seterrno(ENOMEM);
611                return -1;
612            }
613        }
614    }
615
616    /* Set new size */
617    for(f = 0; f < cv->framecount; f++)
618    {
619        if(cv->frames[f].x > (int)width)
620            cv->frames[f].x = width;
621        if(cv->frames[f].y > (int)height)
622            cv->frames[f].y = height;
623
624        cv->frames[f].width = width;
625        cv->frames[f].height = height;
626    }
627
628    /* Reset the current frame shortcuts */
629    _caca_load_frame_info(cv);
630
631    return 0;
632}
633
634/*
635 * XXX: The following functions are aliases.
636 */
637
638cucul_canvas_t * cucul_create_canvas(int, int) CACA_ALIAS(caca_create_canvas);
639int cucul_manage_canvas(cucul_canvas_t *, int (*)(void *), void *)
640    CACA_ALIAS(caca_manage_canvas);
641int cucul_unmanage_canvas(cucul_canvas_t *, int (*)(void *), void *)
642    CACA_ALIAS(caca_unmanage_canvas);
643int cucul_set_canvas_size(cucul_canvas_t *, int, int)
644    CACA_ALIAS(caca_set_canvas_size);
645int cucul_get_canvas_width(cucul_canvas_t const *)
646    CACA_ALIAS(caca_get_canvas_width);
647int cucul_get_canvas_height(cucul_canvas_t const *)
648    CACA_ALIAS(caca_get_canvas_height);
649uint8_t const * cucul_get_canvas_chars(cucul_canvas_t const *)
650    CACA_ALIAS(caca_get_canvas_chars);
651uint8_t const * cucul_get_canvas_attrs(cucul_canvas_t const *)
652    CACA_ALIAS(caca_get_canvas_attrs);
653int cucul_free_canvas(cucul_canvas_t *) CACA_ALIAS(caca_free_canvas);
654int cucul_rand(int, int) CACA_ALIAS(caca_rand);
655
Note: See TracBrowser for help on using the repository browser.