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

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