source: libcaca/trunk/cucul/canvas.c @ 1346

Last change on this file since 1346 was 1346, checked in by Sam Hocevar, 14 years ago
  • Made cucul_blit() handle-aware. Added a blit test for that.
  • Property svn:keywords set to Id
File size: 13.8 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: canvas.c 1346 2006-11-11 15:38:40Z 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 various canvas handling functions such as character
16 *  and string drawing.
17 */
18
19#include "config.h"
20#include "common.h"
21
22#if !defined(__KERNEL__)
23#   include <stdio.h> /* BUFSIZ */
24#   include <string.h>
25#   include <stdlib.h>
26#   include <stdarg.h>
27#   if defined(HAVE_ERRNO_H)
28#       include <errno.h>
29#   endif
30#   if defined(HAVE_UNISTD_H)
31#       include <unistd.h>
32#   endif
33#   if defined(HAVE_SIGNAL_H)
34#       include <signal.h>
35#   endif
36#   if defined(HAVE_SYS_IOCTL_H)
37#       include <sys/ioctl.h>
38#   endif
39#endif
40
41#include "cucul.h"
42#include "cucul_internals.h"
43
44/** \brief Set cursor position.
45 *
46 *  Put the cursor at the given coordinates. Functions making use of the
47 *  cursor will use the new values. Setting the cursor position outside the
48 *  canvas is legal but the cursor will not be shown.
49 *
50 *  This function never fails.
51 *
52 *  \param cv A handle to the libcucul canvas.
53 *  \param x X cursor coordinate.
54 *  \param y Y cursor coordinate.
55 *  \return This function always returns 0.
56 */
57int cucul_gotoxy(cucul_canvas_t *cv, int x, int y)
58{
59    cv->frames[cv->frame].x = x;
60    cv->frames[cv->frame].y = y;
61
62    return 0;
63}
64
65/** \brief Get X cursor position.
66 *
67 *  Retrieve the X coordinate of the cursor's position.
68 *
69 *  This function never fails.
70 *
71 *  \param cv A handle to the libcucul canvas.
72 *  \return The cursor's X coordinate.
73 */
74int cucul_get_cursor_x(cucul_canvas_t *cv)
75{
76    return cv->frames[cv->frame].x;
77}
78
79/** \brief Get Y cursor position.
80 *
81 *  Retrieve the Y coordinate of the cursor's position.
82 *
83 *  This function never fails.
84 *
85 *  \param cv A handle to the libcucul canvas.
86 *  \return The cursor's Y coordinate.
87 */
88int cucul_get_cursor_y(cucul_canvas_t *cv)
89{
90    return cv->frames[cv->frame].y;
91}
92
93/** \brief Print an ASCII or Unicode character.
94 *
95 *  Print an ASCII or Unicode character at the given coordinates, using
96 *  the default foreground and background colour values.
97 *
98 *  If the coordinates are outside the canvas boundaries, nothing is printed.
99 *  If a fullwidth Unicode character gets overwritten, its remaining visible
100 *  parts are replaced with spaces. If the canvas' boundaries would split the
101 *  fullwidth character in two, a space is printed instead.
102 *
103 *  The behaviour when printing non-printable characters or invalid UTF-32
104 *  characters is undefined. To print a sequence of bytes forming an UTF-8
105 *  character instead of an UTF-32 character, use the cucul_putstr() function.
106 *
107 *  This function never fails.
108 *
109 *  \param cv A handle to the libcucul canvas.
110 *  \param x X coordinate.
111 *  \param y Y coordinate.
112 *  \param ch The character to print.
113 *  \return This function always returns 0.
114 */
115int cucul_putchar(cucul_canvas_t *cv, int x, int y, unsigned long int ch)
116{
117    uint32_t *curchar, *curattr, attr;
118    int fullwidth;
119
120    if(x >= (int)cv->width || y < 0 || y >= (int)cv->height)
121        return 0;
122
123    if(ch == CUCUL_MAGIC_FULLWIDTH)
124        return 0;
125
126    fullwidth = cucul_utf32_is_fullwidth(ch);
127
128    if(x == -1 && fullwidth)
129    {
130        x = 0;
131        ch = ' ';
132        fullwidth = 0;
133    }
134    else if(x < 0)
135        return 0;
136
137    curchar = cv->chars + x + y * cv->width;
138    curattr = cv->attrs + x + y * cv->width;
139    attr = cv->curattr;
140
141    /* When overwriting the right part of a fullwidth character,
142     * replace its left part with a space. */
143    if(x && curchar[0] == CUCUL_MAGIC_FULLWIDTH)
144        curchar[-1] = ' ';
145
146    if(fullwidth)
147    {
148        if(x + 1 == (int)cv->width)
149            ch = ' ';
150        else
151        {
152            /* When overwriting the left part of a fullwidth character,
153             * replace its right part with a space. */
154            if(x + 2 < (int)cv->width && curchar[2] == CUCUL_MAGIC_FULLWIDTH)
155                curchar[2] = ' ';
156
157            curchar[1] = CUCUL_MAGIC_FULLWIDTH;
158            curattr[1] = attr;
159        }
160    }
161    else
162    {
163        /* When overwriting the left part of a fullwidth character,
164         * replace its right part with a space. */
165        if(x + 1 != (int)cv->width && curchar[1] == CUCUL_MAGIC_FULLWIDTH)
166            curchar[1] = ' ';
167    }
168
169    curchar[0] = ch;
170    curattr[0] = attr;
171
172    return 0;
173}
174
175/** \brief Get the Unicode character at the given coordinates.
176 *
177 *  Get the ASCII or Unicode value of the character at the given
178 *  coordinates. If the value is less or equal to 127 (0x7f),
179 *  the character can be printed as ASCII. Otherise, it must be handled
180 *  as a UTF-32 value.
181 *
182 *  If the coordinates are outside the canvas boundaries, a space (0x20)
183 *  is returned.
184 *
185 *  A special exception is when CUCUL_MAGIC_FULLWIDTH is returned. This
186 *  value is guaranteed not to be a valid Unicode character, and indicates
187 *  that the character at the left of the requested one is a fullwidth
188 *  character.
189 *
190 *  This function never fails.
191 *
192 *  \param cv A handle to the libcucul canvas.
193 *  \param x X coordinate.
194 *  \param y Y coordinate.
195 *  \return This function always returns 0.
196 */
197unsigned long int cucul_getchar(cucul_canvas_t *cv, int x, int y)
198{
199    if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
200        return ' ';
201
202    return (unsigned long int)cv->chars[x + y * cv->width];
203}
204
205/** \brief Print a string.
206 *
207 *  Print an UTF-8 string at the given coordinates, using the default
208 *  foreground and background values. The coordinates may be outside the
209 *  canvas boundaries (eg. a negative Y coordinate) and the string will
210 *  be cropped accordingly if it is too long.
211 *
212 *  See cucul_putchar() for more information on how fullwidth characters
213 *  are handled when overwriting each other or at the canvas' boundaries.
214 *
215 *  This function never fails.
216 *
217 *  \param cv A handle to the libcucul canvas.
218 *  \param x X coordinate.
219 *  \param y Y coordinate.
220 *  \param s The string to print.
221 *  \return This function always returns 0.
222 */
223int cucul_putstr(cucul_canvas_t *cv, int x, int y, char const *s)
224{
225    unsigned int read;
226
227    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
228        return 0;
229
230    while(*s && x < -1)
231    {
232        x += cucul_utf32_is_fullwidth(cucul_utf8_to_utf32(s, &read)) ? 2 : 1;
233        s += read;
234    }
235
236    while(*s && x < (int)cv->width)
237    {
238        uint32_t ch = cucul_utf8_to_utf32(s, &read);
239        cucul_putchar(cv, x, y, ch);
240        x += cucul_utf32_is_fullwidth(ch) ? 2 : 1;
241        s += read;
242    }
243
244    return 0;
245}
246
247/** \brief Print a formated string.
248 *
249 *  Format a string at the given coordinates, using the default foreground
250 *  and background values. The coordinates may be outside the canvas
251 *  boundaries (eg. a negative Y coordinate) and the string will be cropped
252 *  accordingly if it is too long. The syntax of the format string is the
253 *  same as for the C printf() function.
254 *
255 *  This function never fails.
256 *
257 *  \param cv A handle to the libcucul canvas.
258 *  \param x X coordinate.
259 *  \param y Y coordinate.
260 *  \param format The format string to print.
261 *  \param ... Arguments to the format string.
262 *  \return This function always returns 0.
263 */
264int cucul_printf(cucul_canvas_t *cv, int x, int y, char const *format, ...)
265{
266    char tmp[BUFSIZ];
267    char *buf = tmp;
268    va_list args;
269
270    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
271        return 0;
272
273    if(cv->width - x + 1 > BUFSIZ)
274        buf = malloc(cv->width - x + 1);
275
276    va_start(args, format);
277#if defined(HAVE_VSNPRINTF)
278    vsnprintf(buf, cv->width - x + 1, format, args);
279#else
280    vsprintf(buf, format, args);
281#endif
282    buf[cv->width - x] = '\0';
283    va_end(args);
284
285    cucul_putstr(cv, x, y, buf);
286
287    if(buf != tmp)
288        free(buf);
289
290    return 0;
291}
292
293/** \brief Clear the canvas.
294 *
295 *  Clear the canvas using the current foreground and background colours.
296 *
297 *  This function never fails.
298 *
299 *  \param cv The canvas to clear.
300 *  \return This function always returns 0.
301 */
302int cucul_clear_canvas(cucul_canvas_t *cv)
303{
304    uint32_t attr = cv->curattr;
305    unsigned int n;
306
307    for(n = cv->width * cv->height; n--; )
308    {
309        cv->chars[n] = (uint32_t)' ';
310        cv->attrs[n] = attr;
311    }
312
313    return 0;
314}
315
316/** \brief Set cursor handle.
317 *
318 *  Set the canvas' handle. Blitting functions will use the handle value
319 *  to put the canvas at the proper coordinates.
320 *
321 *  This function never fails.
322 *
323 *  \param cv A handle to the libcucul canvas.
324 *  \param x X handle coordinate.
325 *  \param y Y handle coordinate.
326 *  \return This function always returns 0.
327 */
328int cucul_set_canvas_handle(cucul_canvas_t *cv, int x, int y)
329{
330    cv->frames[cv->frame].handlex = x;
331    cv->frames[cv->frame].handley = y;
332
333    return 0;
334}
335
336/** \brief Get X handle position.
337 *
338 *  Retrieve the X coordinate of the canvas' handle.
339 *
340 *  This function never fails.
341 *
342 *  \param cv A handle to the libcucul canvas.
343 *  \return The canvas' handle's X coordinate.
344 */
345int cucul_get_canvas_handle_x(cucul_canvas_t *cv)
346{
347    return cv->frames[cv->frame].handlex;
348}
349
350/** \brief Get Y handle position.
351 *
352 *  Retrieve the Y coordinate of the canvas' handle.
353 *
354 *  This function never fails.
355 *
356 *  \param cv A handle to the libcucul canvas.
357 *  \return The canvas' handle's Y coordinate.
358 */
359int cucul_get_canvas_handle_y(cucul_canvas_t *cv)
360{
361    return cv->frames[cv->frame].handley;
362}
363
364/** \brief Blit a canvas onto another one.
365 *
366 *  Blit a canvas onto another one at the given coordinates.
367 *  An optional mask canvas can be used.
368 *
369 *  If an error occurs, -1 is returned and \b errno is set accordingly:
370 *  - \c EINVAL A mask was specified but the mask size and source canvas
371 *    size do not match.
372 *
373 *  \param dst The destination canvas.
374 *  \param x X coordinate.
375 *  \param y Y coordinate.
376 *  \param src The source canvas.
377 *  \param mask The mask canvas.
378 *  \return 0 in case of success, -1 if an error occurred.
379 */
380int cucul_blit(cucul_canvas_t *dst, int x, int y,
381               cucul_canvas_t const *src, cucul_canvas_t const *mask)
382{
383    int i, j, starti, startj, endi, endj;
384
385    if(mask && (src->width != mask->width || src->height != mask->height))
386    {
387#if defined(HAVE_ERRNO_H)
388        errno = EINVAL;
389#endif
390        return -1;
391    }
392
393    x -= src->frames[src->frame].handlex;
394    y -= src->frames[src->frame].handley;
395
396    starti = x < 0 ? -x : 0;
397    startj = y < 0 ? -y : 0;
398    endi = (x + src->width >= dst->width) ? dst->width - x : src->width;
399    endj = (y + src->height >= dst->height) ? dst->height - y : src->height;
400
401    if((unsigned int)starti > src->width || (unsigned int)startj > src->height
402        || starti >= endi || startj >= endj)
403        return 0;
404
405    for(j = startj; j < endj; j++)
406    {
407        unsigned int dstix = (j + y) * dst->width + starti + x;
408        unsigned int srcix = j * src->width + starti;
409        int stride = endi - starti;
410
411        /* FIXME: we are ignoring the mask here */
412        if((starti + x) && dst->chars[dstix] == CUCUL_MAGIC_FULLWIDTH)
413            dst->chars[dstix - 1] = ' ';
414
415        if((unsigned int)(endi + x) < dst->width
416                && dst->chars[dstix + stride] == CUCUL_MAGIC_FULLWIDTH)
417            dst->chars[dstix + stride] = ' ';
418
419        if(mask)
420        {
421            for(i = 0; i < stride; i++)
422            {
423                if(mask->chars[srcix + i] == (uint32_t)' ')
424                    continue;
425
426                dst->chars[dstix + i] = src->chars[srcix + i];
427                dst->attrs[dstix + i] = src->attrs[srcix + i];
428            }
429        }
430        else
431        {
432            memcpy(dst->chars + dstix, src->chars + srcix, stride * 4);
433            memcpy(dst->attrs + dstix, src->attrs + srcix, stride * 4);
434        }
435
436        /* Fix split fullwidth chars */
437        if(src->chars[srcix] == CUCUL_MAGIC_FULLWIDTH)
438            dst->chars[dstix] = ' ';
439
440        if((unsigned int)endi < src->width
441                && src->chars[endi] == CUCUL_MAGIC_FULLWIDTH)
442            dst->chars[dstix + stride - 1] = ' ';
443    }
444
445    return 0;
446}
447
448/** \brief Set a canvas' new boundaries.
449 *
450 *  Set new boundaries for a canvas. This function can be used to crop a
451 *  canvas, to expand it or for combinations of both actions. All frames
452 *  are affected by this function.
453 *
454 *  If an error occurs, -1 is returned and \b errno is set accordingly:
455 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
456 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
457 *    happens, the canvas handle becomes invalid and should not be used.
458 *
459 *  \param cv The canvas to crop.
460 *  \param x X coordinate of the top-left corner.
461 *  \param y Y coordinate of the top-left corner.
462 *  \param w The width of the cropped area.
463 *  \param h The height of the cropped area.
464 *  \return 0 in case of success, -1 if an error occurred.
465 */
466int cucul_set_canvas_boundaries(cucul_canvas_t *cv, int x, int y,
467                                unsigned int w, unsigned int h)
468{
469    cucul_canvas_t *new;
470    unsigned int f, saved_f, framecount;
471
472    if(cv->refcount)
473    {
474#if defined(HAVE_ERRNO_H)
475        errno = EBUSY;
476#endif
477        return -1;
478    }
479
480    new = cucul_create_canvas(w, h);
481
482    framecount = cucul_get_canvas_frame_count(cv);
483    saved_f = cv->frame;
484
485    for(f = 0; f < framecount; f++)
486    {
487        if(f)
488            cucul_create_canvas_frame(new, framecount);
489
490        cucul_set_canvas_frame(cv, f);
491        cucul_set_canvas_frame(new, f);
492        cucul_blit(new, -x, -y, cv, NULL);
493
494        free(cv->frames[f].chars);
495        free(cv->frames[f].attrs);
496    }
497    free(cv->frames);
498
499    memcpy(cv, new, sizeof(cucul_canvas_t));
500    free(new);
501
502    cucul_set_canvas_frame(cv, saved_f);
503
504    return 0;
505}
506
Note: See TracBrowser for help on using the repository browser.