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

Last change on this file since 1231 was 1231, checked in by Sam Hocevar, 14 years ago
  • Removed "This function..." constructs from documentation. Fixed a few documentation errors or imprecisions.
  • Property svn:keywords set to Id
File size: 11.4 KB
RevLine 
[524]1/*
[672]2 *  libcucul      Canvas for ultrafast compositing of Unicode letters
[527]3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
[524]4 *                All Rights Reserved
5 *
[769]6 *  $Id: canvas.c 1231 2006-10-25 22:06:21Z sam $
7 *
[524]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
[769]14/*
[668]15 *  This file contains various canvas handling functions such as character
16 *  and string drawing.
[524]17 */
18
19#include "config.h"
[859]20#include "common.h"
[524]21
[568]22#if !defined(__KERNEL__)
23#   include <stdio.h> /* BUFSIZ */
24#   include <string.h>
25#   include <stdlib.h>
26#   include <stdarg.h>
[870]27#   if defined(HAVE_ERRNO_H)
28#       include <errno.h>
29#   endif
[568]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
[524]39#endif
40
41#include "cucul.h"
42#include "cucul_internals.h"
43
[958]44/** \brief Print an ASCII or Unicode character.
[524]45 *
[1231]46 *  Print an ASCII or Unicode character at the given coordinates, using
47 *  the default foreground and background colour values.
[524]48 *
[958]49 *  If the coordinates are outside the canvas boundaries, nothing is printed.
[1231]50 *  If a fullwidth Unicode character gets overwritten, its remaining visible
51 *  parts are replaced with spaces. If the canvas' boundaries would split the
[1215]52 *  fullwidth character in two, a space is printed instead.
[958]53 *
[1215]54 *  The behaviour when printing non-printable characters or invalid UTF-32
55 *  characters is undefined. To print a sequence of bytes forming an UTF-8
[1221]56 *  character instead of an UTF-32 character, use the cucul_putstr() function.
[1215]57 *
[870]58 *  This function never fails.
59 *
[811]60 *  \param cv A handle to the libcucul canvas.
[524]61 *  \param x X coordinate.
62 *  \param y Y coordinate.
[810]63 *  \param ch The character to print.
[870]64 *  \return This function always returns 0.
[524]65 */
[958]66int cucul_putchar(cucul_canvas_t *cv, int x, int y, unsigned long int ch)
[524]67{
[1215]68    uint32_t *curchar, *curattr, attr;
69    int fullwidth;
70
[1218]71    if(x >= (int)cv->width || y < 0 || y >= (int)cv->height)
[874]72        return 0;
[524]73
[1218]74    if(ch == CUCUL_MAGIC_FULLWIDTH)
75        return 0;
76
[1215]77    fullwidth = cucul_utf32_is_fullwidth(ch);
[583]78
[1215]79    if(x == -1 && fullwidth)
80    {
81        x = 0;
82        ch = ' ';
83        fullwidth = 0;
84    }
85    else if(x < 0)
86        return 0;
[874]87
[1215]88    curchar = cv->chars + x + y * cv->width;
89    curattr = cv->attr + x + y * cv->width;
90    attr = (cv->bgcolor << 16) | cv->fgcolor;
91
92    /* When overwriting the right part of a fullwidth character,
93     * replace its left part with a space. */
[1218]94    if(x && curchar[0] == CUCUL_MAGIC_FULLWIDTH)
[1215]95        curchar[-1] = ' ';
96
97    if(fullwidth)
98    {
99        if(x + 1 == (int)cv->width)
100            ch = ' ';
101        else
102        {
103            /* When overwriting the left part of a fullwidth character,
104             * replace its right part with a space. */
[1218]105            if(x + 2 < (int)cv->width && curchar[2] == CUCUL_MAGIC_FULLWIDTH)
[1215]106                curchar[2] = ' ';
107
[1218]108            curchar[1] = CUCUL_MAGIC_FULLWIDTH;
[1215]109            curattr[1] = attr;
110        }
111    }
112    else
113    {
114        /* When overwriting the left part of a fullwidth character,
115         * replace its right part with a space. */
[1218]116        if(x + 1 != (int)cv->width && curchar[1] == CUCUL_MAGIC_FULLWIDTH)
[1215]117            curchar[1] = ' ';
118    }
119
120    curchar[0] = ch;
121    curattr[0] = attr;
122
[874]123    return 0;
[524]124}
125
[1066]126/** \brief Get the Unicode character at the given coordinates.
127 *
[1231]128 *  Get the ASCII or Unicode value of the character at the given
129 *  coordinates. If the value is less or equal to 127 (0x7f),
[1066]130 *  the character can be printed as ASCII. Otherise, it must be handled
131 *  as a UTF-32 value.
132 *
133 *  If the coordinates are outside the canvas boundaries, a space (0x20)
[1221]134 *  is returned.
[1066]135 *
[1221]136 *  A special exception is when CUCUL_MAGIC_FULLWIDTH is returned. This
137 *  value is guaranteed not to be a valid Unicode character, and indicates
138 *  that the character at the left of the requested one is a fullwidth
139 *  character.
140 *
[1066]141 *  This function never fails.
142 *
143 *  \param cv A handle to the libcucul canvas.
144 *  \param x X coordinate.
145 *  \param y Y coordinate.
146 *  \param ch The requested character value.
[1231]147 *  \return This function always returns 0.
[1066]148 */
149unsigned long int cucul_getchar(cucul_canvas_t *cv, int x, int y)
150{
151    if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
[1215]152        return ' ';
[1066]153
154    return (unsigned long int)cv->chars[x + y * cv->width];
155}
156
[524]157/** \brief Print a string.
158 *
[1231]159 *  Print an UTF-8 string at the given coordinates, using the default
160 *  foreground and background values. The coordinates may be outside the
161 *  canvas boundaries (eg. a negative Y coordinate) and the string will
[524]162 *  be cropped accordingly if it is too long.
163 *
[1215]164 *  See cucul_putchar() for more information on how fullwidth characters
165 *  are handled when overwriting each other or at the canvas' boundaries.
166 *
[870]167 *  This function never fails.
168 *
[811]169 *  \param cv A handle to the libcucul canvas.
[524]170 *  \param x X coordinate.
171 *  \param y Y coordinate.
172 *  \param s The string to print.
[870]173 *  \return This function always returns 0.
[524]174 */
[874]175int cucul_putstr(cucul_canvas_t *cv, int x, int y, char const *s)
[524]176{
[1215]177    unsigned int read;
[524]178
[811]179    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
[874]180        return 0;
[524]181
[1215]182    while(*s && x < -1)
[524]183    {
[1215]184        x += cucul_utf32_is_fullwidth(cucul_utf8_to_utf32(s, &read)) ? 2 : 1;
185        s += read;
[524]186    }
187
[1215]188    while(*s && x < (int)cv->width)
[524]189    {
[1215]190        uint32_t ch = cucul_utf8_to_utf32(s, &read);
191        cucul_putchar(cv, x, y, ch);
192        x += cucul_utf32_is_fullwidth(ch) ? 2 : 1;
193        s += read;
[524]194    }
[874]195
196    return 0;
[524]197}
198
[773]199/** \brief Print a formated string.
[524]200 *
[1231]201 *  Format a string at the given coordinates, using the default foreground
202 *  and background values. The coordinates may be outside the canvas
203 *  boundaries (eg. a negative Y coordinate) and the string will be cropped
204 *  accordingly if it is too long. The syntax of the format string is the
205 *  same as for the C printf() function.
[524]206 *
[870]207 *  This function never fails.
208 *
[811]209 *  \param cv A handle to the libcucul canvas.
[524]210 *  \param x X coordinate.
211 *  \param y Y coordinate.
212 *  \param format The format string to print.
213 *  \param ... Arguments to the format string.
[870]214 *  \return This function always returns 0.
[524]215 */
[874]216int cucul_printf(cucul_canvas_t *cv, int x, int y, char const *format, ...)
[524]217{
218    char tmp[BUFSIZ];
219    char *buf = tmp;
220    va_list args;
221
[811]222    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
[874]223        return 0;
[524]224
[811]225    if(cv->width - x + 1 > BUFSIZ)
226        buf = malloc(cv->width - x + 1);
[524]227
228    va_start(args, format);
229#if defined(HAVE_VSNPRINTF)
[811]230    vsnprintf(buf, cv->width - x + 1, format, args);
[524]231#else
232    vsprintf(buf, format, args);
233#endif
[811]234    buf[cv->width - x] = '\0';
[524]235    va_end(args);
236
[811]237    cucul_putstr(cv, x, y, buf);
[524]238
239    if(buf != tmp)
240        free(buf);
[874]241
242    return 0;
[524]243}
244
[773]245/** \brief Clear the canvas.
[524]246 *
[1231]247 *  Clear the canvas using the current foreground and background colours.
[814]248 *
[870]249 *  This function never fails.
250 *
[814]251 *  \param cv The canvas to clear.
[870]252 *  \return This function always returns 0.
[524]253 */
[874]254int cucul_clear_canvas(cucul_canvas_t *cv)
[524]255{
[832]256    uint32_t color = (cv->bgcolor << 16) | cv->fgcolor;
257    unsigned int n;
[524]258
[832]259    for(n = cv->width * cv->height; n--; )
260    {
261        cv->chars[n] = (uint32_t)' ';
262        cv->attr[n] = color;
263    }
[874]264
265    return 0;
[524]266}
267
[670]268/** \brief Blit a canvas onto another one.
269 *
[1231]270 *  Blit a canvas onto another one at the given coordinates.
[670]271 *  An optional mask canvas can be used.
272 *
[874]273 *  If an error occurs, -1 is returned and \b errno is set accordingly:
274 *  - \c EINVAL A mask was specified but the mask size and source canvas
275 *    size do not match.
276 *
[670]277 *  \param dst The destination canvas.
278 *  \param x X coordinate.
279 *  \param y Y coordinate.
280 *  \param src The source canvas.
281 *  \param mask The mask canvas.
[874]282 *  \return 0 in case of success, -1 if an error occurred.
[670]283 */
[870]284int cucul_blit(cucul_canvas_t *dst, int x, int y,
285               cucul_canvas_t const *src, cucul_canvas_t const *mask)
[670]286{
287    int i, j, starti, startj, endi, endj;
288
[671]289    if(mask && (src->width != mask->width || src->height != mask->height))
[870]290    {
291#if defined(HAVE_ERRNO_H)
292        errno = EINVAL;
293#endif
294        return -1;
295    }
[670]296
297    starti = x < 0 ? -x : 0;
298    startj = y < 0 ? -y : 0;
299    endi = (x + src->width >= dst->width) ? dst->width - x : src->width;
300    endj = (y + src->height >= dst->height) ? dst->height - y : src->height;
301
[988]302    if((unsigned int)starti > src->width || (unsigned int)startj > src->height
303        || starti >= endi || startj >= endj)
[870]304        return 0;
[671]305
[670]306    for(j = startj; j < endj; j++)
307    {
[1224]308        unsigned int dstix = (j + y) * dst->width + starti + x;
309        unsigned int srcix = j * src->width + starti;
310        int stride = endi - starti;
311
312        /* FIXME: we are ignoring the mask here */
313        if((starti + x) && dst->chars[dstix] == CUCUL_MAGIC_FULLWIDTH)
314            dst->chars[dstix - 1] = ' ';
315
316        if((unsigned int)(endi + x) < dst->width
317                && dst->chars[dstix + stride] == CUCUL_MAGIC_FULLWIDTH)
318            dst->chars[dstix + stride] = ' ';
319
[671]320        if(mask)
[670]321        {
[1224]322            for(i = 0; i < stride; i++)
[671]323            {
[1224]324                if(mask->chars[srcix + i] == (uint32_t)' ')
[671]325                    continue;
[670]326
[1224]327                dst->chars[dstix + i] = src->chars[srcix + i];
328                dst->attr[dstix + i] = src->attr[srcix + i];
[671]329            }
[670]330        }
[671]331        else
332        {
[1224]333            memcpy(dst->chars + dstix, src->chars + srcix, (stride) * 4);
334            memcpy(dst->attr + dstix, src->attr + srcix, (stride) * 4);
[671]335        }
[1224]336
337        /* Fix split fullwidth chars */
338        if(src->chars[srcix] == CUCUL_MAGIC_FULLWIDTH)
339            dst->chars[dstix] = ' ';
340
341        if((unsigned int)endi < src->width
342                && src->chars[endi] == CUCUL_MAGIC_FULLWIDTH)
343            dst->chars[dstix + stride - 1] = ' ';
[670]344    }
[870]345
346    return 0;
[670]347}
348
[1079]349/** \brief Set a canvas' new boundaries.
350 *
[1231]351 *  Set new boundaries for a canvas. This function can be used to crop a
[1079]352 *  canvas, to expand it or for combinations of both actions.
353 *
354 *  If an error occurs, -1 is returned and \b errno is set accordingly:
355 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
356 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
357 *    happens, the canvas handle becomes invalid and should not be used.
358 *
359 *  \param cv The canvas to crop.
360 *  \param x X coordinate of the top-left corner.
361 *  \param y Y coordinate of the top-left corner.
362 *  \param w The width of the cropped area.
363 *  \param h The height of the cropped area.
364 *  \return 0 in case of success, -1 if an error occurred.
365 */
366int cucul_set_canvas_boundaries(cucul_canvas_t *cv, int x, int y,
367                                unsigned int w, unsigned int h)
368{
369    cucul_canvas_t *new;
370    unsigned int f, saved_f, framecount;
371
372    if(cv->refcount)
373    {
374#if defined(HAVE_ERRNO_H)
375        errno = EBUSY;
376#endif
377        return -1;
378    }
379
380    new = cucul_create_canvas(w, h);
381
382    framecount = cucul_get_canvas_frame_count(cv);
383    saved_f = cv->frame;
384
385    for(f = 0; f < framecount; f++)
386    {
387        if(f)
388            cucul_create_canvas_frame(new, framecount);
389
390        cucul_set_canvas_frame(cv, f);
391        cucul_set_canvas_frame(new, f);
392        cucul_blit(new, -x, -y, cv, NULL);
393
394        free(cv->allchars[f]);
395        free(cv->allattr[f]);
396    }
[1149]397    free(cv->allchars);
398    free(cv->allattr);
[1079]399
400    memcpy(cv, new, sizeof(cucul_canvas_t));
401    free(new);
402
403    cucul_set_canvas_frame(cv, saved_f);
404
405    return 0;
406}
407
Note: See TracBrowser for help on using the repository browser.