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

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