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

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