source: libcaca/trunk/caca/font.c @ 4333

Last change on this file since 4333 was 4333, checked in by Sam Hocevar, 10 years ago

Large source code cleanup, getting rid of spaces, tabs, and svn keywords.

  • Property svn:keywords set to Id
File size: 16.6 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  This library is free software. It comes without any warranty, to
7 *  the extent permitted by applicable law. You can redistribute it
8 *  and/or modify it under the terms of the Do What The Fuck You Want
9 *  To Public License, Version 2, as published by Sam Hocevar. See
10 *  http://sam.zoy.org/wtfpl/COPYING for more details.
11 */
12
13/*
14 *  This file contains font handling functions.
15 */
16
17#include "config.h"
18
19#if !defined(__KERNEL__)
20#   if defined(HAVE_ENDIAN_H)
21#       include <endian.h>
22#   endif
23#   include <stdio.h>
24#   include <stdlib.h>
25#   include <string.h>
26#endif
27
28#include "caca.h"
29#include "caca_internals.h"
30
31/* Internal fonts */
32#include "mono9.data"
33#include "monobold12.data"
34
35/* Helper structures for font loading */
36#if !defined(_DOXYGEN_SKIP_ME)
37struct font_header
38{
39    uint32_t control_size, data_size;
40    uint16_t version, blocks;
41    uint32_t glyphs;
42    uint16_t bpp, width, height, maxwidth, maxheight, flags;
43};
44
45struct block_info
46{
47    uint32_t start, stop, index;
48};
49
50struct glyph_info
51{
52    uint16_t width, height;
53    uint32_t data_offset;
54};
55
56struct caca_font
57{
58    struct font_header header;
59
60    struct block_info *block_list;
61    uint32_t *user_block_list;
62    struct glyph_info *glyph_list;
63    uint8_t *font_data;
64
65    uint8_t *private;
66};
67#endif
68
69#define DECLARE_UNPACKGLYPH(bpp) \
70    static inline void \
71      unpack_glyph ## bpp(uint8_t *glyph, uint8_t *packed_data, int n) \
72{ \
73    int i; \
74    \
75    for(i = 0; i < n; i++) \
76    { \
77        uint8_t pixel = packed_data[i / (8 / bpp)]; \
78        pixel >>= bpp * ((8 / bpp) - 1 - (i % (8 / bpp))); \
79        pixel %= (1 << bpp); \
80        pixel *= 0xff / ((1 << bpp) - 1); \
81        *glyph++ = pixel; \
82    } \
83}
84
85DECLARE_UNPACKGLYPH(4)
86DECLARE_UNPACKGLYPH(2)
87DECLARE_UNPACKGLYPH(1)
88
89/** \brief Load a font from memory for future use.
90 *
91 *  This function loads a font and returns a handle to its internal
92 *  structure. The handle can then be used with caca_render_canvas()
93 *  for bitmap output.
94 *
95 *  Internal fonts can also be loaded: if \c size is set to 0, \c data must
96 *  be a string containing the internal font name.
97 *
98 *  If \c size is non-zero, the \c size bytes of memory at address \c data
99 *  are loaded as a font. This memory are must not be freed by the calling
100 *  program until the font handle has been freed with caca_free_font().
101 *
102 *  If an error occurs, NULL is returned and \b errno is set accordingly:
103 *  - \c ENOENT Requested built-in font does not exist.
104 *  - \c EINVAL Invalid font data in memory area.
105 *  - \c ENOMEM Not enough memory to allocate font structure.
106 *
107 *  \param data The memory area containing the font or its name.
108 *  \param size The size of the memory area, or 0 if the font name is given.
109 *  \return A font handle or NULL in case of error.
110 */
111caca_font_t *caca_load_font(void const *data, size_t size)
112{
113    caca_font_t *f;
114    int i;
115
116    if(size == 0)
117    {
118        if(!strcasecmp(data, "Monospace 9"))
119            return caca_load_font(mono9_data, mono9_size);
120        if(!strcasecmp(data, "Monospace Bold 12"))
121            return caca_load_font(monobold12_data, monobold12_size);
122
123        seterrno(ENOENT);
124        return NULL;
125    }
126
127    if(size < sizeof(struct font_header))
128    {
129        debug("font error: data size %i < header size %i",
130              size, (int)sizeof(struct font_header));
131        seterrno(EINVAL);
132        return NULL;
133    }
134
135    f = malloc(sizeof(caca_font_t));
136    if(!f)
137    {
138        seterrno(ENOMEM);
139        return NULL;
140    }
141
142    f->private = (void *)(uintptr_t)data;
143
144    memcpy(&f->header, f->private + 4, sizeof(struct font_header));
145    f->header.control_size = hton32(f->header.control_size);
146    f->header.data_size = hton32(f->header.data_size);
147    f->header.version = hton16(f->header.version);
148    f->header.blocks = hton16(f->header.blocks);
149    f->header.glyphs = hton32(f->header.glyphs);
150    f->header.bpp = hton16(f->header.bpp);
151    f->header.width = hton16(f->header.width);
152    f->header.height = hton16(f->header.height);
153    f->header.maxwidth = hton16(f->header.maxwidth);
154    f->header.maxheight = hton16(f->header.maxheight);
155    f->header.flags = hton16(f->header.flags);
156
157    if(size != 4 + f->header.control_size + f->header.data_size
158        || (f->header.bpp != 8 && f->header.bpp != 4 &&
159            f->header.bpp != 2 && f->header.bpp != 1)
160        || (f->header.flags & 1) == 0)
161    {
162#if defined DEBUG
163        if(size != 4 + f->header.control_size + f->header.data_size)
164            debug("font error: data size %i < expected size %i",
165                  size, 4 + f->header.control_size + f->header.data_size);
166        else if(f->header.bpp != 8 && f->header.bpp != 4 &&
167                f->header.bpp != 2 && f->header.bpp != 1)
168            debug("font error: invalid bpp %i", f->header.bpp);
169        else if((f->header.flags & 1) == 0)
170            debug("font error: invalid flags %.04x", f->header.flags);
171#endif
172        free(f);
173        seterrno(EINVAL);
174        return NULL;
175    }
176
177    f->block_list = malloc(f->header.blocks * sizeof(struct block_info));
178    if(!f->block_list)
179    {
180        free(f);
181        seterrno(ENOMEM);
182        return NULL;
183    }
184
185    f->user_block_list = malloc((f->header.blocks + 1)
186                                  * 2 * sizeof(uint32_t));
187    if(!f->user_block_list)
188    {
189        free(f->block_list);
190        free(f);
191        seterrno(ENOMEM);
192        return NULL;
193    }
194
195    memcpy(f->block_list,
196           f->private + 4 + sizeof(struct font_header),
197           f->header.blocks * sizeof(struct block_info));
198    for(i = 0; i < f->header.blocks; i++)
199    {
200        f->block_list[i].start = hton32(f->block_list[i].start);
201        f->block_list[i].stop = hton32(f->block_list[i].stop);
202        f->block_list[i].index = hton32(f->block_list[i].index);
203
204        if(f->block_list[i].start > f->block_list[i].stop
205            || (i > 0 && f->block_list[i].start < f->block_list[i - 1].stop)
206            || f->block_list[i].index >= f->header.glyphs)
207        {
208#if defined DEBUG
209            if(f->block_list[i].start > f->block_list[i].stop)
210                debug("font error: block %i has start %i > stop %i",
211                      i, f->block_list[i].start, f->block_list[i].stop);
212            else if(i > 0 && f->block_list[i].start < f->block_list[i - 1].stop)
213                debug("font error: block %i has start %i < previous stop %i",
214                      f->block_list[i].start, f->block_list[i - 1].stop);
215            else if(f->block_list[i].index >= f->header.glyphs)
216                debug("font error: block %i has index >= glyph count %i",
217                      f->block_list[i].index, f->header.glyphs);
218#endif
219            free(f->user_block_list);
220            free(f->block_list);
221            free(f);
222            seterrno(EINVAL);
223            return NULL;
224        }
225
226        f->user_block_list[i * 2] = f->block_list[i].start;
227        f->user_block_list[i * 2 + 1] = f->block_list[i].stop;
228    }
229
230    f->user_block_list[i * 2] = 0;
231    f->user_block_list[i * 2 + 1] = 0;
232
233    f->glyph_list = malloc(f->header.glyphs * sizeof(struct glyph_info));
234    if(!f->glyph_list)
235    {
236        free(f->user_block_list);
237        free(f->block_list);
238        free(f);
239        seterrno(ENOMEM);
240        return NULL;
241    }
242
243    memcpy(f->glyph_list,
244           f->private + 4 + sizeof(struct font_header)
245                + f->header.blocks * sizeof(struct block_info),
246           f->header.glyphs * sizeof(struct glyph_info));
247    for(i = 0; i < (int)f->header.glyphs; i++)
248    {
249        f->glyph_list[i].width = hton16(f->glyph_list[i].width);
250        f->glyph_list[i].height = hton16(f->glyph_list[i].height);
251        f->glyph_list[i].data_offset = hton32(f->glyph_list[i].data_offset);
252
253        if(f->glyph_list[i].data_offset >= f->header.data_size
254            || f->glyph_list[i].data_offset
255                + (f->glyph_list[i].width * f->glyph_list[i].height *
256                   f->header.bpp + 7) / 8 > f->header.data_size
257            || f->glyph_list[i].width > f->header.maxwidth
258            || f->glyph_list[i].height > f->header.maxheight)
259        {
260#if defined DEBUG
261            if(f->glyph_list[i].data_offset >= f->header.data_size)
262                debug("font error: glyph %i has data start %i > "
263                      "data end %i",
264                      f->glyph_list[i].data_offset, f->header.data_size);
265            else if(f->glyph_list[i].data_offset
266                     + (f->glyph_list[i].width * f->glyph_list[i].height *
267                        f->header.bpp + 7) / 8 > f->header.data_size)
268                debug("font error: glyph %i has data end %i > "
269                      "data end %i", f->glyph_list[i].data_offset
270                       + (f->glyph_list[i].width * f->glyph_list[i].height *
271                          f->header.bpp + 7) / 8, f->header.data_size);
272            else if(f->glyph_list[i].width > f->header.maxwidth)
273                debug("font error: glyph %i has width %i > max width %i",
274                      f->glyph_list[i].width, f->header.maxwidth);
275            else if(f->glyph_list[i].height > f->header.maxheight)
276                debug("font error: glyph %i has height %i > max height %i",
277                      f->glyph_list[i].height, f->header.maxheight);
278#endif
279            free(f->glyph_list);
280            free(f->user_block_list);
281            free(f->block_list);
282            free(f);
283            seterrno(EINVAL);
284            return NULL;
285        }
286    }
287
288    f->font_data = f->private + 4 + f->header.control_size;
289
290    return f;
291}
292
293/** \brief Get available builtin fonts
294 *
295 *  Return a list of available builtin fonts. The list is a NULL-terminated
296 *  array of strings.
297 *
298 *  This function never fails.
299 *
300 *  \return An array of strings.
301 */
302char const * const * caca_get_font_list(void)
303{
304    static char const * const list[] =
305    {
306        "Monospace 9",
307        "Monospace Bold 12",
308        NULL
309    };
310
311    return list;
312}
313
314/** \brief Get a font's standard glyph width.
315 *
316 *  Return the standard value for the current font's glyphs. Most glyphs in
317 *  the font will have this width, except fullwidth characters.
318 *
319 *  This function never fails.
320 *
321 *  \param f The font, as returned by caca_load_font()
322 *  \return The standard glyph width.
323 */
324int caca_get_font_width(caca_font_t const *f)
325{
326    return f->header.width;
327}
328
329/** \brief Get a font's standard glyph height.
330 *
331 *  Returns the standard value for the current font's glyphs. Most glyphs in
332 *  the font will have this height.
333 *
334 *  This function never fails.
335 *
336 *  \param f The font, as returned by caca_load_font()
337 *  \return The standard glyph height.
338 */
339int caca_get_font_height(caca_font_t const *f)
340{
341    return f->header.height;
342}
343
344/** \brief Get a font's list of supported glyphs.
345 *
346 *  This function returns the list of Unicode blocks supported by the
347 *  given font. The list is a zero-terminated list of indices. Here is
348 *  an example:
349 *
350 *  \code
351 *  {
352 *     0x0000, 0x0080,   // Basic latin: A, B, C, a, b, c
353 *     0x0080, 0x0100,   // Latin-1 supplement: "A, 'e, ^u
354 *     0x0530, 0x0590,   // Armenian
355 *     0x0000, 0x0000,   // END
356 *  };
357 *  \endcode
358 *
359 *  This function never fails.
360 *
361 *  \param f The font, as returned by caca_load_font()
362 *  \return The list of Unicode blocks supported by the font.
363 */
364uint32_t const *caca_get_font_blocks(caca_font_t const *f)
365{
366    return (uint32_t const *)f->user_block_list;
367}
368
369/** \brief Free a font structure.
370 *
371 *  This function frees all data allocated by caca_load_font(). The
372 *  font structure is no longer usable by other libcaca functions. Once
373 *  this function has returned, the memory area that was given to
374 *  caca_load_font() can be freed.
375 *
376 *  This function never fails.
377 *
378 *  \param f The font, as returned by caca_load_font()
379 *  \return This function always returns 0.
380 */
381int caca_free_font(caca_font_t *f)
382{
383    free(f->glyph_list);
384    free(f->user_block_list);
385    free(f->block_list);
386    free(f);
387
388    return 0;
389}
390
391/** \brief Render the canvas onto an image buffer.
392 *
393 *  This function renders the given canvas on an image buffer using a specific
394 *  font. The pixel format is fixed (32-bit ARGB, 8 bits for each component).
395 *
396 *  The required image width can be computed using
397 *  caca_get_canvas_width() and caca_get_font_width(). The required
398 *  height can be computed using caca_get_canvas_height() and
399 *  caca_get_font_height().
400 *
401 *  Glyphs that do not fit in the image buffer are currently not rendered at
402 *  all. They may be cropped instead in future versions.
403 *
404 *  If an error occurs, -1 is returned and \b errno is set accordingly:
405 *  - \c EINVAL Specified width, height or pitch is invalid.
406 *
407 *  \param cv The canvas to render
408 *  \param f The font, as returned by caca_load_font()
409 *  \param buf The image buffer
410 *  \param width The width (in pixels) of the image buffer
411 *  \param height The height (in pixels) of the image buffer
412 *  \param pitch The pitch (in bytes) of an image buffer line.
413 *  \return 0 in case of success, -1 if an error occurred.
414 */
415int caca_render_canvas(caca_canvas_t const *cv, caca_font_t const *f,
416                        void *buf, int width, int height, int pitch)
417{
418    uint8_t *glyph = NULL;
419    int x, y, xmax, ymax;
420
421    if(width < 0 || height < 0 || pitch < 0)
422    {
423        seterrno(EINVAL);
424        return -1;
425    }
426
427    if(f->header.bpp != 8)
428        glyph = malloc(f->header.width * 2 * f->header.height);
429
430    if(width < cv->width * f->header.width)
431        xmax = width / f->header.width;
432    else
433        xmax = cv->width;
434
435    if(height < cv->height * f->header.height)
436        ymax = height / f->header.height;
437    else
438        ymax = cv->height;
439
440    for(y = 0; y < ymax; y++)
441    {
442        for(x = 0; x < xmax; x++)
443        {
444            uint8_t argb[8];
445            int starty = y * f->header.height;
446            int startx = x * f->header.width;
447            uint32_t ch = cv->chars[y * cv->width + x];
448            uint32_t attr = cv->attrs[y * cv->width + x];
449            int b, i, j;
450            struct glyph_info *g;
451
452            /* Find the Unicode block where our glyph lies */
453            for(b = 0; b < f->header.blocks; b++)
454            {
455                if(ch < f->block_list[b].start)
456                {
457                    b = f->header.blocks;
458                    break;
459                }
460
461                if(ch < f->block_list[b].stop)
462                    break;
463            }
464
465            /* Glyph not in font? Skip it. */
466            if(b == f->header.blocks)
467                continue;
468
469            g = &f->glyph_list[f->block_list[b].index
470                                + ch - f->block_list[b].start];
471
472            caca_attr_to_argb64(attr, argb);
473
474            /* Step 1: unpack glyph */
475            switch(f->header.bpp)
476            {
477            case 8:
478                glyph = f->font_data + g->data_offset;
479                break;
480            case 4:
481                unpack_glyph4(glyph, f->font_data + g->data_offset,
482                              g->width * g->height);
483                break;
484            case 2:
485                unpack_glyph2(glyph, f->font_data + g->data_offset,
486                              g->width * g->height);
487                break;
488            case 1:
489                unpack_glyph1(glyph, f->font_data + g->data_offset,
490                              g->width * g->height);
491                break;
492            }
493
494            /* Step 2: render glyph using colour attribute */
495            for(j = 0; j < g->height; j++)
496            {
497                uint8_t *line = buf;
498                line += (starty + j) * pitch + 4 * startx;
499
500                for(i = 0; i < g->width; i++)
501                {
502                    uint8_t *pixel = line + 4 * i;
503                    uint32_t p, q, t;
504
505                    p = glyph[j * g->width + i];
506                    q = 0xff - p;
507
508                    for(t = 0; t < 4; t++)
509                        pixel[t] = (((q * argb[t]) + (p * argb[4 + t])) / 0xf);
510                }
511            }
512        }
513    }
514
515    if(f->header.bpp != 8)
516        free(glyph);
517
518    return 0;
519}
520
521/*
522 * XXX: The following functions are aliases.
523 */
524
525cucul_font_t *cucul_load_font(void const *, size_t) CACA_ALIAS(caca_load_font);
526char const * const * cucul_get_font_list(void) CACA_ALIAS(caca_get_font_list);
527int cucul_get_font_width(cucul_font_t const *) CACA_ALIAS(caca_get_font_width);
528int cucul_get_font_height(cucul_font_t const *)
529         CACA_ALIAS(caca_get_font_height);
530uint32_t const *cucul_get_font_blocks(cucul_font_t const *)
531         CACA_ALIAS(caca_get_font_blocks);
532int cucul_render_canvas(cucul_canvas_t const *, cucul_font_t const *,
533                        void *, int, int, int) CACA_ALIAS(caca_render_canvas);
534int cucul_free_font(cucul_font_t *) CACA_ALIAS(caca_free_font);
535
Note: See TracBrowser for help on using the repository browser.