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

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

win32: define a custom sprintf_s() weak symbol. The VS2010 runtime does not
provide the deprecated snprintf(). The mingw32 runtime does not provide the
MS-specific sprintf_s(). Mingw-w64 copes with both. So we switch to sprintf_s
but also provide it as a weak symbol so that mingw32 does not complain.

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