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

Last change on this file since 768 was 768, checked in by Sam Hocevar, 14 years ago
  • Added a lot of sanity checks to the font parsing. It should now be secure against broken files, but it probably needs peer review.
  • Property svn:keywords set to Id
File size: 12.8 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 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the Do What The Fuck You Want To
8 *  Public License, Version 2, as published by Sam Hocevar. See
9 *  http://sam.zoy.org/wtfpl/COPYING for more details.
10 */
11
12/** \file font.c
13 *  \version \$Id: font.c 768 2006-04-13 17:57:19Z sam $
14 *  \author Sam Hocevar <sam@zoy.org>
15 *  \brief Colour handling
16 *
17 *  This file contains font handling functions.
18 */
19
20#include "config.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#   include <arpa/inet.h>
30#endif
31
32#include "cucul.h"
33#include "cucul_internals.h"
34
35/* Internal fonts */
36#include "font_mono9.h"
37#include "font_monobold12.h"
38
39/* Helper structure for font loading */
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, 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    struct glyph_info *glyph_list;
65    uint8_t *font_data;
66
67    uint8_t *private;
68};
69
70#define DECLARE_UNPACKGLYPH(bpp) \
71    static inline void \
72      unpack_glyph ## bpp(uint8_t *glyph, uint8_t *packed_data, \
73                          unsigned int n) \
74{ \
75    unsigned 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 \e cucul_render_canvas()
95 *  for bitmap output.
96 *
97 *  Internal fonts can also be loaded: if \e size is set to 0, \e data must
98 *  be a string containing the internal font name.
99 *
100 *  If \e size is non-zero, the \e size bytes of memory at address \e 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 \e cucul_free_font().
103 *
104 *  \param data The memory area containing the font or its name.
105 *  \param size The size of the memory area, or 0 if the font name is given.
106 *  \return A font handle or NULL in case of error.
107 */
108struct cucul_font *cucul_load_font(void const *data, unsigned int size)
109{
110    struct cucul_font *f;
111    unsigned int i;
112
113    if(size == 0)
114    {
115        if(!strcasecmp(data, "Monospace 9"))
116            return cucul_load_font(mono9_data, mono9_size);
117        if(!strcasecmp(data, "Monospace Bold 12"))
118            return cucul_load_font(monobold12_data, monobold12_size);
119
120        return NULL;
121    }
122
123    if(size < sizeof(struct font_header))
124        return NULL;
125
126    f = malloc(sizeof(struct cucul_font));
127    f->private = (void *)(uintptr_t)data;
128
129    memcpy(&f->header, f->private + 8, sizeof(struct font_header));
130    f->header.control_size = htonl(f->header.control_size);
131    f->header.data_size = htonl(f->header.data_size);
132    f->header.version = htons(f->header.version);
133    f->header.blocks = htons(f->header.blocks);
134    f->header.glyphs = htonl(f->header.glyphs);
135    f->header.bpp = htons(f->header.bpp);
136    f->header.width = htons(f->header.width);
137    f->header.height = htons(f->header.height);
138    f->header.flags = htons(f->header.flags);
139
140    if(size != 8 + f->header.control_size + f->header.data_size
141        || (f->header.bpp != 8 && f->header.bpp != 4 &&
142            f->header.bpp != 2 && f->header.bpp != 1))
143    {
144        free(f);
145        return NULL;
146    }
147
148    f->block_list = malloc(f->header.blocks * sizeof(struct block_info));
149    memcpy(f->block_list,
150           f->private + 8 + sizeof(struct font_header),
151           f->header.blocks * sizeof(struct block_info));
152    for(i = 0; i < f->header.blocks; i++)
153    {
154        f->block_list[i].start = htonl(f->block_list[i].start);
155        f->block_list[i].stop = htonl(f->block_list[i].stop);
156        f->block_list[i].index = htonl(f->block_list[i].index);
157
158        if(f->block_list[i].start > f->block_list[i].stop
159            || (i > 0 && f->block_list[i].start < f->block_list[i - 1].stop)
160            || f->block_list[i].index >= f->header.glyphs)
161        {
162            free(f->block_list);
163            free(f);
164            return NULL;
165        }
166    }
167
168    f->glyph_list = malloc(f->header.glyphs * sizeof(struct glyph_info));
169    memcpy(f->glyph_list,
170           f->private + 8 + sizeof(struct font_header)
171                + f->header.blocks * sizeof(struct block_info),
172           f->header.glyphs * sizeof(struct glyph_info));
173    for(i = 0; i < f->header.glyphs; i++)
174    {
175        f->glyph_list[i].width = htons(f->glyph_list[i].width);
176        f->glyph_list[i].height = htons(f->glyph_list[i].height);
177        f->glyph_list[i].data_offset = htonl(f->glyph_list[i].data_offset);
178
179        if(f->glyph_list[i].data_offset >= f->header.data_size
180            || f->glyph_list[i].data_offset
181                + f->glyph_list[i].width * f->glyph_list[i].height *
182                  f->header.bpp / 8 >= f->header.data_size)
183        {
184            free(f->glyph_list);
185            free(f->block_list);
186            free(f);
187            return NULL;
188        }
189    }
190
191    f->font_data = f->private + 8 + f->header.control_size;
192
193    return f;
194}
195
196/**
197 * \brief Get available builtin fonts
198 *
199 * Return a list of available builtin fonts. The list is a NULL-terminated
200 * array of strings.
201 *
202 * \return An array of strings.
203 */
204char const * const * cucul_get_font_list(void)
205{
206    static char const * const list[] =
207    {
208        "Monospace 9",
209        "Monospace Bold 12",
210        NULL
211    };
212
213    return list;
214}
215
216/** \brief Get a font's maximum glyph width.
217 *
218 *  This function returns the maximum value for the current font's glyphs
219 *
220 *  \param f The font, as returned by \e cucul_load_font()
221 *  \return The maximum glyph width.
222 */
223unsigned int cucul_get_font_width(struct cucul_font *f)
224{
225    return f->header.width;
226}
227
228/** \brief Get a font's maximum glyph height.
229 *
230 *  This function returns the maximum value for the current font's glyphs
231 *
232 *  \param f The font, as returned by \e cucul_load_font()
233 *  \return The maximum glyph height.
234 */
235unsigned int cucul_get_font_height(struct cucul_font *f)
236{
237    return f->header.height;
238}
239
240/** \brief Free a font structure.
241 *
242 *  This function frees all data allocated by \e cucul_load_font(). The
243 *  font structure is no longer usable by other libcucul functions. Once
244 *  this function has returned, the memory area that was given to
245 *  \e cucul_load_font() can be freed.
246 *
247 *  \param f The font, as returned by \e cucul_load_font()
248 */
249void cucul_free_font(struct cucul_font *f)
250{
251    free(f->glyph_list);
252    free(f->block_list);
253    free(f);
254}
255
256/** \brief Render the canvas onto an image buffer.
257 *
258 *  This function renders the given canvas on an image buffer using a specific
259 *  font. The pixel format is fixed (32-bit ARGB, 8 bits for each component).
260 *
261 *  The required image width can be computed using \e cucul_get_width(qq) and
262 *  \e cucul_get_font_width(f). The required height can be computed using
263 *  \e cucul_get_height(qq) and \e cucul_get_font_height(f).
264 *
265 *  Glyphs that do not fit in the image buffer are currently not rendered at
266 *  all. They may be cropped instead in future versions.
267 *
268 *  \param qq The canvas to render
269 *  \param f The font, as returned by \e cucul_load_font()
270 *  \param buf The image buffer
271 *  \param width The width (in pixels) of the image buffer
272 *  \param height The height (in pixels) of the image buffer
273 *  \param pitch The pitch (in bytes) of an image buffer line.
274 */
275void cucul_render_canvas(cucul_t *qq, struct cucul_font *f,
276                         unsigned char *buf, unsigned int width,
277                         unsigned int height, unsigned int pitch)
278{
279    uint8_t *glyph = NULL;
280    unsigned int x, y, xmax, ymax;
281
282    if(f->header.bpp != 8)
283        glyph = malloc(f->header.width * f->header.height);
284
285    if(width < qq->width * f->header.width)
286        xmax = width / f->header.width;
287    else
288        xmax = qq->width;
289
290    if(height < qq->height * f->header.height)
291        ymax = height / f->header.height;
292    else
293        ymax = qq->height;
294
295    for(y = 0; y < ymax; y++)
296    {
297        for(x = 0; x < xmax; x++)
298        {
299            uint8_t argb[8];
300            unsigned int starty = y * f->header.height;
301            unsigned int startx = x * f->header.width;
302            uint32_t ch = qq->chars[y * qq->width + x];
303            uint32_t attr = qq->attr[y * qq->width + x];
304            unsigned int b, i, j;
305            struct glyph_info *g;
306
307            /* Find the Unicode block where our glyph lies */
308            for(b = 0; b < f->header.blocks; b++)
309            {
310                if(ch < f->block_list[b].start)
311                {
312                    b = f->header.blocks;
313                    break;
314                }
315
316                if(ch < f->block_list[b].stop)
317                    break;
318            }
319
320            /* Glyph not in font? Skip it. */
321            if(b == f->header.blocks)
322                continue;
323
324            g = &f->glyph_list[f->block_list[b].index
325                                + ch - f->block_list[b].start];
326
327            _cucul_argb32_to_argb4(attr, argb);
328
329            /* Step 1: unpack glyph */
330            switch(f->header.bpp)
331            {
332            case 8:
333                glyph = f->font_data + g->data_offset;
334                break;
335            case 4:
336                unpack_glyph4(glyph, f->font_data + g->data_offset,
337                              g->width * g->height);
338                break;
339            case 2:
340                unpack_glyph2(glyph, f->font_data + g->data_offset,
341                              g->width * g->height);
342                break;
343            case 1:
344                unpack_glyph1(glyph, f->font_data + g->data_offset,
345                              g->width * g->height);
346                break;
347            }
348
349            /* Step 2: render glyph using colour attribute */
350            for(j = 0; j < g->height; j++)
351            {
352                uint8_t *line = buf + (starty + j) * pitch + 4 * startx;
353
354                for(i = 0; i < g->width; i++)
355                {
356                    uint8_t *pixel = line + 4 * i;
357                    uint32_t p, q, t;
358
359                    p = glyph[j * g->width + i];
360                    q = 0xff - p;
361
362                    for(t = 0; t < 4; t++)
363                       pixel[t] = (((q * argb[t]) + (p * argb[4 + t])) / 0xf);
364                }
365            }
366        }
367    }
368
369    if(f->header.bpp != 8)
370        free(glyph);
371}
372
373/*
374 * The libcaca font format, version 1
375 * ----------------------------------
376 *
377 * All types are big endian.
378 *
379 * struct
380 * {
381 *    uint8_t caca_header[4];    // "CACA"
382 *    uint8_t caca_file_type[4]; // "FONT"
383 *
384 * font_header:
385 *    uint32_t control_size;     // Control size (font_data - font_header)
386 *    uint32_t data_size;        // Data size (EOF - font_data)
387 *
388 *    uint16_t version;          // Font format version
389 *                               //  bit 0: set to 1 if font is compatible
390 *                               //         with version 1 of the format
391 *                               //  bits 1-15: unused yet, must be 0
392 *
393 *    uint16_t blocks;           // Number of blocks in the font
394 *    uint32_t glyphs;           // Total number of glyphs in the font
395 *
396 *    uint16_t bpp;              // Bits per pixel for glyph data (valid
397 *                               // Values are 1, 2, 4 and 8)
398 *    uint16_t width;            // Maximum glyph width
399 *    uint16_t height;           // Maximum glyph height
400 *
401 *    uint16_t flags;            // Feature flags
402 *                               //  bit 0: set to 1 if font is fixed width
403 *                               //  bits 1-15: unused yet, must be 0
404 *
405 * block_info:
406 *    struct
407 *    {
408 *       uint32_t start;         // Unicode index of the first glyph
409 *       uint32_t stop;          // Unicode index of the last glyph + 1
410 *       uint32_t index;         // Glyph info index of the first glyph
411 *    }
412 *    block_list[blocks];
413 *
414 * glyph_info:
415 *    struct
416 *    {
417 *       uint16_t width;         // Glyph width in pixels
418 *       uint16_t height;        // Glyph height in pixels
419 *       uint32_t data_offset;   // Offset (starting from data) to the data
420 *                               // for the first character
421 *    }
422 *    glyph_list[glyphs];
423 *
424 * extension_1:
425 * extension_2:
426 *    ...
427 * extension_N:
428 *    ...                        // reserved for future use
429 *
430 * font_data:
431 *    uint8_t data[data_size];   // glyph data
432 * };
433 */
434
Note: See TracBrowser for help on using the repository browser.