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

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

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

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