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

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

Allow to temporarily disable dirty rectangle handling. This allows for huge
speedups when the calling application knows the dirty rectangle covered by
a complex operation.

  • 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 3583 2009-07-26 19:17:35Z 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        {
174            curchar[1] = ' ';
175            xmax++;
176        }
177    }
178
179    /* Only add a dirty rectangle if we are pasting a different character
180     * or attribute at that place. This does not account for inconsistencies
181     * in the canvas, ie. if CACA_MAGIC_FULLWIDTH lies at illegal places,
182     * but it's the caller's responsibility not to corrupt the contents. */
183    if(!cv->dirty_disabled
184        && (curchar[0] != ch || curattr[0] != attr))
185        caca_add_dirty_rect(cv, xmin, y, xmax - xmin + 1, 1);
186
187    curchar[0] = ch;
188    curattr[0] = attr;
189
190    return 0;
191}
192
193/** \brief Get the Unicode character at the given coordinates.
194 *
195 *  Get the ASCII or Unicode value of the character at the given
196 *  coordinates. If the value is less or equal to 127 (0x7f),
197 *  the character can be printed as ASCII. Otherise, it must be handled
198 *  as a UTF-32 value.
199 *
200 *  If the coordinates are outside the canvas boundaries, a space (0x20)
201 *  is returned.
202 *
203 *  A special exception is when CACA_MAGIC_FULLWIDTH is returned. This
204 *  value is guaranteed not to be a valid Unicode character, and indicates
205 *  that the character at the left of the requested one is a fullwidth
206 *  character.
207 *
208 *  This function never fails.
209 *
210 *  \param cv A handle to the libcaca canvas.
211 *  \param x X coordinate.
212 *  \param y Y coordinate.
213 *  \return The Unicode character at the given coordinates.
214 */
215uint32_t caca_get_char(caca_canvas_t const *cv, int x, int y)
216{
217    if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
218        return ' ';
219
220    return cv->chars[x + y * cv->width];
221}
222
223/** \brief Print a string.
224 *
225 *  Print an UTF-8 string at the given coordinates, using the default
226 *  foreground and background values. The coordinates may be outside the
227 *  canvas boundaries (eg. a negative Y coordinate) and the string will
228 *  be cropped accordingly if it is too long.
229 *
230 *  See caca_put_char() for more information on how fullwidth characters
231 *  are handled when overwriting each other or at the canvas' boundaries.
232 *
233 *  This function never fails.
234 *
235 *  \param cv A handle to the libcaca canvas.
236 *  \param x X coordinate.
237 *  \param y Y coordinate.
238 *  \param s The string to print.
239 *  \return This function always returns 0.
240 */
241int caca_put_str(caca_canvas_t *cv, int x, int y, char const *s)
242{
243    size_t rd;
244
245    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
246        return 0;
247
248    while(*s && x < -1)
249    {
250        x += caca_utf32_is_fullwidth(caca_utf8_to_utf32(s, &rd)) ? 2 : 1;
251        s += rd;
252    }
253
254    while(*s && x < (int)cv->width)
255    {
256        uint32_t ch = caca_utf8_to_utf32(s, &rd);
257        caca_put_char(cv, x, y, ch);
258        x += caca_utf32_is_fullwidth(ch) ? 2 : 1;
259        s += rd;
260    }
261
262    return 0;
263}
264
265/** \brief Print a formated string.
266 *
267 *  Format a string at the given coordinates, using the default foreground
268 *  and background values. The coordinates may be outside the canvas
269 *  boundaries (eg. a negative Y coordinate) and the string will be cropped
270 *  accordingly if it is too long. The syntax of the format string is the
271 *  same as for the C printf() function.
272 *
273 *  This function never fails.
274 *
275 *  \param cv A handle to the libcaca canvas.
276 *  \param x X coordinate.
277 *  \param y Y coordinate.
278 *  \param format The format string to print.
279 *  \param ... Arguments to the format string.
280 *  \return This function always returns 0.
281 */
282int caca_printf(caca_canvas_t *cv, int x, int y, char const *format, ...)
283{
284    char tmp[BUFSIZ];
285    char *buf = tmp;
286    va_list args;
287
288    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
289        return 0;
290
291    if(cv->width - x + 1 > BUFSIZ)
292        buf = malloc(cv->width - x + 1);
293
294    va_start(args, format);
295#if defined(HAVE_VSNPRINTF)
296    vsnprintf(buf, cv->width - x + 1, format, args);
297#else
298    vsprintf(buf, format, args);
299#endif
300    buf[cv->width - x] = '\0';
301    va_end(args);
302
303    caca_put_str(cv, x, y, buf);
304
305    if(buf != tmp)
306        free(buf);
307
308    return 0;
309}
310
311/** \brief Clear the canvas.
312 *
313 *  Clear the canvas using the current foreground and background colours.
314 *
315 *  This function never fails.
316 *
317 *  \param cv The canvas to clear.
318 *  \return This function always returns 0.
319 */
320int caca_clear_canvas(caca_canvas_t *cv)
321{
322    uint32_t attr = cv->curattr;
323    int n;
324
325    for(n = cv->width * cv->height; n--; )
326    {
327        cv->chars[n] = (uint32_t)' ';
328        cv->attrs[n] = attr;
329    }
330
331    if(!cv->dirty_disabled)
332        caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
333
334    return 0;
335}
336
337/** \brief Set cursor handle.
338 *
339 *  Set the canvas' handle. Blitting functions will use the handle value
340 *  to put the canvas at the proper coordinates.
341 *
342 *  This function never fails.
343 *
344 *  \param cv A handle to the libcaca canvas.
345 *  \param x X handle coordinate.
346 *  \param y Y handle coordinate.
347 *  \return This function always returns 0.
348 */
349int caca_set_canvas_handle(caca_canvas_t *cv, int x, int y)
350{
351    cv->frames[cv->frame].handlex = x;
352    cv->frames[cv->frame].handley = y;
353
354    return 0;
355}
356
357/** \brief Get X handle position.
358 *
359 *  Retrieve the X coordinate of the canvas' handle.
360 *
361 *  This function never fails.
362 *
363 *  \param cv A handle to the libcaca canvas.
364 *  \return The canvas' handle's X coordinate.
365 */
366int caca_get_canvas_handle_x(caca_canvas_t const *cv)
367{
368    return cv->frames[cv->frame].handlex;
369}
370
371/** \brief Get Y handle position.
372 *
373 *  Retrieve the Y coordinate of the canvas' handle.
374 *
375 *  This function never fails.
376 *
377 *  \param cv A handle to the libcaca canvas.
378 *  \return The canvas' handle's Y coordinate.
379 */
380int caca_get_canvas_handle_y(caca_canvas_t const *cv)
381{
382    return cv->frames[cv->frame].handley;
383}
384
385/** \brief Blit a canvas onto another one.
386 *
387 *  Blit a canvas onto another one at the given coordinates.
388 *  An optional mask canvas can be used.
389 *
390 *  If an error occurs, -1 is returned and \b errno is set accordingly:
391 *  - \c EINVAL A mask was specified but the mask size and source canvas
392 *    size do not match.
393 *
394 *  \param dst The destination canvas.
395 *  \param x X coordinate.
396 *  \param y Y coordinate.
397 *  \param src The source canvas.
398 *  \param mask The mask canvas.
399 *  \return 0 in case of success, -1 if an error occurred.
400 */
401int caca_blit(caca_canvas_t *dst, int x, int y,
402              caca_canvas_t const *src, caca_canvas_t const *mask)
403{
404    int i, j, starti, startj, endi, endj, stride, bleed_left, bleed_right;
405
406    if(mask && (src->width != mask->width || src->height != mask->height))
407    {
408        seterrno(EINVAL);
409        return -1;
410    }
411
412    x -= src->frames[src->frame].handlex;
413    y -= src->frames[src->frame].handley;
414
415    starti = x < 0 ? -x : 0;
416    startj = y < 0 ? -y : 0;
417    endi = (x + src->width >= dst->width) ? dst->width - x : src->width;
418    endj = (y + src->height >= dst->height) ? dst->height - y : src->height;
419    stride = endi - starti;
420
421    if(starti > src->width || startj > src->height
422        || starti >= endi || startj >= endj)
423        return 0;
424
425    bleed_left = bleed_right = 0;
426
427    for(j = startj; j < endj; j++)
428    {
429        int dstix = (j + y) * dst->width + starti + x;
430        int srcix = j * src->width + starti;
431
432        /* FIXME: we are ignoring the mask here */
433        if((starti + x) && dst->chars[dstix] == CACA_MAGIC_FULLWIDTH)
434        {
435            dst->chars[dstix - 1] = ' ';
436            bleed_left = 1;
437        }
438
439        if(endi + x < dst->width
440                && dst->chars[dstix + stride] == CACA_MAGIC_FULLWIDTH)
441        {
442            dst->chars[dstix + stride] = ' ';
443            bleed_right = 1;
444        }
445
446        if(mask)
447        {
448            for(i = 0; i < stride; i++)
449            {
450                if(mask->chars[srcix + i] == (uint32_t)' ')
451                    continue;
452
453                if(dst->chars[dstix + i] != src->chars[srcix + i] ||
454                   dst->attrs[dstix + i] != src->attrs[srcix + i])
455                {
456                    dst->chars[dstix + i] = src->chars[srcix + i];
457                    dst->attrs[dstix + i] = src->attrs[srcix + i];
458                    if(!dst->dirty_disabled)
459                        caca_add_dirty_rect(dst, x + starti + i, y + j, 1, 1);
460                }
461            }
462        }
463        else
464        {
465            if(memcmp(dst->chars + dstix, src->chars + srcix, stride * 4) ||
466               memcmp(dst->attrs + dstix, src->attrs + srcix, stride * 4))
467            {
468                /* FIXME be more precise ? */
469                memcpy(dst->chars + dstix, src->chars + srcix, stride * 4);
470                memcpy(dst->attrs + dstix, src->attrs + srcix, stride * 4);
471                if(!dst->dirty_disabled)
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    if(!cv->dirty_disabled)
550        caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
551
552    return 0;
553}
554
555/*
556 * XXX: The following functions are aliases.
557 */
558
559int cucul_gotoxy(cucul_canvas_t *, int, int) CACA_ALIAS(caca_gotoxy);
560int cucul_get_cursor_x(cucul_canvas_t const *) CACA_ALIAS(caca_get_cursor_x);
561int cucul_get_cursor_y(cucul_canvas_t const *) CACA_ALIAS(caca_get_cursor_y);
562int cucul_put_char(cucul_canvas_t *, int, int, uint32_t)
563         CACA_ALIAS(caca_put_char);
564uint32_t cucul_get_char(cucul_canvas_t const *, int, int)
565         CACA_ALIAS(caca_get_char);
566int cucul_put_str(cucul_canvas_t *, int, int, char const *)
567         CACA_ALIAS(caca_put_str);
568int cucul_printf(cucul_canvas_t *, int, int, char const *, ...)
569         CACA_ALIAS(caca_printf);
570int cucul_clear_canvas(cucul_canvas_t *) CACA_ALIAS(caca_clear_canvas);
571int cucul_set_canvas_handle(cucul_canvas_t *, int, int)
572         CACA_ALIAS(caca_set_canvas_handle);
573int cucul_get_canvas_handle_x(cucul_canvas_t const *)
574         CACA_ALIAS(caca_get_canvas_handle_x);
575int cucul_get_canvas_handle_y(cucul_canvas_t const *)
576         CACA_ALIAS(caca_get_canvas_handle_y);
577int cucul_blit(cucul_canvas_t *, int, int, cucul_canvas_t const *,
578                        cucul_canvas_t const *) CACA_ALIAS(caca_blit);
579int cucul_set_canvas_boundaries(cucul_canvas_t *, int, int, int, int)
580         CACA_ALIAS(caca_set_canvas_boundaries);
581
Note: See TracBrowser for help on using the repository browser.