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

Last change on this file since 3470 was 3470, checked in by Sam Hocevar, 11 years ago

Change the dirty rectangle API so that it can handle several rectangles. The
inner implementation still only handles one dirty rectangle, but this way
we can prepare supporting applictions for the future.

  • Property svn:keywords set to Id
File size: 15.5 KB
RevLine 
[524]1/*
[2821]2 *  libcaca       Colour ASCII-Art library
[527]3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
[524]4 *                All Rights Reserved
5 *
[769]6 *  $Id: string.c 3470 2009-05-19 00:51:47Z sam $
7 *
[1462]8 *  This library is free software. It comes without any warranty, to
[1452]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
[524]12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
[769]15/*
[668]16 *  This file contains various canvas handling functions such as character
17 *  and string drawing.
[524]18 */
19
20#include "config.h"
21
[568]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
[524]36#endif
37
[2821]38#include "caca.h"
39#include "caca_internals.h"
[524]40
[1342]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 *
[2821]49 *  \param cv A handle to the libcaca canvas.
[1342]50 *  \param x X cursor coordinate.
51 *  \param y Y cursor coordinate.
52 *  \return This function always returns 0.
53 */
[2821]54int caca_gotoxy(caca_canvas_t *cv, int x, int y)
[1342]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 *
[2821]68 *  \param cv A handle to the libcaca canvas.
[1342]69 *  \return The cursor's X coordinate.
70 */
[2821]71int caca_get_cursor_x(caca_canvas_t const *cv)
[1342]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 *
[2821]82 *  \param cv A handle to the libcaca canvas.
[1342]83 *  \return The cursor's Y coordinate.
84 */
[2821]85int caca_get_cursor_y(caca_canvas_t const *cv)
[1342]86{
87    return cv->frames[cv->frame].y;
88}
89
[958]90/** \brief Print an ASCII or Unicode character.
[524]91 *
[1231]92 *  Print an ASCII or Unicode character at the given coordinates, using
93 *  the default foreground and background colour values.
[524]94 *
[958]95 *  If the coordinates are outside the canvas boundaries, nothing is printed.
[1231]96 *  If a fullwidth Unicode character gets overwritten, its remaining visible
97 *  parts are replaced with spaces. If the canvas' boundaries would split the
[1215]98 *  fullwidth character in two, a space is printed instead.
[958]99 *
[1215]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
[2821]102 *  character instead of an UTF-32 character, use the caca_put_str() function.
[1215]103 *
[870]104 *  This function never fails.
105 *
[2821]106 *  \param cv A handle to the libcaca canvas.
[524]107 *  \param x X coordinate.
108 *  \param y Y coordinate.
[810]109 *  \param ch The character to print.
[870]110 *  \return This function always returns 0.
[524]111 */
[2821]112int caca_put_char(caca_canvas_t *cv, int x, int y, uint32_t ch)
[524]113{
[1215]114    uint32_t *curchar, *curattr, attr;
[3448]115    int fullwidth, xmin, xmax;
[1215]116
[1218]117    if(x >= (int)cv->width || y < 0 || y >= (int)cv->height)
[874]118        return 0;
[524]119
[2821]120    if(ch == CACA_MAGIC_FULLWIDTH)
[1218]121        return 0;
122
[2821]123    fullwidth = caca_utf32_is_fullwidth(ch);
[583]124
[1215]125    if(x == -1 && fullwidth)
126    {
127        x = 0;
128        ch = ' ';
129        fullwidth = 0;
130    }
131    else if(x < 0)
132        return 0;
[874]133
[1215]134    curchar = cv->chars + x + y * cv->width;
[1254]135    curattr = cv->attrs + x + y * cv->width;
136    attr = cv->curattr;
[1215]137
[3448]138    xmin = xmax = x;
139
[1215]140    /* When overwriting the right part of a fullwidth character,
141     * replace its left part with a space. */
[2821]142    if(x && curchar[0] == CACA_MAGIC_FULLWIDTH)
[3448]143    {
[1215]144        curchar[-1] = ' ';
[3448]145        xmin--;
146    }
[1215]147
148    if(fullwidth)
149    {
150        if(x + 1 == (int)cv->width)
151            ch = ' ';
152        else
153        {
[3448]154            xmax++;
155
[1215]156            /* When overwriting the left part of a fullwidth character,
157             * replace its right part with a space. */
[2821]158            if(x + 2 < (int)cv->width && curchar[2] == CACA_MAGIC_FULLWIDTH)
[3448]159            {
[1215]160                curchar[2] = ' ';
[3448]161                xmax++;
162            }
[1215]163
[2821]164            curchar[1] = CACA_MAGIC_FULLWIDTH;
[1215]165            curattr[1] = attr;
166        }
167    }
168    else
169    {
170        /* When overwriting the left part of a fullwidth character,
171         * replace its right part with a space. */
[2821]172        if(x + 1 != (int)cv->width && curchar[1] == CACA_MAGIC_FULLWIDTH)
[3450]173        {
[1215]174            curchar[1] = ' ';
[3450]175            xmax++;
176        }
[1215]177    }
178
[3450]179    caca_add_dirty_rectangle(cv, xmin, y, xmax, y);
[3448]180
[1215]181    curchar[0] = ch;
182    curattr[0] = attr;
183
[874]184    return 0;
[524]185}
186
[1066]187/** \brief Get the Unicode character at the given coordinates.
188 *
[1231]189 *  Get the ASCII or Unicode value of the character at the given
190 *  coordinates. If the value is less or equal to 127 (0x7f),
[1066]191 *  the character can be printed as ASCII. Otherise, it must be handled
192 *  as a UTF-32 value.
193 *
194 *  If the coordinates are outside the canvas boundaries, a space (0x20)
[1221]195 *  is returned.
[1066]196 *
[2821]197 *  A special exception is when CACA_MAGIC_FULLWIDTH is returned. This
[1221]198 *  value is guaranteed not to be a valid Unicode character, and indicates
199 *  that the character at the left of the requested one is a fullwidth
200 *  character.
201 *
[1066]202 *  This function never fails.
203 *
[2821]204 *  \param cv A handle to the libcaca canvas.
[1066]205 *  \param x X coordinate.
206 *  \param y Y coordinate.
[1231]207 *  \return This function always returns 0.
[1066]208 */
[2821]209uint32_t caca_get_char(caca_canvas_t const *cv, int x, int y)
[1066]210{
211    if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
[1215]212        return ' ';
[1066]213
[2303]214    return cv->chars[x + y * cv->width];
[1066]215}
216
[524]217/** \brief Print a string.
218 *
[1231]219 *  Print an UTF-8 string at the given coordinates, using the default
220 *  foreground and background values. The coordinates may be outside the
221 *  canvas boundaries (eg. a negative Y coordinate) and the string will
[524]222 *  be cropped accordingly if it is too long.
223 *
[2821]224 *  See caca_put_char() for more information on how fullwidth characters
[1215]225 *  are handled when overwriting each other or at the canvas' boundaries.
226 *
[870]227 *  This function never fails.
228 *
[2821]229 *  \param cv A handle to the libcaca canvas.
[524]230 *  \param x X coordinate.
231 *  \param y Y coordinate.
232 *  \param s The string to print.
[870]233 *  \return This function always returns 0.
[524]234 */
[2821]235int caca_put_str(caca_canvas_t *cv, int x, int y, char const *s)
[524]236{
[2304]237    size_t rd;
[524]238
[811]239    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
[874]240        return 0;
[524]241
[1215]242    while(*s && x < -1)
[524]243    {
[2821]244        x += caca_utf32_is_fullwidth(caca_utf8_to_utf32(s, &rd)) ? 2 : 1;
[1438]245        s += rd;
[524]246    }
247
[1215]248    while(*s && x < (int)cv->width)
[524]249    {
[2821]250        uint32_t ch = caca_utf8_to_utf32(s, &rd);
251        caca_put_char(cv, x, y, ch);
252        x += caca_utf32_is_fullwidth(ch) ? 2 : 1;
[1438]253        s += rd;
[524]254    }
[874]255
256    return 0;
[524]257}
258
[773]259/** \brief Print a formated string.
[524]260 *
[1231]261 *  Format a string at the given coordinates, using the default foreground
262 *  and background values. The coordinates may be outside the canvas
263 *  boundaries (eg. a negative Y coordinate) and the string will be cropped
264 *  accordingly if it is too long. The syntax of the format string is the
265 *  same as for the C printf() function.
[524]266 *
[870]267 *  This function never fails.
268 *
[2821]269 *  \param cv A handle to the libcaca canvas.
[524]270 *  \param x X coordinate.
271 *  \param y Y coordinate.
272 *  \param format The format string to print.
273 *  \param ... Arguments to the format string.
[870]274 *  \return This function always returns 0.
[524]275 */
[2821]276int caca_printf(caca_canvas_t *cv, int x, int y, char const *format, ...)
[524]277{
278    char tmp[BUFSIZ];
279    char *buf = tmp;
280    va_list args;
281
[811]282    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
[874]283        return 0;
[524]284
[811]285    if(cv->width - x + 1 > BUFSIZ)
286        buf = malloc(cv->width - x + 1);
[524]287
288    va_start(args, format);
289#if defined(HAVE_VSNPRINTF)
[811]290    vsnprintf(buf, cv->width - x + 1, format, args);
[524]291#else
292    vsprintf(buf, format, args);
293#endif
[811]294    buf[cv->width - x] = '\0';
[524]295    va_end(args);
296
[2821]297    caca_put_str(cv, x, y, buf);
[524]298
299    if(buf != tmp)
300        free(buf);
[874]301
302    return 0;
[524]303}
304
[773]305/** \brief Clear the canvas.
[524]306 *
[1231]307 *  Clear the canvas using the current foreground and background colours.
[814]308 *
[870]309 *  This function never fails.
310 *
[814]311 *  \param cv The canvas to clear.
[870]312 *  \return This function always returns 0.
[524]313 */
[2821]314int caca_clear_canvas(caca_canvas_t *cv)
[524]315{
[1254]316    uint32_t attr = cv->curattr;
[2305]317    int n;
[524]318
[832]319    for(n = cv->width * cv->height; n--; )
320    {
321        cv->chars[n] = (uint32_t)' ';
[1254]322        cv->attrs[n] = attr;
[832]323    }
[874]324
[3470]325    caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
[3448]326
[874]327    return 0;
[524]328}
329
[1342]330/** \brief Set cursor handle.
331 *
332 *  Set the canvas' handle. Blitting functions will use the handle value
333 *  to put the canvas at the proper coordinates.
334 *
335 *  This function never fails.
336 *
[2821]337 *  \param cv A handle to the libcaca canvas.
[1342]338 *  \param x X handle coordinate.
339 *  \param y Y handle coordinate.
340 *  \return This function always returns 0.
341 */
[2821]342int caca_set_canvas_handle(caca_canvas_t *cv, int x, int y)
[1342]343{
344    cv->frames[cv->frame].handlex = x;
345    cv->frames[cv->frame].handley = y;
346
347    return 0;
348}
349
350/** \brief Get X handle position.
351 *
352 *  Retrieve the X coordinate of the canvas' handle.
353 *
354 *  This function never fails.
355 *
[2821]356 *  \param cv A handle to the libcaca canvas.
[1342]357 *  \return The canvas' handle's X coordinate.
358 */
[2821]359int caca_get_canvas_handle_x(caca_canvas_t const *cv)
[1342]360{
361    return cv->frames[cv->frame].handlex;
362}
363
364/** \brief Get Y handle position.
365 *
366 *  Retrieve the Y coordinate of the canvas' handle.
367 *
368 *  This function never fails.
369 *
[2821]370 *  \param cv A handle to the libcaca canvas.
[1342]371 *  \return The canvas' handle's Y coordinate.
372 */
[2821]373int caca_get_canvas_handle_y(caca_canvas_t const *cv)
[1342]374{
375    return cv->frames[cv->frame].handley;
376}
377
[670]378/** \brief Blit a canvas onto another one.
379 *
[1231]380 *  Blit a canvas onto another one at the given coordinates.
[670]381 *  An optional mask canvas can be used.
382 *
[874]383 *  If an error occurs, -1 is returned and \b errno is set accordingly:
384 *  - \c EINVAL A mask was specified but the mask size and source canvas
385 *    size do not match.
386 *
[670]387 *  \param dst The destination canvas.
388 *  \param x X coordinate.
389 *  \param y Y coordinate.
390 *  \param src The source canvas.
391 *  \param mask The mask canvas.
[874]392 *  \return 0 in case of success, -1 if an error occurred.
[670]393 */
[2821]394int caca_blit(caca_canvas_t *dst, int x, int y,
[3448]395              caca_canvas_t const *src, caca_canvas_t const *mask)
[670]396{
[3448]397    int i, j, starti, startj, endi, endj, stride, bleed_left, bleed_right;
[670]398
[671]399    if(mask && (src->width != mask->width || src->height != mask->height))
[870]400    {
[1362]401        seterrno(EINVAL);
[870]402        return -1;
403    }
[670]404
[1346]405    x -= src->frames[src->frame].handlex;
406    y -= src->frames[src->frame].handley;
407
[670]408    starti = x < 0 ? -x : 0;
409    startj = y < 0 ? -y : 0;
410    endi = (x + src->width >= dst->width) ? dst->width - x : src->width;
411    endj = (y + src->height >= dst->height) ? dst->height - y : src->height;
[3448]412    stride = endi - starti;
[670]413
[2305]414    if(starti > src->width || startj > src->height
[988]415        || starti >= endi || startj >= endj)
[870]416        return 0;
[671]417
[3448]418    bleed_left = bleed_right = 0;
419
[670]420    for(j = startj; j < endj; j++)
421    {
[2305]422        int dstix = (j + y) * dst->width + starti + x;
423        int srcix = j * src->width + starti;
[1224]424
425        /* FIXME: we are ignoring the mask here */
[2821]426        if((starti + x) && dst->chars[dstix] == CACA_MAGIC_FULLWIDTH)
[3448]427        {
[1224]428            dst->chars[dstix - 1] = ' ';
[3448]429            bleed_left = 1;
430        }
[1224]431
[2305]432        if(endi + x < dst->width
[2821]433                && dst->chars[dstix + stride] == CACA_MAGIC_FULLWIDTH)
[3448]434        {
[1224]435            dst->chars[dstix + stride] = ' ';
[3448]436            bleed_right = 1;
437        }
[1224]438
[671]439        if(mask)
[670]440        {
[1224]441            for(i = 0; i < stride; i++)
[671]442            {
[1224]443                if(mask->chars[srcix + i] == (uint32_t)' ')
[671]444                    continue;
[670]445
[1224]446                dst->chars[dstix + i] = src->chars[srcix + i];
[1254]447                dst->attrs[dstix + i] = src->attrs[srcix + i];
[671]448            }
[670]449        }
[671]450        else
451        {
[1254]452            memcpy(dst->chars + dstix, src->chars + srcix, stride * 4);
453            memcpy(dst->attrs + dstix, src->attrs + srcix, stride * 4);
[671]454        }
[1224]455
456        /* Fix split fullwidth chars */
[2821]457        if(src->chars[srcix] == CACA_MAGIC_FULLWIDTH)
[1224]458            dst->chars[dstix] = ' ';
459
[2821]460        if(endi < src->width && src->chars[endi] == CACA_MAGIC_FULLWIDTH)
[1224]461            dst->chars[dstix + stride - 1] = ' ';
[670]462    }
[870]463
[3448]464    caca_add_dirty_rectangle(dst, starti + x - bleed_left, startj + y,
465                             endi + x - 1 + bleed_right, endj + y - 1);
466
[870]467    return 0;
[670]468}
469
[1079]470/** \brief Set a canvas' new boundaries.
471 *
[1231]472 *  Set new boundaries for a canvas. This function can be used to crop a
[1341]473 *  canvas, to expand it or for combinations of both actions. All frames
474 *  are affected by this function.
[1079]475 *
476 *  If an error occurs, -1 is returned and \b errno is set accordingly:
[2305]477 *  - \c EINVAL Specified width or height is invalid.
[1079]478 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
479 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
480 *    happens, the canvas handle becomes invalid and should not be used.
481 *
482 *  \param cv The canvas to crop.
483 *  \param x X coordinate of the top-left corner.
484 *  \param y Y coordinate of the top-left corner.
485 *  \param w The width of the cropped area.
486 *  \param h The height of the cropped area.
487 *  \return 0 in case of success, -1 if an error occurred.
488 */
[2821]489int caca_set_canvas_boundaries(caca_canvas_t *cv, int x, int y, int w, int h)
[1079]490{
[2821]491    caca_canvas_t *new;
[2305]492    int f, saved_f, framecount;
[1079]493
494    if(cv->refcount)
495    {
[1362]496        seterrno(EBUSY);
[1079]497        return -1;
498    }
499
[2305]500    if(w < 0 || h < 0)
501    {
502        seterrno(EINVAL);
503        return -1;
504    }
505
[2821]506    new = caca_create_canvas(w, h);
[1079]507
[2821]508    framecount = caca_get_frame_count(cv);
[1079]509    saved_f = cv->frame;
510
511    for(f = 0; f < framecount; f++)
512    {
513        if(f)
[2821]514            caca_create_frame(new, framecount);
[1079]515
[2821]516        caca_set_frame(cv, f);
517        caca_set_frame(new, f);
518        caca_blit(new, -x, -y, cv, NULL);
[1338]519        free(cv->frames[f].chars);
520        free(cv->frames[f].attrs);
[1079]521    }
[1338]522    free(cv->frames);
[1079]523
[2110]524    cv->frames = new->frames;
[1079]525    free(new);
526
[2821]527    caca_set_frame(cv, saved_f);
528    _caca_load_frame_info(cv);
529
[3448]530    /* FIXME: this may be optimised somewhat */
[3470]531    caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
[3448]532
[1079]533    return 0;
534}
535
[2826]536/*
537 * XXX: The following functions are aliases.
538 */
539
540int cucul_gotoxy(cucul_canvas_t *, int, int) CACA_ALIAS(caca_gotoxy);
541int cucul_get_cursor_x(cucul_canvas_t const *) CACA_ALIAS(caca_get_cursor_x);
542int cucul_get_cursor_y(cucul_canvas_t const *) CACA_ALIAS(caca_get_cursor_y);
543int cucul_put_char(cucul_canvas_t *, int, int, uint32_t)
544         CACA_ALIAS(caca_put_char);
545uint32_t cucul_get_char(cucul_canvas_t const *, int, int)
546         CACA_ALIAS(caca_get_char);
547int cucul_put_str(cucul_canvas_t *, int, int, char const *)
548         CACA_ALIAS(caca_put_str);
549int cucul_printf(cucul_canvas_t *, int, int, char const *, ...)
550         CACA_ALIAS(caca_printf);
551int cucul_clear_canvas(cucul_canvas_t *) CACA_ALIAS(caca_clear_canvas);
552int cucul_set_canvas_handle(cucul_canvas_t *, int, int)
553         CACA_ALIAS(caca_set_canvas_handle);
554int cucul_get_canvas_handle_x(cucul_canvas_t const *)
555         CACA_ALIAS(caca_get_canvas_handle_x);
556int cucul_get_canvas_handle_y(cucul_canvas_t const *)
557         CACA_ALIAS(caca_get_canvas_handle_y);
558int cucul_blit(cucul_canvas_t *, int, int, cucul_canvas_t const *,
559                        cucul_canvas_t const *) CACA_ALIAS(caca_blit);
560int cucul_set_canvas_boundaries(cucul_canvas_t *, int, int, int, int)
561         CACA_ALIAS(caca_set_canvas_boundaries);
562
Note: See TracBrowser for help on using the repository browser.