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

Last change on this file since 4823 was 4823, checked in by Sam Hocevar, 9 years ago

build: ship a weak symbol for vsnprintf, too, because our weak symbol for
sprintf_s uses that function and VS2010 does not strip it off even if it
is unused. Also do not use 64-bit integer division in the timer code because
that will create unwanted references to _moddi3 and _divdi3.

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