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