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

Last change on this file since 3485 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
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 3470 2009-05-19 00:51:47Z 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    caca_add_dirty_rectangle(cv, xmin, y, xmax, y);
180
181    curchar[0] = ch;
182    curattr[0] = attr;
183
184    return 0;
185}
186
187/** \brief Get the Unicode character at the given coordinates.
188 *
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),
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)
195 *  is returned.
196 *
197 *  A special exception is when CACA_MAGIC_FULLWIDTH is returned. This
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 *
202 *  This function never fails.
203 *
204 *  \param cv A handle to the libcaca canvas.
205 *  \param x X coordinate.
206 *  \param y Y coordinate.
207 *  \return This function always returns 0.
208 */
209uint32_t caca_get_char(caca_canvas_t const *cv, int x, int y)
210{
211    if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
212        return ' ';
213
214    return cv->chars[x + y * cv->width];
215}
216
217/** \brief Print a string.
218 *
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
222 *  be cropped accordingly if it is too long.
223 *
224 *  See caca_put_char() for more information on how fullwidth characters
225 *  are handled when overwriting each other or at the canvas' boundaries.
226 *
227 *  This function never fails.
228 *
229 *  \param cv A handle to the libcaca canvas.
230 *  \param x X coordinate.
231 *  \param y Y coordinate.
232 *  \param s The string to print.
233 *  \return This function always returns 0.
234 */
235int caca_put_str(caca_canvas_t *cv, int x, int y, char const *s)
236{
237    size_t rd;
238
239    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
240        return 0;
241
242    while(*s && x < -1)
243    {
244        x += caca_utf32_is_fullwidth(caca_utf8_to_utf32(s, &rd)) ? 2 : 1;
245        s += rd;
246    }
247
248    while(*s && x < (int)cv->width)
249    {
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;
253        s += rd;
254    }
255
256    return 0;
257}
258
259/** \brief Print a formated string.
260 *
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.
266 *
267 *  This function never fails.
268 *
269 *  \param cv A handle to the libcaca canvas.
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.
274 *  \return This function always returns 0.
275 */
276int caca_printf(caca_canvas_t *cv, int x, int y, char const *format, ...)
277{
278    char tmp[BUFSIZ];
279    char *buf = tmp;
280    va_list args;
281
282    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
283        return 0;
284
285    if(cv->width - x + 1 > BUFSIZ)
286        buf = malloc(cv->width - x + 1);
287
288    va_start(args, format);
289#if defined(HAVE_VSNPRINTF)
290    vsnprintf(buf, cv->width - x + 1, format, args);
291#else
292    vsprintf(buf, format, args);
293#endif
294    buf[cv->width - x] = '\0';
295    va_end(args);
296
297    caca_put_str(cv, x, y, buf);
298
299    if(buf != tmp)
300        free(buf);
301
302    return 0;
303}
304
305/** \brief Clear the canvas.
306 *
307 *  Clear the canvas using the current foreground and background colours.
308 *
309 *  This function never fails.
310 *
311 *  \param cv The canvas to clear.
312 *  \return This function always returns 0.
313 */
314int caca_clear_canvas(caca_canvas_t *cv)
315{
316    uint32_t attr = cv->curattr;
317    int n;
318
319    for(n = cv->width * cv->height; n--; )
320    {
321        cv->chars[n] = (uint32_t)' ';
322        cv->attrs[n] = attr;
323    }
324
325    caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
326
327    return 0;
328}
329
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 *
337 *  \param cv A handle to the libcaca canvas.
338 *  \param x X handle coordinate.
339 *  \param y Y handle coordinate.
340 *  \return This function always returns 0.
341 */
342int caca_set_canvas_handle(caca_canvas_t *cv, int x, int y)
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 *
356 *  \param cv A handle to the libcaca canvas.
357 *  \return The canvas' handle's X coordinate.
358 */
359int caca_get_canvas_handle_x(caca_canvas_t const *cv)
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 *
370 *  \param cv A handle to the libcaca canvas.
371 *  \return The canvas' handle's Y coordinate.
372 */
373int caca_get_canvas_handle_y(caca_canvas_t const *cv)
374{
375    return cv->frames[cv->frame].handley;
376}
377
378/** \brief Blit a canvas onto another one.
379 *
380 *  Blit a canvas onto another one at the given coordinates.
381 *  An optional mask canvas can be used.
382 *
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 *
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.
392 *  \return 0 in case of success, -1 if an error occurred.
393 */
394int caca_blit(caca_canvas_t *dst, int x, int y,
395              caca_canvas_t const *src, caca_canvas_t const *mask)
396{
397    int i, j, starti, startj, endi, endj, stride, bleed_left, bleed_right;
398
399    if(mask && (src->width != mask->width || src->height != mask->height))
400    {
401        seterrno(EINVAL);
402        return -1;
403    }
404
405    x -= src->frames[src->frame].handlex;
406    y -= src->frames[src->frame].handley;
407
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;
412    stride = endi - starti;
413
414    if(starti > src->width || startj > src->height
415        || starti >= endi || startj >= endj)
416        return 0;
417
418    bleed_left = bleed_right = 0;
419
420    for(j = startj; j < endj; j++)
421    {
422        int dstix = (j + y) * dst->width + starti + x;
423        int srcix = j * src->width + starti;
424
425        /* FIXME: we are ignoring the mask here */
426        if((starti + x) && dst->chars[dstix] == CACA_MAGIC_FULLWIDTH)
427        {
428            dst->chars[dstix - 1] = ' ';
429            bleed_left = 1;
430        }
431
432        if(endi + x < dst->width
433                && dst->chars[dstix + stride] == CACA_MAGIC_FULLWIDTH)
434        {
435            dst->chars[dstix + stride] = ' ';
436            bleed_right = 1;
437        }
438
439        if(mask)
440        {
441            for(i = 0; i < stride; i++)
442            {
443                if(mask->chars[srcix + i] == (uint32_t)' ')
444                    continue;
445
446                dst->chars[dstix + i] = src->chars[srcix + i];
447                dst->attrs[dstix + i] = src->attrs[srcix + i];
448            }
449        }
450        else
451        {
452            memcpy(dst->chars + dstix, src->chars + srcix, stride * 4);
453            memcpy(dst->attrs + dstix, src->attrs + srcix, stride * 4);
454        }
455
456        /* Fix split fullwidth chars */
457        if(src->chars[srcix] == CACA_MAGIC_FULLWIDTH)
458            dst->chars[dstix] = ' ';
459
460        if(endi < src->width && src->chars[endi] == CACA_MAGIC_FULLWIDTH)
461            dst->chars[dstix + stride - 1] = ' ';
462    }
463
464    caca_add_dirty_rectangle(dst, starti + x - bleed_left, startj + y,
465                             endi + x - 1 + bleed_right, endj + y - 1);
466
467    return 0;
468}
469
470/** \brief Set a canvas' new boundaries.
471 *
472 *  Set new boundaries for a canvas. This function can be used to crop a
473 *  canvas, to expand it or for combinations of both actions. All frames
474 *  are affected by this function.
475 *
476 *  If an error occurs, -1 is returned and \b errno is set accordingly:
477 *  - \c EINVAL Specified width or height is invalid.
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 */
489int caca_set_canvas_boundaries(caca_canvas_t *cv, int x, int y, int w, int h)
490{
491    caca_canvas_t *new;
492    int f, saved_f, framecount;
493
494    if(cv->refcount)
495    {
496        seterrno(EBUSY);
497        return -1;
498    }
499
500    if(w < 0 || h < 0)
501    {
502        seterrno(EINVAL);
503        return -1;
504    }
505
506    new = caca_create_canvas(w, h);
507
508    framecount = caca_get_frame_count(cv);
509    saved_f = cv->frame;
510
511    for(f = 0; f < framecount; f++)
512    {
513        if(f)
514            caca_create_frame(new, framecount);
515
516        caca_set_frame(cv, f);
517        caca_set_frame(new, f);
518        caca_blit(new, -x, -y, cv, NULL);
519        free(cv->frames[f].chars);
520        free(cv->frames[f].attrs);
521    }
522    free(cv->frames);
523
524    cv->frames = new->frames;
525    free(new);
526
527    caca_set_frame(cv, saved_f);
528    _caca_load_frame_info(cv);
529
530    /* FIXME: this may be optimised somewhat */
531    caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
532
533    return 0;
534}
535
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.