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

Last change on this file since 2305 was 2305, checked in by Sam Hocevar, 12 years ago
  • Remove all unsigned ints from exported functions. Signed arithmetic is far better for error checking.
  • 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 2305 2008-04-19 19:25:52Z 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 "cucul.h"
31#include "cucul_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 cucul_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 cucul_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 cucul_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 */
113cucul_font_t *cucul_load_font(void const *data, size_t size)
114{
115    cucul_font_t *f;
116    int i;
117
118    if(size == 0)
119    {
120        if(!strcasecmp(data, "Monospace 9"))
121            return cucul_load_font((char *)&mono9_data, mono9_size);
122        if(!strcasecmp(data, "Monospace Bold 12"))
123            return cucul_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(cucul_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 * cucul_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 cucul_load_font()
324 *  \return The standard glyph width.
325 */
326int cucul_get_font_width(cucul_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 cucul_load_font()
339 *  \return The standard glyph height.
340 */
341int cucul_get_font_height(cucul_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 cucul_load_font()
364 *  \return The list of Unicode blocks supported by the font.
365 */
366uint32_t const *cucul_get_font_blocks(cucul_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 cucul_load_font(). The
374 *  font structure is no longer usable by other libcucul functions. Once
375 *  this function has returned, the memory area that was given to
376 *  cucul_load_font() can be freed.
377 *
378 *  This function never fails.
379 *
380 *  \param f The font, as returned by cucul_load_font()
381 *  \return This function always returns 0.
382 */
383int cucul_free_font(cucul_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 *  cucul_get_canvas_width() and cucul_get_font_width(). The required
400 *  height can be computed using cucul_get_canvas_height() and
401 *  cucul_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 cucul_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 cucul_render_canvas(cucul_canvas_t const *cv, cucul_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            cucul_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.