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

Last change on this file since 3559 was 3559, checked in by Pascal Terjan, 10 years ago
  • Add dirty rectangles when moving cursor
  • Property svn:keywords set to Id
File size: 16.4 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2009 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  $Id: string.c 3559 2009-07-20 19:32:37Z pterjan $
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    /* FIXME Not needed if cursor is invisible */
57    caca_add_dirty_rect(cv, cv->frames[cv->frame].x, cv->frames[cv->frame].y, 1, 1);
58    caca_add_dirty_rect(cv, x, y, 1, 1);
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_get_cursor_x(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_get_cursor_y(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 never fails.
109 *
110 *  \param cv A handle to the libcaca canvas.
111 *  \param x X coordinate.
112 *  \param y Y coordinate.
113 *  \param ch The character to print.
114 *  \return This function always returns 0.
115 */
116int caca_put_char(caca_canvas_t *cv, int x, int y, uint32_t ch)
117{
118    uint32_t *curchar, *curattr, attr;
119    int fullwidth, xmin, xmax;
120
121    if(x >= (int)cv->width || y < 0 || y >= (int)cv->height)
122        return 0;
123
124    if(ch == CACA_MAGIC_FULLWIDTH)
125        return 0;
126
127    fullwidth = caca_utf32_is_fullwidth(ch);
128
129    if(x == -1 && fullwidth)
130    {
131        x = 0;
132        ch = ' ';
133        fullwidth = 0;
134    }
135    else if(x < 0)
136        return 0;
137
138    curchar = cv->chars + x + y * cv->width;
139    curattr = cv->attrs + x + y * cv->width;
140    attr = cv->curattr;
141
142    xmin = xmax = x;
143
144    /* When overwriting the right part of a fullwidth character,
145     * replace its left part with a space. */
146    if(x && curchar[0] == CACA_MAGIC_FULLWIDTH)
147    {
148        curchar[-1] = ' ';
149        xmin--;
150    }
151
152    if(fullwidth)
153    {
154        if(x + 1 == (int)cv->width)
155            ch = ' ';
156        else
157        {
158            xmax++;
159
160            /* When overwriting the left part of a fullwidth character,
161             * replace its right part with a space. */
162            if(x + 2 < (int)cv->width && curchar[2] == CACA_MAGIC_FULLWIDTH)
163            {
164                curchar[2] = ' ';
165                xmax++;
166            }
167
168            curchar[1] = CACA_MAGIC_FULLWIDTH;
169            curattr[1] = attr;
170        }
171    }
172    else
173    {
174        /* When overwriting the left part of a fullwidth character,
175         * replace its right part with a space. */
176        if(x + 1 != (int)cv->width && curchar[1] == CACA_MAGIC_FULLWIDTH)
177        {
178            curchar[1] = ' ';
179            xmax++;
180        }
181    }
182
183    /* Only add a dirty rectangle if we are pasting a different character
184     * or attribute at that place. This does not account for inconsistencies
185     * in the canvas, ie. if CACA_MAGIC_FULLWIDTH lies at illegal places,
186     * but it's the caller's responsibility not to corrupt the contents. */
187    if(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 0;
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 never fails.
237 *
238 *  \param cv A handle to the libcaca canvas.
239 *  \param x X coordinate.
240 *  \param y Y coordinate.
241 *  \param s The string to print.
242 *  \return This function always returns 0.
243 */
244int caca_put_str(caca_canvas_t *cv, int x, int y, char const *s)
245{
246    size_t rd;
247
248    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
249        return 0;
250
251    while(*s && x < -1)
252    {
253        x += caca_utf32_is_fullwidth(caca_utf8_to_utf32(s, &rd)) ? 2 : 1;
254        s += rd;
255    }
256
257    while(*s && x < (int)cv->width)
258    {
259        uint32_t ch = caca_utf8_to_utf32(s, &rd);
260        caca_put_char(cv, x, y, ch);
261        x += caca_utf32_is_fullwidth(ch) ? 2 : 1;
262        s += rd;
263    }
264
265    return 0;
266}
267
268/** \brief Print a formated string.
269 *
270 *  Format a string at the given coordinates, using the default foreground
271 *  and background values. The coordinates may be outside the canvas
272 *  boundaries (eg. a negative Y coordinate) and the string will be cropped
273 *  accordingly if it is too long. The syntax of the format string is the
274 *  same as for the C printf() function.
275 *
276 *  This function never fails.
277 *
278 *  \param cv A handle to the libcaca canvas.
279 *  \param x X coordinate.
280 *  \param y Y coordinate.
281 *  \param format The format string to print.
282 *  \param ... Arguments to the format string.
283 *  \return This function always returns 0.
284 */
285int caca_printf(caca_canvas_t *cv, int x, int y, char const *format, ...)
286{
287    char tmp[BUFSIZ];
288    char *buf = tmp;
289    va_list args;
290
291    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
292        return 0;
293
294    if(cv->width - x + 1 > BUFSIZ)
295        buf = malloc(cv->width - x + 1);
296
297    va_start(args, format);
298#if defined(HAVE_VSNPRINTF)
299    vsnprintf(buf, cv->width - x + 1, format, args);
300#else
301    vsprintf(buf, format, args);
302#endif
303    buf[cv->width - x] = '\0';
304    va_end(args);
305
306    caca_put_str(cv, x, y, buf);
307
308    if(buf != tmp)
309        free(buf);
310
311    return 0;
312}
313
314/** \brief Clear the canvas.
315 *
316 *  Clear the canvas using the current foreground and background colours.
317 *
318 *  This function never fails.
319 *
320 *  \param cv The canvas to clear.
321 *  \return This function always returns 0.
322 */
323int caca_clear_canvas(caca_canvas_t *cv)
324{
325    uint32_t attr = cv->curattr;
326    int n;
327
328    for(n = cv->width * cv->height; n--; )
329    {
330        cv->chars[n] = (uint32_t)' ';
331        cv->attrs[n] = attr;
332    }
333
334    caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
335
336    return 0;
337}
338
339/** \brief Set cursor handle.
340 *
341 *  Set the canvas' handle. Blitting functions will use the handle value
342 *  to put the canvas at the proper coordinates.
343 *
344 *  This function never fails.
345 *
346 *  \param cv A handle to the libcaca canvas.
347 *  \param x X handle coordinate.
348 *  \param y Y handle coordinate.
349 *  \return This function always returns 0.
350 */
351int caca_set_canvas_handle(caca_canvas_t *cv, int x, int y)
352{
353    cv->frames[cv->frame].handlex = x;
354    cv->frames[cv->frame].handley = y;
355
356    return 0;
357}
358
359/** \brief Get X handle position.
360 *
361 *  Retrieve the X coordinate of the canvas' handle.
362 *
363 *  This function never fails.
364 *
365 *  \param cv A handle to the libcaca canvas.
366 *  \return The canvas' handle's X coordinate.
367 */
368int caca_get_canvas_handle_x(caca_canvas_t const *cv)
369{
370    return cv->frames[cv->frame].handlex;
371}
372
373/** \brief Get Y handle position.
374 *
375 *  Retrieve the Y coordinate of the canvas' handle.
376 *
377 *  This function never fails.
378 *
379 *  \param cv A handle to the libcaca canvas.
380 *  \return The canvas' handle's Y coordinate.
381 */
382int caca_get_canvas_handle_y(caca_canvas_t const *cv)
383{
384    return cv->frames[cv->frame].handley;
385}
386
387/** \brief Blit a canvas onto another one.
388 *
389 *  Blit a canvas onto another one at the given coordinates.
390 *  An optional mask canvas can be used.
391 *
392 *  If an error occurs, -1 is returned and \b errno is set accordingly:
393 *  - \c EINVAL A mask was specified but the mask size and source canvas
394 *    size do not match.
395 *
396 *  \param dst The destination canvas.
397 *  \param x X coordinate.
398 *  \param y Y coordinate.
399 *  \param src The source canvas.
400 *  \param mask The mask canvas.
401 *  \return 0 in case of success, -1 if an error occurred.
402 */
403int caca_blit(caca_canvas_t *dst, int x, int y,
404              caca_canvas_t const *src, caca_canvas_t const *mask)
405{
406    int i, j, starti, startj, endi, endj, stride, bleed_left, bleed_right;
407
408    if(mask && (src->width != mask->width || src->height != mask->height))
409    {
410        seterrno(EINVAL);
411        return -1;
412    }
413
414    x -= src->frames[src->frame].handlex;
415    y -= src->frames[src->frame].handley;
416
417    starti = x < 0 ? -x : 0;
418    startj = y < 0 ? -y : 0;
419    endi = (x + src->width >= dst->width) ? dst->width - x : src->width;
420    endj = (y + src->height >= dst->height) ? dst->height - y : src->height;
421    stride = endi - starti;
422
423    if(starti > src->width || startj > src->height
424        || starti >= endi || startj >= endj)
425        return 0;
426
427    bleed_left = bleed_right = 0;
428
429    for(j = startj; j < endj; j++)
430    {
431        int dstix = (j + y) * dst->width + starti + x;
432        int srcix = j * src->width + starti;
433
434        /* FIXME: we are ignoring the mask here */
435        if((starti + x) && dst->chars[dstix] == CACA_MAGIC_FULLWIDTH)
436        {
437            dst->chars[dstix - 1] = ' ';
438            bleed_left = 1;
439        }
440
441        if(endi + x < dst->width
442                && dst->chars[dstix + stride] == CACA_MAGIC_FULLWIDTH)
443        {
444            dst->chars[dstix + stride] = ' ';
445            bleed_right = 1;
446        }
447
448        if(mask)
449        {
450            for(i = 0; i < stride; i++)
451            {
452                if(mask->chars[srcix + i] == (uint32_t)' ')
453                    continue;
454
455                if(dst->chars[dstix + i] != src->chars[srcix + i] ||
456                   dst->attrs[dstix + i] != src->attrs[srcix + i])
457                {
458                    dst->chars[dstix + i] = src->chars[srcix + i];
459                    dst->attrs[dstix + i] = src->attrs[srcix + i];
460                    caca_add_dirty_rect(dst, x + starti + i, y + j, 1, 1);
461                }
462            }
463        }
464        else
465        {
466            if(memcmp(dst->chars + dstix, src->chars + srcix, stride * 4) ||
467               memcmp(dst->attrs + dstix, src->attrs + srcix, stride * 4))
468            {
469                /* FIXME be more precise ? */
470                memcpy(dst->chars + dstix, src->chars + srcix, stride * 4);
471                memcpy(dst->attrs + dstix, src->attrs + srcix, stride * 4);
472                caca_add_dirty_rect(dst, x + starti, y + j, stride, 1);
473            }
474        }
475
476        /* Fix split fullwidth chars */
477        if(src->chars[srcix] == CACA_MAGIC_FULLWIDTH)
478            dst->chars[dstix] = ' ';
479
480        if(endi < src->width && src->chars[endi] == CACA_MAGIC_FULLWIDTH)
481            dst->chars[dstix + stride - 1] = ' ';
482    }
483
484
485    return 0;
486}
487
488/** \brief Set a canvas' new boundaries.
489 *
490 *  Set new boundaries for a canvas. This function can be used to crop a
491 *  canvas, to expand it or for combinations of both actions. All frames
492 *  are affected by this function.
493 *
494 *  If an error occurs, -1 is returned and \b errno is set accordingly:
495 *  - \c EINVAL Specified width or height is invalid.
496 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
497 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
498 *    happens, the canvas handle becomes invalid and should not be used.
499 *
500 *  \param cv The canvas to crop.
501 *  \param x X coordinate of the top-left corner.
502 *  \param y Y coordinate of the top-left corner.
503 *  \param w The width of the cropped area.
504 *  \param h The height of the cropped area.
505 *  \return 0 in case of success, -1 if an error occurred.
506 */
507int caca_set_canvas_boundaries(caca_canvas_t *cv, int x, int y, int w, int h)
508{
509    caca_canvas_t *new;
510    int f, saved_f, framecount;
511
512    if(cv->refcount)
513    {
514        seterrno(EBUSY);
515        return -1;
516    }
517
518    if(w < 0 || h < 0)
519    {
520        seterrno(EINVAL);
521        return -1;
522    }
523
524    new = caca_create_canvas(w, h);
525
526    framecount = caca_get_frame_count(cv);
527    saved_f = cv->frame;
528
529    for(f = 0; f < framecount; f++)
530    {
531        if(f)
532            caca_create_frame(new, framecount);
533
534        caca_set_frame(cv, f);
535        caca_set_frame(new, f);
536        caca_blit(new, -x, -y, cv, NULL);
537        free(cv->frames[f].chars);
538        free(cv->frames[f].attrs);
539    }
540    free(cv->frames);
541
542    cv->frames = new->frames;
543    free(new);
544
545    caca_set_frame(cv, saved_f);
546    _caca_load_frame_info(cv);
547
548    /* FIXME: this may be optimised somewhat */
549    caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
550
551    return 0;
552}
553
554/*
555 * XXX: The following functions are aliases.
556 */
557
558int cucul_gotoxy(cucul_canvas_t *, int, int) CACA_ALIAS(caca_gotoxy);
559int cucul_get_cursor_x(cucul_canvas_t const *) CACA_ALIAS(caca_get_cursor_x);
560int cucul_get_cursor_y(cucul_canvas_t const *) CACA_ALIAS(caca_get_cursor_y);
561int cucul_put_char(cucul_canvas_t *, int, int, uint32_t)
562         CACA_ALIAS(caca_put_char);
563uint32_t cucul_get_char(cucul_canvas_t const *, int, int)
564         CACA_ALIAS(caca_get_char);
565int cucul_put_str(cucul_canvas_t *, int, int, char const *)
566         CACA_ALIAS(caca_put_str);
567int cucul_printf(cucul_canvas_t *, int, int, char const *, ...)
568         CACA_ALIAS(caca_printf);
569int cucul_clear_canvas(cucul_canvas_t *) CACA_ALIAS(caca_clear_canvas);
570int cucul_set_canvas_handle(cucul_canvas_t *, int, int)
571         CACA_ALIAS(caca_set_canvas_handle);
572int cucul_get_canvas_handle_x(cucul_canvas_t const *)
573         CACA_ALIAS(caca_get_canvas_handle_x);
574int cucul_get_canvas_handle_y(cucul_canvas_t const *)
575         CACA_ALIAS(caca_get_canvas_handle_y);
576int cucul_blit(cucul_canvas_t *, int, int, cucul_canvas_t const *,
577                        cucul_canvas_t const *) CACA_ALIAS(caca_blit);
578int cucul_set_canvas_boundaries(cucul_canvas_t *, int, int, int, int)
579         CACA_ALIAS(caca_set_canvas_boundaries);
580
Note: See TracBrowser for help on using the repository browser.