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

Last change on this file since 1148 was 1137, checked in by Sam Hocevar, 14 years ago
  • Fixed a bug in cucul_putchar() that broke half of the Unicode set.
  • Property svn:keywords set to Id
File size: 9.6 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 1137 2006-09-29 23:36:02Z 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 Print an ASCII or Unicode character.
45 *
46 *  This function prints an ASCII or Unicode character at the given
47 *  coordinates, using the default foreground and background values.
48 *
49 *  If the coordinates are outside the canvas boundaries, nothing is printed.
50 *  If the character value is a non-printable character or is outside the
51 *  UTF-32 range, it is replaced with a space. To print a sequence of bytes
52 *  forming an UTF-8 character instead of an UTF-32 character, use the
53 *  cucul_putstr() function instead.
54 *
55 *  This function never fails.
56 *
57 *  \param cv A handle to the libcucul canvas.
58 *  \param x X coordinate.
59 *  \param y Y coordinate.
60 *  \param ch The character to print.
61 *  \return This function always returns 0.
62 */
63int cucul_putchar(cucul_canvas_t *cv, int x, int y, unsigned long int ch)
64{
65    if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
66        return 0;
67
68    if(ch < 0x20)
69        ch = 0x20;
70
71    cv->chars[x + y * cv->width] = ch;
72    cv->attr[x + y * cv->width] = (cv->bgcolor << 16) | cv->fgcolor;
73
74    return 0;
75}
76
77/** \brief Get the Unicode character at the given coordinates.
78 *
79 *  This function gets the ASCII or Unicode value of the character at
80 *  the given coordinates. If the value is less or equal to 127 (0x7f),
81 *  the character can be printed as ASCII. Otherise, it must be handled
82 *  as a UTF-32 value.
83 *
84 *  If the coordinates are outside the canvas boundaries, a space (0x20)
85 *  is returned.
86 *
87 *  This function never fails.
88 *
89 *  \param cv A handle to the libcucul canvas.
90 *  \param x X coordinate.
91 *  \param y Y coordinate.
92 *  \param ch The requested character value.
93 *  \return The character always returns 0.
94 */
95unsigned long int cucul_getchar(cucul_canvas_t *cv, int x, int y)
96{
97    if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
98        return (unsigned char)' ';
99
100    return (unsigned long int)cv->chars[x + y * cv->width];
101}
102
103/** \brief Print a string.
104 *
105 *  This function prints an UTF-8 string at the given coordinates, using the
106 *  default foreground and background values. The coordinates may be outside
107 *  the canvas boundaries (eg. a negative Y coordinate) and the string will
108 *  be cropped accordingly if it is too long.
109 *
110 *  This function never fails.
111 *
112 *  \param cv A handle to the libcucul canvas.
113 *  \param x X coordinate.
114 *  \param y Y coordinate.
115 *  \param s The string to print.
116 *  \return This function always returns 0.
117 */
118int cucul_putstr(cucul_canvas_t *cv, int x, int y, char const *s)
119{
120    uint32_t *chars, *attr;
121    unsigned int len;
122
123    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
124        return 0;
125
126    len = _cucul_strlen_utf8(s);
127
128    if(x < 0)
129    {
130        if(len < (unsigned int)-x)
131            return 0;
132        len -= -x;
133        s = _cucul_skip_utf8(s, -x);
134        x = 0;
135    }
136
137    chars = cv->chars + x + y * cv->width;
138    attr = cv->attr + x + y * cv->width;
139
140    if(x + len >= cv->width)
141        len = cv->width - x;
142
143    while(len)
144    {
145        *chars++ = cucul_utf8_to_utf32(s, NULL);
146        *attr++ = (cv->bgcolor << 16) | cv->fgcolor;
147
148        s = _cucul_skip_utf8(s, 1);
149        len--;
150    }
151
152    return 0;
153}
154
155/** \brief Print a formated string.
156 *
157 *  This function formats a string at the given coordinates, using the
158 *  default foreground and background values. The coordinates may be outside
159 *  the canvas boundaries (eg. a negative Y coordinate) and the string will
160 *  be cropped accordingly if it is too long. The syntax of the format
161 *  string is the same as for the C printf() function.
162 *
163 *  This function never fails.
164 *
165 *  \param cv A handle to the libcucul canvas.
166 *  \param x X coordinate.
167 *  \param y Y coordinate.
168 *  \param format The format string to print.
169 *  \param ... Arguments to the format string.
170 *  \return This function always returns 0.
171 */
172int cucul_printf(cucul_canvas_t *cv, int x, int y, char const *format, ...)
173{
174    char tmp[BUFSIZ];
175    char *buf = tmp;
176    va_list args;
177
178    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
179        return 0;
180
181    if(cv->width - x + 1 > BUFSIZ)
182        buf = malloc(cv->width - x + 1);
183
184    va_start(args, format);
185#if defined(HAVE_VSNPRINTF)
186    vsnprintf(buf, cv->width - x + 1, format, args);
187#else
188    vsprintf(buf, format, args);
189#endif
190    buf[cv->width - x] = '\0';
191    va_end(args);
192
193    cucul_putstr(cv, x, y, buf);
194
195    if(buf != tmp)
196        free(buf);
197
198    return 0;
199}
200
201/** \brief Clear the canvas.
202 *
203 *  This function clears the canvas using the current background colour.
204 *
205 *  This function never fails.
206 *
207 *  \param cv The canvas to clear.
208 *  \return This function always returns 0.
209 */
210int cucul_clear_canvas(cucul_canvas_t *cv)
211{
212    uint32_t color = (cv->bgcolor << 16) | cv->fgcolor;
213    unsigned int n;
214
215    /* We could use SLsmg_cls() etc., but drawing empty lines is much faster */
216    for(n = cv->width * cv->height; n--; )
217    {
218        cv->chars[n] = (uint32_t)' ';
219        cv->attr[n] = color;
220    }
221
222    return 0;
223}
224
225/** \brief Blit a canvas onto another one.
226 *
227 *  This function blits a canvas onto another one at the given coordinates.
228 *  An optional mask canvas can be used.
229 *
230 *  If an error occurs, -1 is returned and \b errno is set accordingly:
231 *  - \c EINVAL A mask was specified but the mask size and source canvas
232 *    size do not match.
233 *
234 *  \param dst The destination canvas.
235 *  \param x X coordinate.
236 *  \param y Y coordinate.
237 *  \param src The source canvas.
238 *  \param mask The mask canvas.
239 *  \return 0 in case of success, -1 if an error occurred.
240 */
241int cucul_blit(cucul_canvas_t *dst, int x, int y,
242               cucul_canvas_t const *src, cucul_canvas_t const *mask)
243{
244    int i, j, starti, startj, endi, endj;
245
246    if(mask && (src->width != mask->width || src->height != mask->height))
247    {
248#if defined(HAVE_ERRNO_H)
249        errno = EINVAL;
250#endif
251        return -1;
252    }
253
254    starti = x < 0 ? -x : 0;
255    startj = y < 0 ? -y : 0;
256    endi = (x + src->width >= dst->width) ? dst->width - x : src->width;
257    endj = (y + src->height >= dst->height) ? dst->height - y : src->height;
258
259    if((unsigned int)starti > src->width || (unsigned int)startj > src->height
260        || starti >= endi || startj >= endj)
261        return 0;
262
263    for(j = startj; j < endj; j++)
264    {
265        if(mask)
266        {
267            for(i = starti; i < endi; i++)
268            {
269                if(mask->chars[j * src->width + i] == (uint32_t)' ')
270                    continue;
271
272                dst->chars[(j + y) * dst->width + (i + x)]
273                                             = src->chars[j * src->width + i];
274                dst->attr[(j + y) * dst->width + (i + x)]
275                                             = src->attr[j * src->width + i];
276            }
277        }
278        else
279        {
280            memcpy(dst->chars + (j + y) * dst->width + starti + x,
281                   src->chars + j * src->width + starti,
282                   (endi - starti) * 4);
283            memcpy(dst->attr + (j + y) * dst->width + starti + x,
284                   src->attr + j * src->width + starti,
285                   (endi - starti) * 4);
286        }
287    }
288
289    return 0;
290}
291
292/** \brief Set a canvas' new boundaries.
293 *
294 *  This function sets new boundaries for a canvas. It can be used to crop a
295 *  canvas, to expand it or for combinations of both actions.
296 *
297 *  If an error occurs, -1 is returned and \b errno is set accordingly:
298 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
299 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
300 *    happens, the canvas handle becomes invalid and should not be used.
301 *
302 *  \param cv The canvas to crop.
303 *  \param x X coordinate of the top-left corner.
304 *  \param y Y coordinate of the top-left corner.
305 *  \param w The width of the cropped area.
306 *  \param h The height of the cropped area.
307 *  \return 0 in case of success, -1 if an error occurred.
308 */
309int cucul_set_canvas_boundaries(cucul_canvas_t *cv, int x, int y,
310                                unsigned int w, unsigned int h)
311{
312    cucul_canvas_t *new;
313    unsigned int f, saved_f, framecount;
314
315    if(cv->refcount)
316    {
317#if defined(HAVE_ERRNO_H)
318        errno = EBUSY;
319#endif
320        return -1;
321    }
322
323    new = cucul_create_canvas(w, h);
324
325    framecount = cucul_get_canvas_frame_count(cv);
326    saved_f = cv->frame;
327
328    for(f = 0; f < framecount; f++)
329    {
330        if(f)
331            cucul_create_canvas_frame(new, framecount);
332
333        cucul_set_canvas_frame(cv, f);
334        cucul_set_canvas_frame(new, f);
335        cucul_blit(new, -x, -y, cv, NULL);
336
337        free(cv->allchars[f]);
338        free(cv->allattr[f]);
339    }
340
341    memcpy(cv, new, sizeof(cucul_canvas_t));
342    free(new);
343
344    cucul_set_canvas_frame(cv, saved_f);
345
346    return 0;
347}
348
Note: See TracBrowser for help on using the repository browser.