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

Last change on this file since 3448 was 3448, checked in by Sam Hocevar, 13 years ago

Finish the dirty rectangle architecture. They are now reliable in the sense
that anything outside the dirty rectangle is guaranteed to be unchanged, but
we currently mark far too many cells as dirty. This must be optimised.

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