source: libcaca/trunk/caca/string.c @ 4369

Last change on this file since 4369 was 4369, checked in by Sam Hocevar, 12 years ago

Add the copyright unit test and update copyright information everywhere.

  • Property svn:keywords set to Id
File size: 18.2 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2010 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  This library is free software. It comes without any warranty, to
7 *  the extent permitted by applicable law. You can redistribute it
8 *  and/or modify it under the terms of the Do What The Fuck You Want
9 *  To Public License, Version 2, as published by Sam Hocevar. See
10 *  http://sam.zoy.org/wtfpl/COPYING for more details.
11 */
12
13/*
14 *  This file contains various canvas handling functions such as character
15 *  and string drawing.
16 */
17
18#include "config.h"
19
20#if !defined(__KERNEL__)
21#   include <stdio.h> /* BUFSIZ */
22#   include <string.h>
23#   include <stdlib.h>
24#   include <stdarg.h>
25#   if defined(HAVE_UNISTD_H)
26#       include <unistd.h>
27#   endif
28#   if defined(HAVE_SIGNAL_H)
29#       include <signal.h>
30#   endif
31#   if defined(HAVE_SYS_IOCTL_H)
32#       include <sys/ioctl.h>
33#   endif
34#endif
35
36#include "caca.h"
37#include "caca_internals.h"
38
39/** \brief Set cursor position.
40 *
41 *  Put the cursor at the given coordinates. Functions making use of the
42 *  cursor will use the new values. Setting the cursor position outside the
43 *  canvas is legal but the cursor will not be shown.
44 *
45 *  This function never fails.
46 *
47 *  \param cv A handle to the libcaca canvas.
48 *  \param x X cursor coordinate.
49 *  \param y Y cursor coordinate.
50 *  \return This function always returns 0.
51 */
52int caca_gotoxy(caca_canvas_t *cv, int x, int y)
53{
54    cv->frames[cv->frame].x = x;
55    cv->frames[cv->frame].y = y;
56
57    return 0;
58}
59
60/** \brief Get X cursor position.
61 *
62 *  Retrieve the X coordinate of the cursor's position.
63 *
64 *  This function never fails.
65 *
66 *  \param cv A handle to the libcaca canvas.
67 *  \return The cursor's X coordinate.
68 */
69int caca_wherex(caca_canvas_t const *cv)
70{
71    return cv->frames[cv->frame].x;
72}
73
74/** \brief Get Y cursor position.
75 *
76 *  Retrieve the Y coordinate of the cursor's position.
77 *
78 *  This function never fails.
79 *
80 *  \param cv A handle to the libcaca canvas.
81 *  \return The cursor's Y coordinate.
82 */
83int caca_wherey(caca_canvas_t const *cv)
84{
85    return cv->frames[cv->frame].y;
86}
87
88/** \brief Print an ASCII or Unicode character.
89 *
90 *  Print an ASCII or Unicode character at the given coordinates, using
91 *  the default foreground and background colour values.
92 *
93 *  If the coordinates are outside the canvas boundaries, nothing is printed.
94 *  If a fullwidth Unicode character gets overwritten, its remaining visible
95 *  parts are replaced with spaces. If the canvas' boundaries would split the
96 *  fullwidth character in two, a space is printed instead.
97 *
98 *  The behaviour when printing non-printable characters or invalid UTF-32
99 *  characters is undefined. To print a sequence of bytes forming an UTF-8
100 *  character instead of an UTF-32 character, use the caca_put_str() function.
101 *
102 *  This function returns the width of the printed character. If it is a
103 *  fullwidth character, 2 is returned. Otherwise, 1 is returned.
104 *
105 *  This function never fails.
106 *
107 *  \param cv A handle to the libcaca canvas.
108 *  \param x X coordinate.
109 *  \param y Y coordinate.
110 *  \param ch The character to print.
111 *  \return The width of the printed character: 2 for a fullwidth character,
112 *          1 otherwise.
113 */
114int caca_put_char(caca_canvas_t *cv, int x, int y, uint32_t ch)
115{
116    uint32_t *curchar, *curattr, attr;
117    int fullwidth, xmin, xmax, ret;
118
119    if(ch == CACA_MAGIC_FULLWIDTH)
120        return 1;
121
122    fullwidth = caca_utf32_is_fullwidth(ch);
123    ret = fullwidth ? 2 : 1;
124
125    if(x >= (int)cv->width || y < 0 || y >= (int)cv->height)
126        return ret;
127
128    if(x == -1 && fullwidth)
129    {
130        x = 0;
131        ch = ' ';
132        fullwidth = 0;
133    }
134    else if(x < 0)
135        return ret;
136
137    curchar = cv->chars + x + y * cv->width;
138    curattr = cv->attrs + x + y * cv->width;
139    attr = cv->curattr;
140
141    xmin = xmax = x;
142
143    /* When overwriting the right part of a fullwidth character,
144     * replace its left part with a space. */
145    if(x && curchar[0] == CACA_MAGIC_FULLWIDTH)
146    {
147        curchar[-1] = ' ';
148        xmin--;
149    }
150
151    if(fullwidth)
152    {
153        if(x + 1 == (int)cv->width)
154            ch = ' ';
155        else
156        {
157            xmax++;
158
159            /* When overwriting the left part of a fullwidth character,
160             * replace its right part with a space. */
161            if(x + 2 < (int)cv->width && curchar[2] == CACA_MAGIC_FULLWIDTH)
162            {
163                curchar[2] = ' ';
164                xmax++;
165            }
166
167            curchar[1] = CACA_MAGIC_FULLWIDTH;
168            curattr[1] = attr;
169        }
170    }
171    else
172    {
173        /* When overwriting the left part of a fullwidth character,
174         * replace its right part with a space. */
175        if(x + 1 != (int)cv->width && curchar[1] == CACA_MAGIC_FULLWIDTH)
176        {
177            curchar[1] = ' ';
178            xmax++;
179        }
180    }
181
182    /* Only add a dirty rectangle if we are pasting a different character
183     * or attribute at that place. This does not account for inconsistencies
184     * in the canvas, ie. if CACA_MAGIC_FULLWIDTH lies at illegal places,
185     * but it's the caller's responsibility not to corrupt the contents. */
186    if(!cv->dirty_disabled
187        && (curchar[0] != ch || curattr[0] != attr))
188        caca_add_dirty_rect(cv, xmin, y, xmax - xmin + 1, 1);
189
190    curchar[0] = ch;
191    curattr[0] = attr;
192
193    return ret;
194}
195
196/** \brief Get the Unicode character at the given coordinates.
197 *
198 *  Get the ASCII or Unicode value of the character at the given
199 *  coordinates. If the value is less or equal to 127 (0x7f),
200 *  the character can be printed as ASCII. Otherise, it must be handled
201 *  as a UTF-32 value.
202 *
203 *  If the coordinates are outside the canvas boundaries, a space (0x20)
204 *  is returned.
205 *
206 *  A special exception is when CACA_MAGIC_FULLWIDTH is returned. This
207 *  value is guaranteed not to be a valid Unicode character, and indicates
208 *  that the character at the left of the requested one is a fullwidth
209 *  character.
210 *
211 *  This function never fails.
212 *
213 *  \param cv A handle to the libcaca canvas.
214 *  \param x X coordinate.
215 *  \param y Y coordinate.
216 *  \return The Unicode character at the given coordinates.
217 */
218uint32_t caca_get_char(caca_canvas_t const *cv, int x, int y)
219{
220    if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
221        return ' ';
222
223    return cv->chars[x + y * cv->width];
224}
225
226/** \brief Print a string.
227 *
228 *  Print an UTF-8 string at the given coordinates, using the default
229 *  foreground and background values. The coordinates may be outside the
230 *  canvas boundaries (eg. a negative Y coordinate) and the string will
231 *  be cropped accordingly if it is too long.
232 *
233 *  See caca_put_char() for more information on how fullwidth characters
234 *  are handled when overwriting each other or at the canvas' boundaries.
235 *
236 *  This function returns the number of cells printed by the string. It is
237 *  not the number of characters printed, because fullwidth characters
238 *  account for two cells.
239 *
240 *  This function never fails.
241 *
242 *  \param cv A handle to the libcaca canvas.
243 *  \param x X coordinate.
244 *  \param y Y coordinate.
245 *  \param s The string to print.
246 *  \return The number of cells printed.
247 */
248int caca_put_str(caca_canvas_t *cv, int x, int y, char const *s)
249{
250    size_t rd;
251    int len = 0;
252
253    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
254    {
255        while(*s)
256        {
257            len += caca_utf32_is_fullwidth(caca_utf8_to_utf32(s, &rd)) ? 2 : 1;
258            s += rd;
259        }
260        return len;
261    }
262
263    while(*s)
264    {
265        uint32_t ch = caca_utf8_to_utf32(s, &rd);
266
267        if(x + len >= -1 && x + len < (int)cv->width)
268            caca_put_char(cv, x + len, y, ch);
269
270        len += caca_utf32_is_fullwidth(ch) ? 2 : 1;
271        s += rd;
272    }
273
274    return len;
275}
276
277/** \brief Print a formated string.
278 *
279 *  Format a string at the given coordinates, using the default foreground
280 *  and background values. The coordinates may be outside the canvas
281 *  boundaries (eg. a negative Y coordinate) and the string will be cropped
282 *  accordingly if it is too long. The syntax of the format string is the
283 *  same as for the C printf() function.
284 *
285 *  This function returns the number of cells printed by the string. It is
286 *  not the number of characters printed, because fullwidth characters
287 *  account for two cells.
288 *
289 *  This function never fails.
290 *
291 *  \param cv A handle to the libcaca canvas.
292 *  \param x X coordinate.
293 *  \param y Y coordinate.
294 *  \param format The format string to print.
295 *  \param ... Arguments to the format string.
296 *  \return The number of cells printed.
297 */
298int caca_printf(caca_canvas_t *cv, int x, int y, char const *format, ...)
299{
300    va_list args;
301    int ret;
302    va_start(args, format);
303    ret = caca_vprintf(cv, x, y, format, args);
304    va_end(args);
305    return ret;
306}
307
308/** \brief Print a formated string (va_list version).
309 *
310 *  Format a string at the given coordinates, using the default foreground
311 *  and background values. The coordinates may be outside the canvas
312 *  boundaries (eg. a negative Y coordinate) and the string will be cropped
313 *  accordingly if it is too long. The syntax of the format string is the
314 *  same as for the C vprintf() function.
315 *
316 *  This function returns the number of cells printed by the string. It is
317 *  not the number of characters printed, because fullwidth characters
318 *  account for two cells.
319 *
320 *  This function never fails.
321 *
322 *  \param cv A handle to the libcaca canvas.
323 *  \param x X coordinate.
324 *  \param y Y coordinate.
325 *  \param format The format string to print.
326 *  \param args A va_list containting the arguments to the format string.
327 *  \return The number of cells printed.
328 */
329int caca_vprintf(caca_canvas_t *cv, int x, int y, char const *format,
330                 va_list args)
331{
332    char tmp[BUFSIZ];
333    char *buf = tmp;
334    int ret;
335
336    if(cv->width - x + 1 > BUFSIZ)
337        buf = malloc(cv->width - x + 1);
338
339#if defined(HAVE_VSNPRINTF)
340    vsnprintf(buf, cv->width - x + 1, format, args);
341#else
342    vsprintf(buf, format, args);
343#endif
344    buf[cv->width - x] = '\0';
345
346    ret = caca_put_str(cv, x, y, buf);
347
348    if(buf != tmp)
349        free(buf);
350
351    return ret;
352}
353
354/** \brief Clear the canvas.
355 *
356 *  Clear the canvas using the current foreground and background colours.
357 *
358 *  This function never fails.
359 *
360 *  \param cv The canvas to clear.
361 *  \return This function always returns 0.
362 */
363int caca_clear_canvas(caca_canvas_t *cv)
364{
365    uint32_t attr = cv->curattr;
366    int n;
367
368    for(n = cv->width * cv->height; n--; )
369    {
370        cv->chars[n] = (uint32_t)' ';
371        cv->attrs[n] = attr;
372    }
373
374    if(!cv->dirty_disabled)
375        caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
376
377    return 0;
378}
379
380/** \brief Set cursor handle.
381 *
382 *  Set the canvas' handle. Blitting functions will use the handle value
383 *  to put the canvas at the proper coordinates.
384 *
385 *  This function never fails.
386 *
387 *  \param cv A handle to the libcaca canvas.
388 *  \param x X handle coordinate.
389 *  \param y Y handle coordinate.
390 *  \return This function always returns 0.
391 */
392int caca_set_canvas_handle(caca_canvas_t *cv, int x, int y)
393{
394    cv->frames[cv->frame].handlex = x;
395    cv->frames[cv->frame].handley = y;
396
397    return 0;
398}
399
400/** \brief Get X handle position.
401 *
402 *  Retrieve the X coordinate of the canvas' handle.
403 *
404 *  This function never fails.
405 *
406 *  \param cv A handle to the libcaca canvas.
407 *  \return The canvas' handle's X coordinate.
408 */
409int caca_get_canvas_handle_x(caca_canvas_t const *cv)
410{
411    return cv->frames[cv->frame].handlex;
412}
413
414/** \brief Get Y handle position.
415 *
416 *  Retrieve the Y coordinate of the canvas' handle.
417 *
418 *  This function never fails.
419 *
420 *  \param cv A handle to the libcaca canvas.
421 *  \return The canvas' handle's Y coordinate.
422 */
423int caca_get_canvas_handle_y(caca_canvas_t const *cv)
424{
425    return cv->frames[cv->frame].handley;
426}
427
428/** \brief Blit a canvas onto another one.
429 *
430 *  Blit a canvas onto another one at the given coordinates.
431 *  An optional mask canvas can be used.
432 *
433 *  If an error occurs, -1 is returned and \b errno is set accordingly:
434 *  - \c EINVAL A mask was specified but the mask size and source canvas
435 *    size do not match.
436 *
437 *  \param dst The destination canvas.
438 *  \param x X coordinate.
439 *  \param y Y coordinate.
440 *  \param src The source canvas.
441 *  \param mask The mask canvas.
442 *  \return 0 in case of success, -1 if an error occurred.
443 */
444int caca_blit(caca_canvas_t *dst, int x, int y,
445              caca_canvas_t const *src, caca_canvas_t const *mask)
446{
447    int i, j, starti, startj, endi, endj, stride, bleed_left, bleed_right;
448
449    if(mask && (src->width != mask->width || src->height != mask->height))
450    {
451        seterrno(EINVAL);
452        return -1;
453    }
454
455    x -= src->frames[src->frame].handlex;
456    y -= src->frames[src->frame].handley;
457
458    starti = x < 0 ? -x : 0;
459    startj = y < 0 ? -y : 0;
460    endi = (x + src->width >= dst->width) ? dst->width - x : src->width;
461    endj = (y + src->height >= dst->height) ? dst->height - y : src->height;
462    stride = endi - starti;
463
464    if(starti > src->width || startj > src->height
465        || starti >= endi || startj >= endj)
466        return 0;
467
468    bleed_left = bleed_right = 0;
469
470    for(j = startj; j < endj; j++)
471    {
472        int dstix = (j + y) * dst->width + starti + x;
473        int srcix = j * src->width + starti;
474
475        /* FIXME: we are ignoring the mask here */
476        if((starti + x) && dst->chars[dstix] == CACA_MAGIC_FULLWIDTH)
477        {
478            dst->chars[dstix - 1] = ' ';
479            bleed_left = 1;
480        }
481
482        if(endi + x < dst->width
483                && dst->chars[dstix + stride] == CACA_MAGIC_FULLWIDTH)
484        {
485            dst->chars[dstix + stride] = ' ';
486            bleed_right = 1;
487        }
488
489        if(mask)
490        {
491            for(i = 0; i < stride; i++)
492            {
493                if(mask->chars[srcix + i] == (uint32_t)' ')
494                    continue;
495
496                if(dst->chars[dstix + i] != src->chars[srcix + i] ||
497                   dst->attrs[dstix + i] != src->attrs[srcix + i])
498                {
499                    dst->chars[dstix + i] = src->chars[srcix + i];
500                    dst->attrs[dstix + i] = src->attrs[srcix + i];
501                    if(!dst->dirty_disabled)
502                        caca_add_dirty_rect(dst, x + starti + i, y + j, 1, 1);
503                }
504            }
505        }
506        else
507        {
508            if(memcmp(dst->chars + dstix, src->chars + srcix, stride * 4) ||
509               memcmp(dst->attrs + dstix, src->attrs + srcix, stride * 4))
510            {
511                /* FIXME be more precise ? */
512                memcpy(dst->chars + dstix, src->chars + srcix, stride * 4);
513                memcpy(dst->attrs + dstix, src->attrs + srcix, stride * 4);
514                if(!dst->dirty_disabled)
515                    caca_add_dirty_rect(dst, x + starti, y + j, stride, 1);
516            }
517        }
518
519        /* Fix split fullwidth chars */
520        if(src->chars[srcix] == CACA_MAGIC_FULLWIDTH)
521            dst->chars[dstix] = ' ';
522
523        if(endi < src->width && src->chars[endi] == CACA_MAGIC_FULLWIDTH)
524            dst->chars[dstix + stride - 1] = ' ';
525    }
526
527
528    return 0;
529}
530
531/** \brief Set a canvas' new boundaries.
532 *
533 *  Set new boundaries for a canvas. This function can be used to crop a
534 *  canvas, to expand it or for combinations of both actions. All frames
535 *  are affected by this function.
536 *
537 *  If an error occurs, -1 is returned and \b errno is set accordingly:
538 *  - \c EINVAL Specified width or height is invalid.
539 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
540 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
541 *    happens, the canvas handle becomes invalid and should not be used.
542 *
543 *  \param cv The canvas to crop.
544 *  \param x X coordinate of the top-left corner.
545 *  \param y Y coordinate of the top-left corner.
546 *  \param w The width of the cropped area.
547 *  \param h The height of the cropped area.
548 *  \return 0 in case of success, -1 if an error occurred.
549 */
550int caca_set_canvas_boundaries(caca_canvas_t *cv, int x, int y, int w, int h)
551{
552    caca_canvas_t *new;
553    int f, saved_f, framecount;
554
555    if(cv->refcount)
556    {
557        seterrno(EBUSY);
558        return -1;
559    }
560
561    if(w < 0 || h < 0)
562    {
563        seterrno(EINVAL);
564        return -1;
565    }
566
567    new = caca_create_canvas(w, h);
568
569    framecount = caca_get_frame_count(cv);
570    saved_f = cv->frame;
571
572    for(f = 0; f < framecount; f++)
573    {
574        if(f)
575            caca_create_frame(new, framecount);
576
577        caca_set_frame(cv, f);
578        caca_set_frame(new, f);
579        caca_blit(new, -x, -y, cv, NULL);
580        free(cv->frames[f].chars);
581        free(cv->frames[f].attrs);
582    }
583    free(cv->frames);
584
585    cv->frames = new->frames;
586    free(new);
587
588    caca_set_frame(cv, saved_f);
589    _caca_load_frame_info(cv);
590
591    /* FIXME: this may be optimised somewhat */
592    if(!cv->dirty_disabled)
593        caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
594
595    return 0;
596}
597
598/*
599 * XXX: The following functions are aliases.
600 */
601
602int cucul_gotoxy(cucul_canvas_t *, int, int) CACA_ALIAS(caca_gotoxy);
603int cucul_get_cursor_x(cucul_canvas_t const *) CACA_ALIAS(caca_wherex);
604int cucul_get_cursor_y(cucul_canvas_t const *) CACA_ALIAS(caca_wherey);
605int caca_get_cursor_x(caca_canvas_t const *) CACA_ALIAS(caca_wherex);
606int caca_get_cursor_y(caca_canvas_t const *) CACA_ALIAS(caca_wherey);
607int cucul_put_char(cucul_canvas_t *, int, int, uint32_t)
608         CACA_ALIAS(caca_put_char);
609uint32_t cucul_get_char(cucul_canvas_t const *, int, int)
610         CACA_ALIAS(caca_get_char);
611int cucul_put_str(cucul_canvas_t *, int, int, char const *)
612         CACA_ALIAS(caca_put_str);
613int cucul_printf(cucul_canvas_t *, int, int, char const *, ...)
614         CACA_ALIAS(caca_printf);
615int cucul_clear_canvas(cucul_canvas_t *) CACA_ALIAS(caca_clear_canvas);
616int cucul_set_canvas_handle(cucul_canvas_t *, int, int)
617         CACA_ALIAS(caca_set_canvas_handle);
618int cucul_get_canvas_handle_x(cucul_canvas_t const *)
619         CACA_ALIAS(caca_get_canvas_handle_x);
620int cucul_get_canvas_handle_y(cucul_canvas_t const *)
621         CACA_ALIAS(caca_get_canvas_handle_y);
622int cucul_blit(cucul_canvas_t *, int, int, cucul_canvas_t const *,
623                        cucul_canvas_t const *) CACA_ALIAS(caca_blit);
624int cucul_set_canvas_boundaries(cucul_canvas_t *, int, int, int, int)
625         CACA_ALIAS(caca_set_canvas_boundaries);
626
Note: See TracBrowser for help on using the repository browser.