source: libcaca/trunk/caca/codec/import.c @ 3495

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

Add caca_export_area_to_memory() to export only a selected part of the
current canvas. This is useful to export dirty rectangles. Accordingly,
add caca_import_area_from_memory() and caca_import_area_from_file().

  • Property svn:keywords set to Id
File size: 13.2 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: import.c 3495 2009-05-21 20:55:21Z 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 import functions.
17 */
18
19#include "config.h"
20
21#if !defined __KERNEL__
22#   include <stdlib.h>
23#   include <string.h>
24#   include <stdio.h>
25#endif
26
27#include "caca.h"
28#include "caca_internals.h"
29#include "codec.h"
30
31static inline uint32_t sscanu32(void const *s)
32{
33    uint32_t x;
34    memcpy(&x, s, 4);
35    return hton32(x);
36}
37
38static inline uint16_t sscanu16(void const *s)
39{
40    uint16_t x;
41    memcpy(&x, s, 2);
42    return hton16(x);
43}
44
45static ssize_t import_caca(caca_canvas_t *, void const *, size_t);
46
47/** \brief Import a memory buffer into a canvas
48 *
49 *  Import a memory buffer into the given libcaca canvas's current
50 *  frame. The current frame is resized accordingly and its contents are
51 *  replaced with the imported data.
52 *
53 *  Valid values for \c format are:
54 *  - \c "": attempt to autodetect the file format.
55 *  - \c "caca": import native libcaca files.
56 *  - \c "text": import ASCII text files.
57 *  - \c "ansi": import ANSI files.
58 *  - \c "utf8": import UTF-8 files with ANSI colour codes.
59 *
60 *  The number of bytes read is returned. If the file format is valid, but
61 *  not enough data was available, 0 is returned.
62 *
63 *  If an error occurs, -1 is returned and \b errno is set accordingly:
64 *  - \c ENOMEM Not enough memory to allocate canvas.
65 *  - \c EINVAL Invalid format requested.
66 *
67 *  \param cv A libcaca canvas in which to import the file.
68 *  \param data A memory area containing the data to be loaded into the canvas.
69 *  \param len The size in bytes of the memory area.
70 *  \param format A string describing the input format.
71 *  \return The number of bytes read, or 0 if there was not enough data,
72 *  or -1 if an error occurred.
73 */
74ssize_t caca_import_canvas_from_memory(caca_canvas_t *cv, void const *data,
75                                       size_t len, char const *format)
76{
77    if(!strcasecmp("caca", format))
78        return import_caca(cv, data, len);
79    if(!strcasecmp("utf8", format))
80        return _import_ansi(cv, data, len, 1);
81    if(!strcasecmp("text", format))
82        return _import_text(cv, data, len);
83    if(!strcasecmp("ansi", format))
84        return _import_ansi(cv, data, len, 0);
85
86    /* Autodetection */
87    if(!strcasecmp("", format))
88    {
89        unsigned char const *str = data;
90        unsigned int i;
91
92        /* If 4 first bytes are 0xcaca + 'CV' */
93        if(len >= 4 && str[0] == 0xca &&
94           str[1] == 0xca && str[2] == 'C' && str[3] == 'V')
95            return import_caca(cv, data, len);
96
97        /* If we find ESC[ argv, we guess it's an ANSI file */
98        for(i = 0; i + 1 < len; i++)
99            if((str[i] == '\033') && (str[i + 1] == '['))
100                return _import_ansi(cv, data, len, 0);
101
102        /* Otherwise, import it as text */
103        return _import_text(cv, data, len);
104    }
105
106    seterrno(EINVAL);
107    return -1;
108}
109
110/** \brief Import a file into a canvas
111 *
112 *  Import a file into the given libcaca canvas's current frame. The
113 *  current frame is resized accordingly and its contents are replaced
114 *  with the imported data.
115 *
116 *  Valid values for \c format are:
117 *  - \c "": attempt to autodetect the file format.
118 *  - \c "caca": import native libcaca files.
119 *  - \c "text": import ASCII text files.
120 *  - \c "ansi": import ANSI files.
121 *  - \c "utf8": import UTF-8 files with ANSI colour codes.
122 *
123 *  The number of bytes read is returned. If the file format is valid, but
124 *  not enough data was available, 0 is returned.
125 *
126 *  If an error occurs, -1 is returned and \b errno is set accordingly:
127 *  - \c ENOSYS File access is not implemented on this system.
128 *  - \c ENOMEM Not enough memory to allocate canvas.
129 *  - \c EINVAL Invalid format requested.
130 *  caca_import_file() may also fail and set \b errno for any of the
131 *  errors specified for the routine fopen().
132 *
133 *  \param cv A libcaca canvas in which to import the file.
134 *  \param filename The name of the file to load.
135 *  \param format A string describing the input format.
136 *  \return The number of bytes read, or 0 if there was not enough data,
137 *  or -1 if an error occurred.
138 */
139ssize_t caca_import_canvas_from_file(caca_canvas_t *cv, char const *filename,
140                                     char const *format)
141{
142#if defined __KERNEL__
143    seterrno(ENOSYS);
144    return -1;
145#else
146    caca_file_t *f;
147    char *data = NULL;
148    ssize_t ret, size = 0;
149
150    f = caca_file_open(filename, "rb");
151    if(!f)
152        return -1; /* fopen already set errno */
153
154    while(!caca_file_eof(f))
155    {
156        data = realloc(data, size + 1024);
157        if(!data)
158        {
159            caca_file_close(f);
160            seterrno(ENOMEM);
161            return -1;
162        }
163
164        ret = (ssize_t)caca_file_read(f, data + size, 1024);
165        if(ret >= 0)
166            size += ret;
167    }
168    caca_file_close(f);
169
170    ret = caca_import_canvas_from_memory(cv, data, size, format);
171    free(data);
172
173    return ret;
174#endif
175}
176
177/** \brief Import a memory buffer into a canvas area
178 *
179 *  Import a memory buffer into the given libcaca canvas's current
180 *  frame, at the specified position. For more information, see
181 *  caca_import_canvas_from_memory().
182 *
183 *  If an error occurs, -1 is returned and \b errno is set accordingly:
184 *  - \c EINVAL Unsupported format requested or invalid coordinates.
185 *  - \c ENOMEM Not enough memory to allocate canvas.
186 *
187 *  \param cv A libcaca canvas in which to import the file.
188 *  \param x The leftmost coordinate of the area to import to.
189 *  \param y The topmost coordinate of the area to import to.
190 *  \param data A memory area containing the data to be loaded into the canvas.
191 *  \param len The size in bytes of the memory area.
192 *  \param format A string describing the input format.
193 *  \return The number of bytes read, or 0 if there was not enough data,
194 *  or -1 if an error occurred.
195 */
196ssize_t caca_import_area_from_memory(caca_canvas_t *cv, int x, int y,
197                                     void const *data, size_t len,
198                                     char const *format)
199{
200    caca_canvas_t *tmp;
201    ssize_t ret;
202
203    tmp = caca_create_canvas(0, 0);
204    ret = caca_import_canvas_from_memory(tmp, data, len, format);
205
206    if(ret > 0)
207        caca_blit(cv, x, y, tmp, NULL);
208
209    caca_free_canvas(tmp);
210
211    return ret;
212}
213
214/** \brief Import a file into a canvas area
215 *
216 *  Import a file into the given libcaca canvas's current frame, at the
217 *  specified position. For more information, see
218 *  caca_import_canvas_from_file().
219 *
220 *  If an error occurs, -1 is returned and \b errno is set accordingly:
221 *  - \c ENOSYS File access is not implemented on this system.
222 *  - \c ENOMEM Not enough memory to allocate canvas.
223 *  - \c EINVAL Unsupported format requested or invalid coordinates.
224 *  caca_import_file() may also fail and set \b errno for any of the
225 *  errors specified for the routine fopen().
226 *
227 *  \param cv A libcaca canvas in which to import the file.
228 *  \param x The leftmost coordinate of the area to import to.
229 *  \param y The topmost coordinate of the area to import to.
230 *  \param filename The name of the file to load.
231 *  \param format A string describing the input format.
232 *  \return The number of bytes read, or 0 if there was not enough data,
233 *  or -1 if an error occurred.
234 */
235ssize_t caca_import_area_from_file(caca_canvas_t *cv, int x, int y,
236                                   char const *filename, char const *format)
237{
238    caca_canvas_t *tmp;
239    ssize_t ret;
240
241    tmp = caca_create_canvas(0, 0);
242    ret = caca_import_canvas_from_file(tmp, filename, format);
243
244    if(ret > 0)
245        caca_blit(cv, x, y, tmp, NULL);
246
247    caca_free_canvas(tmp);
248
249    return ret;
250}
251
252/** \brief Get available import formats
253 *
254 *  Return a list of available import formats. The list is a NULL-terminated
255 *  array of strings, interleaving a string containing the internal value for
256 *  the import format, to be used with caca_import_canvas(), and a string
257 *  containing the natural language description for that import format.
258 *
259 *  This function never fails.
260 *
261 *  \return An array of strings.
262 */
263char const * const * caca_get_import_list(void)
264{
265    static char const * const list[] =
266    {
267        "", "autodetect",
268        "caca", "native libcaca format",
269        "text", "plain text",
270        "ansi", "ANSI coloured text",
271        "utf8", "UTF-8 files with ANSI colour codes",
272        NULL, NULL
273    };
274
275    return list;
276}
277
278/*
279 * XXX: the following functions are local.
280 */
281
282static ssize_t import_caca(caca_canvas_t *cv, void const *data, size_t size)
283{
284    uint8_t const *buf = (uint8_t const *)data;
285    size_t control_size, data_size, expected_size;
286    unsigned int frames, f, n, offset;
287    uint16_t version, flags;
288    int32_t xmin = 0, ymin = 0, xmax = 0, ymax = 0;
289
290    if(size < 20)
291        return 0;
292
293    if(buf[0] != 0xca || buf[1] != 0xca || buf[2] != 'C' || buf[3] != 'V')
294    {
295        debug("caca import error: expected \\xca\\xcaCV header");
296        goto invalid_caca;
297    }
298
299    control_size = sscanu32(buf + 4);
300    data_size = sscanu32(buf + 8);
301    version = sscanu16(buf + 12);
302    frames = sscanu32(buf + 14);
303    flags = sscanu16(buf + 18);
304
305    if(size < 4 + control_size + data_size)
306        return 0;
307
308    if(control_size < 16 + frames * 32)
309    {
310        debug("caca import error: control size %u < expected %u",
311              (unsigned int)control_size, 16 + frames * 32);
312        goto invalid_caca;
313    }
314
315    for(expected_size = 0, f = 0; f < frames; f++)
316    {
317        unsigned int width, height, duration;
318        uint32_t attr;
319        int x, y, handlex, handley;
320
321        width = sscanu32(buf + 4 + 16 + f * 32);
322        height = sscanu32(buf + 4 + 16 + f * 32 + 4);
323        duration = sscanu32(buf + 4 + 16 + f * 32 + 8);
324        attr = sscanu32(buf + 4 + 16 + f * 32 + 12);
325        x = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 16);
326        y = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 20);
327        handlex = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 24);
328        handley = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 28);
329        expected_size += width * height * 8;
330        if(-handlex < xmin)
331            xmin = -handlex;
332        if(-handley < ymin)
333            ymin = -handley;
334        if((((int32_t) width) - handlex) > xmax)
335            xmax = ((int32_t) width) - handlex;
336        if((((int32_t) height) - handley) > ymax)
337            ymax = ((int32_t) height) - handley;
338    }
339
340    if(expected_size != data_size)
341    {
342        debug("caca import error: data size %u < expected %u",
343              (unsigned int)data_size, (unsigned int)expected_size);
344        goto invalid_caca;
345    }
346
347    caca_set_canvas_size(cv, 0, 0);
348    caca_set_canvas_size(cv, xmax - xmin, ymax - ymin);
349
350    for (f = caca_get_frame_count(cv); f--; )
351    {
352        caca_free_frame(cv, f);
353    }
354
355    for (offset = 0, f = 0; f < frames; f ++)
356    {
357        unsigned int width, height;
358
359        width = sscanu32(buf + 4 + 16 + f * 32);
360        height = sscanu32(buf + 4 + 16 + f * 32 + 4);
361        caca_create_frame(cv, f);
362        caca_set_frame(cv, f);
363
364        cv->curattr = sscanu32(buf + 4 + 16 + f * 32 + 12);
365        cv->frames[f].x = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 16);
366        cv->frames[f].y = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 20);
367        cv->frames[f].handlex = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 24);
368        cv->frames[f].handley = (int32_t)sscanu32(buf + 4 + 16 + f * 32 + 28);
369
370        /* FIXME: check for return value */
371
372        for(n = width * height; n--; )
373        {
374            int x = (n % width) - cv->frames[f].handlex - xmin;
375            int y = (n / width) - cv->frames[f].handley - ymin;
376
377            caca_put_char(cv, x, y, sscanu32(buf + 4 + control_size
378                                               + offset + 8 * n));
379            caca_put_attr(cv, x, y, sscanu32(buf + 4 + control_size
380                                               + offset + 8 * n + 4));
381        }
382        offset += width * height * 8;
383
384        cv->frames[f].x -= cv->frames[f].handlex;
385        cv->frames[f].y -= cv->frames[f].handley;
386        cv->frames[f].handlex = -xmin;
387        cv->frames[f].handley = -ymin;
388    }
389
390    caca_set_frame(cv, 0);
391
392    return (ssize_t)(4 + control_size + data_size);
393
394invalid_caca:
395    seterrno(EINVAL);
396    return -1;
397}
398
399/*
400 * XXX: The following functions are aliases.
401 */
402
403ssize_t cucul_import_memory(cucul_canvas_t *, void const *, size_t,
404                            char const *) CACA_ALIAS(caca_import_memory);
405ssize_t cucul_import_file(cucul_canvas_t *, char const *,
406                          char const *) CACA_ALIAS(caca_import_file);
407ssize_t caca_import_memory(caca_canvas_t *, void const *, size_t, char const *)
408                                  CACA_ALIAS(caca_import_canvas_from_memory);
409ssize_t caca_import_file(caca_canvas_t *, char const *, char const *)
410                                  CACA_ALIAS(caca_import_canvas_from_file);
411char const * const * cucul_get_import_list(void)
412         CACA_ALIAS(caca_get_import_list);
413
Note: See TracBrowser for help on using the repository browser.