source: libcaca/trunk/cucul/font.c @ 1362

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