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

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