source: trunk/third/librsvg/rsvg-ft.c @ 17277

Revision 17277, 29.0 KB checked in by amb, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17276, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2
3   rsvg-ft.c: Basic functions for freetype/libart integration.
4 
5   Copyright (C) 2000 Eazel, Inc.
6 
7   This program is free software; you can redistribute it and/or
8   modify it under the terms of the GNU General Public License as
9   published by the Free Software Foundation; either version 2 of the
10   License, or (at your option) any later version.
11 
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   General Public License for more details.
16 
17   You should have received a copy of the GNU General Public
18   License along with this program; if not, write to the
19   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20   Boston, MA 02111-1307, USA.
21 
22   Author: Raph Levien <raph@artofcode.com>
23*/
24
25#include <glib.h>
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <math.h>
30
31#include <freetype/freetype.h>
32
33#include <libart_lgpl/art_misc.h>
34#include <libart_lgpl/art_rect.h>
35#include <libart_lgpl/art_alphagamma.h>
36#include <libart_lgpl/art_affine.h>
37#include "art_render.h"
38#include "art_render_mask.h"
39
40#include "rsvg-ft.h"
41
42#define FT_FLOOR( x )  (   (x)        & -64 )
43#define FT_CEIL( x )   ( ( (x) + 63 ) & -64 )
44#define FT_TRUNC( x )  (   (x) >> 6 )
45#define FT_FROMFLOAT(x) ((int)floor ((x) * 64.0 + 0.5))
46#define FT_TOFLOAT(x) ((x) * (1.0 / 64.0))
47
48typedef struct _RsvgFTFont RsvgFTFont;
49typedef struct _RsvgFTFontCacheEntry RsvgFTFontCacheEntry;
50typedef struct _RsvgFTGlyphDesc RsvgFTGlyphDesc;
51typedef struct _RsvgFTGlyphCacheEntry RsvgFTGlyphCacheEntry;
52
53#define SUBPIXEL_FRACTION 4
54
55struct _RsvgFTCtx {
56        FT_Library ftlib;
57
58        GHashTable *font_hash_table; /* map filename to RsvgFTFontCacheEntry */
59
60        /* Notes on the font list:
61
62           There is a font list entry for each font that is "intern"ed.
63           This entry persists for the life of the RsvgFTCtx structure.
64           These entry correspond one-to-one with font handles, which
65           begin at 0 and allocate upwards.
66
67           Each entry in the font list may be either loaded or not, as
68           indicated by the non-NULL status of the font element of the
69           RsvgFTFontCacheEntry.
70
71           The lru list (first and last pointers here, prev and next
72           pointers in the RsvgFTFontCacheEntry) contains only loaded
73           fonts. This should be considered an invariant.
74        */
75
76        int n_font_list;
77        RsvgFTFontCacheEntry **font_list;
78        RsvgFTFontCacheEntry *first, *last;
79
80        int n_loaded_fonts;
81        int n_loaded_fonts_max;
82
83        GHashTable *glyph_hash_table; /* map GlyphDesc to GlyphCacheEntry */
84        int glyph_bytes;
85        int glyph_bytes_max;
86        RsvgFTGlyphCacheEntry *glyph_first, *glyph_last;
87};
88
89struct _RsvgFTFont {
90        /* refcnt is likely to be obsoleted - clients now hold handles
91           which don't really need refcounting */
92        int refcnt;
93        RsvgFTCtx *ctx;
94        FT_Face face;
95};
96
97struct _RsvgFTFontCacheEntry {
98        RsvgFTFontCacheEntry *prev, *next; /* for lru list */
99        char *fn;
100        char *fn_attached;
101        RsvgFTFont *font;
102        RsvgFTFontHandle handle; /* index in ctx->font_cache */
103};
104
105struct _RsvgFTGlyphDesc {
106        RsvgFTFontHandle fh;
107        FT_F26Dot6 char_width;
108        FT_F26Dot6 char_height;
109        FT_UInt glyph_index;
110        unsigned char x_subpixel;
111        unsigned char y_subpixel;
112};
113
114struct _RsvgFTGlyphCacheEntry {
115        RsvgFTGlyphCacheEntry *prev, *next; /* for lru list */
116        int x0, y0; /* relative to affine */
117        RsvgFTGlyph *glyph;
118        RsvgFTGlyphDesc *desc;
119};
120
121/* Glyph cache fun stuff */
122
123static guint
124rsvg_ft_glyph_desc_hash (gconstpointer v)
125{
126        const RsvgFTGlyphDesc *desc = (const RsvgFTGlyphDesc *)v;
127
128        /* not known to be a good mixing function */
129        return (desc->fh << 16) + desc->char_width +
130                ((desc->char_width ^ desc->char_height) << 10) +
131                (desc->x_subpixel << 22) +
132                (desc->y_subpixel << 16) +
133                desc->glyph_index;
134}
135
136static gint
137rsvg_ft_glyph_desc_equal (gconstpointer v, gconstpointer v2)
138{
139        return !memcmp ((const gchar *)v, (const gchar *)v2,
140                        sizeof(RsvgFTGlyphDesc));
141}
142
143/**
144 * rsvg_ft_glyph_lookup: Look up a glyph in the glyph cache.
145 * @ctx: The Rsvg FT context.
146 * @desc: Glyph descriptor.
147 * @xy: Where to store relative coordinates of glyph.
148 *
149 * Looks up the glyph in the glyph cache. If found, moves it to the front
150 * of the LRU list. It does not bump the refcount on the glyph - most
151 * of the time, the caller will want to do that.
152 *
153 * Return value: The glyph if found, otherwise NULL.
154 **/
155static RsvgFTGlyph *
156rsvg_ft_glyph_lookup (RsvgFTCtx *ctx, const RsvgFTGlyphDesc *desc,
157                      int glyph_xy[2])
158{
159        RsvgFTGlyphCacheEntry *entry;
160
161        entry = g_hash_table_lookup (ctx->glyph_hash_table, desc);
162        if (entry == NULL)
163                return NULL;
164
165        /* move entry to front of LRU list */
166        if (entry->prev != NULL) {
167                entry->prev->next = entry->next;
168                if (entry->next != NULL) {
169                        entry->next->prev = entry->prev;
170                } else {
171                        ctx->glyph_last = entry->prev;
172                }
173                entry->prev = NULL;
174                entry->next = ctx->glyph_first;
175                ctx->glyph_first->prev = entry;
176                ctx->glyph_first = entry;
177
178        }
179        glyph_xy[0] = entry->x0;
180        glyph_xy[1] = entry->y0;
181        return entry->glyph;
182}
183
184/**
185 * rsvg_ft_glyph_bytes: Determine the number of bytes in a glyph.
186 * @glyph: Glyph.
187 *
188 * Determines the number of bytes used by @glyph, for the purposes of
189 * caching. Note: I'm not counting malloc overhead. Maybe I should.
190 *
191 * Return value: Number of bytes used by glyph.
192 **/
193static int
194rsvg_ft_glyph_bytes (RsvgFTGlyph *glyph)
195{
196        return glyph->rowstride * glyph->height + sizeof (RsvgFTGlyph);
197}
198
199/**
200 * rsvg_gt_glyph_evict: Evict lru glyph from glyph cache.
201 * @ctx: The RsvgFT context.
202 * @amount_to_evict: The amount above the high water mark for the cache
203 * that we are.
204 *
205 * Evicts any glyphs with a reference count of 1 until it is either
206 * below the high water mark or out of glyphs.
207 **/
208static void
209rsvg_ft_glyph_evict (RsvgFTCtx *ctx, int amount_to_evict)
210{
211        RsvgFTGlyphCacheEntry *victim, *prev;
212        RsvgFTGlyph *glyph;
213        int glyph_bytes, evicted_so_far;
214
215        evicted_so_far = 0;
216        for (victim = ctx->glyph_last; victim != NULL; victim = prev) {
217                prev = victim->prev;
218                glyph = victim->glyph;
219
220                if (glyph->refcnt != 1) {
221                        continue;
222                }
223
224                if (victim->prev != NULL) {
225                        victim->prev->next = victim->next;
226                } else {
227                        ctx->glyph_first = victim->next;
228                }
229                if (victim->next != NULL) {
230                        victim->next->prev = victim->prev;
231                } else {
232                        ctx->glyph_last = victim->prev;
233                }
234               
235                glyph_bytes = rsvg_ft_glyph_bytes (glyph);
236                ctx->glyph_bytes -= glyph_bytes;
237                rsvg_ft_glyph_unref (glyph);
238               
239                g_hash_table_remove (ctx->glyph_hash_table, victim->desc);
240                g_free (victim->desc);
241                g_free (victim);
242               
243                evicted_so_far += glyph_bytes;
244                if (evicted_so_far >= amount_to_evict) {
245                        break;
246                }
247        }
248}
249
250/**
251 * rsvg_ft_glyph_insert: Insert a glyph into the glyph cache.
252 * @ctx: The RsvgFT context.
253 * @desc: Glyph descriptor.
254 * @glyph: The glyph itself.
255 * @x0: Relative x0 of glyph.
256 * @y0: Relative y0 of glyph.
257 *
258 * Inserts @glyph into the glyph cache under the glyph descriptor @desc.
259 * This routine also takes care of evicting glyphs when the cache
260 * reaches its high water limit.
261 **/
262static void
263rsvg_ft_glyph_insert (RsvgFTCtx *ctx, const RsvgFTGlyphDesc *desc,
264                      RsvgFTGlyph *glyph, int x0, int y0)
265{
266        RsvgFTGlyphDesc *new_desc;
267        RsvgFTGlyphCacheEntry *entry;
268
269        ctx->glyph_bytes += rsvg_ft_glyph_bytes (glyph);
270
271        if (ctx->glyph_bytes + rsvg_ft_glyph_bytes (glyph) >= ctx->glyph_bytes_max) {
272                rsvg_ft_glyph_evict (ctx, ctx->glyph_bytes + rsvg_ft_glyph_bytes (glyph) - ctx->glyph_bytes_max);
273        }
274       
275        new_desc = g_new (RsvgFTGlyphDesc, 1);
276        memcpy (new_desc, desc, sizeof (RsvgFTGlyphDesc));
277        entry = g_new (RsvgFTGlyphCacheEntry, 1);
278        entry->prev = NULL;
279        entry->next = ctx->glyph_first;
280        if (entry->next != NULL) {
281                entry->next->prev = entry;
282        } else {
283                ctx->glyph_last = entry;
284        }
285        ctx->glyph_first = entry;
286        entry->glyph = glyph;
287        entry->desc = new_desc;
288        entry->x0 = x0;
289        entry->y0 = y0;
290        g_hash_table_insert (ctx->glyph_hash_table, new_desc, entry);
291}
292
293RsvgFTCtx *
294rsvg_ft_ctx_new (void) {
295        RsvgFTCtx *result = g_new (RsvgFTCtx, 1);
296        FT_Error error;
297
298        error = FT_Init_FreeType (&result->ftlib);
299        if (error) {
300                g_free (result);
301                result = NULL;
302        }
303        result->font_hash_table = g_hash_table_new (g_str_hash, g_str_equal);
304        result->n_font_list = 0;
305        result->font_list = NULL;
306        result->first = NULL;
307        result->last = NULL;
308        result->n_loaded_fonts = 0;
309        result->n_loaded_fonts_max = 10;
310
311        result->glyph_bytes = 0;
312        result->glyph_bytes_max = 0x100000; /* 1 meg */
313        result->glyph_first = NULL;
314        result->glyph_last = NULL;
315
316        result->glyph_hash_table = g_hash_table_new (rsvg_ft_glyph_desc_hash,
317                                                     rsvg_ft_glyph_desc_equal);
318
319        return result;
320}
321
322void
323rsvg_ft_ctx_done (RsvgFTCtx *ctx) {
324        int i;
325        RsvgFTGlyphCacheEntry *glyph_ce, *next;
326
327        g_hash_table_destroy (ctx->font_hash_table);
328        for (i = 0; i < ctx->n_font_list; i++) {
329                RsvgFTFontCacheEntry *entry = ctx->font_list[i];
330                RsvgFTFont *font = entry->font;
331                g_free (entry->fn);
332                g_free (entry->fn_attached);
333                if (font != NULL) {
334                        FT_Done_Face (font->face);
335                        g_free (font);
336                }
337                g_free (entry);
338        }
339        g_free (ctx->font_list);
340
341        /* Free glyph cache. */
342        g_hash_table_destroy (ctx->glyph_hash_table);
343        for (glyph_ce = ctx->glyph_first; glyph_ce != NULL; glyph_ce = next) {
344                g_free (glyph_ce->desc);
345                g_free (glyph_ce->glyph->buf);
346                g_free (glyph_ce->glyph);
347                next = glyph_ce->next;
348                g_free (glyph_ce);
349        }
350
351        FT_Done_FreeType (ctx->ftlib);
352        g_free (ctx);
353}
354
355/**
356 * rsvg_ft_load: Load a font.
357 * @ctx: Rsvg FT context.
358 * @font_file_name: File name.
359 *
360 * Return value: Newly created RsvgFont structure.
361 **/
362static RsvgFTFont *
363rsvg_ft_load (RsvgFTCtx *ctx, const char *font_file_name)
364{
365        FT_Error error;
366        FT_Face face;
367        RsvgFTFont *result;
368
369        error = FT_New_Face (ctx->ftlib, font_file_name,
370                             0, &face);
371        if (error)
372                result = NULL;
373        else {
374                result = g_new (RsvgFTFont, 1);
375                result->refcnt = 1;
376                result->ctx = ctx;
377                result->face = face;
378        }
379        return result;
380}
381
382/**
383 * rsvg_ft_intern: Intern a font.
384 * @ctx: Rsvg FT context.
385 * @font_file_name: File name.
386 *
387 * This routine checks the font list to see if the font has already been
388 * interned. If so, it just returns the existing handle. Otherwise, it
389 * adds the font to the font list with the new font handle.
390 *
391 * Return value: The font handle for the font.
392 **/
393RsvgFTFontHandle
394rsvg_ft_intern (RsvgFTCtx *ctx, const char *font_file_name)
395{
396        RsvgFTFontCacheEntry *entry;
397
398        entry = g_hash_table_lookup (ctx->font_hash_table, font_file_name);
399        if (entry == NULL) {
400                /* not found in font list */
401                int n_font_list;
402
403                n_font_list = ctx->n_font_list++;
404                entry = g_new (RsvgFTFontCacheEntry, 1);
405                entry->fn = g_strdup (font_file_name);
406                entry->fn_attached = NULL;
407                entry->handle = n_font_list;
408                entry->font = NULL;
409                entry->prev = NULL;
410                entry->next = NULL;
411                if (n_font_list == 0) {
412                        ctx->font_list = g_new (RsvgFTFontCacheEntry *, 1);
413                } else if (!(n_font_list & (n_font_list - 1))) {
414                        ctx->font_list = g_renew (RsvgFTFontCacheEntry *,
415                                                   ctx->font_list,
416                                                   n_font_list << 1);
417                }
418                ctx->font_list[n_font_list] = entry;
419        }
420
421/*      fprintf (stderr, "handle = %d\n", entry->handle); */
422        return entry->handle;
423}
424
425/**
426 * rsvg_ft_font_attach: Attach an additional font file.
427 * @ctx: Rsvg FT context.
428 * @fh: Font handle.
429 * @font_file_name: The filename of an additional file to attach.
430 *
431 * Attaches an additional font file to @font. For Type1 fonts, use
432 * rsvg_ft_load() to load the .pfb file, then this one to attach
433 * the afm.
434 **/
435void
436rsvg_ft_font_attach (RsvgFTCtx *ctx, RsvgFTFontHandle fh,
437                     const char *font_file_name)
438{
439        RsvgFTFontCacheEntry *entry;
440        RsvgFTFont *font;
441        FT_Error error;
442
443        if (fh < 0 || fh >= ctx->n_font_list)
444                return;
445        entry = ctx->font_list[fh];
446        if (entry->fn_attached != NULL)
447                return;
448        entry->fn_attached = g_strdup (font_file_name);
449        font = entry->font;
450        if (font != NULL) {
451                error = FT_Attach_File (font->face, font_file_name);
452        }
453}
454
455/**
456 * rsvg_ft_font_evict: Evict least recently used font from font cache.
457 * @ctx: Rsvg FT context.
458 *
459 * Removes the least recently used font from the font cache.
460 **/
461static void
462rsvg_ft_font_evict (RsvgFTCtx *ctx)
463{
464        RsvgFTFontCacheEntry *victim;
465        RsvgFTFont *font;
466
467#ifdef DEBUG
468        g_print ("rsvg_ft_font_evict: evicting!\n");
469#endif
470
471        victim = ctx->last;
472        if (victim == NULL) {
473                /* We definitely shouldn't get here, but if we do,
474                   print an error message that helps explain why we
475                   did. */
476                if (ctx->n_loaded_fonts > 0) {
477                        g_error ("rsvg_ft_font_evict: no font in loaded font list to evict, but ctx->n_loaded_fonts = %d, internal invariant violated",
478                                 ctx->n_loaded_fonts);
479                } else {
480                        g_error ("rsvg_ft_font_evict: ctx->n_loaded_fonts_max = %d, it must be positive",
481                                 ctx->n_loaded_fonts_max);
482                }
483        }
484
485        if (victim->prev == NULL)
486                ctx->first = NULL;
487        else
488                victim->prev->next = NULL;
489        if (victim->next != NULL) {
490                g_warning ("rsvg_ft_font_evict: last font in LRU font list has non-NULL next field, suggesting corruption of data structure");
491        }
492        ctx->last = victim->prev;
493
494        font = victim->font;
495        if (font != NULL) {
496                FT_Done_Face (font->face);
497                g_free (font);
498                victim->font = NULL;
499        }
500
501        ctx->n_loaded_fonts--;
502}
503
504/**
505 * rsvg_ft_font_resolve: Resolve a font handle.
506 * @ctx: Rsvg FT context.
507 * @fh: Font handle.
508 *
509 * Resolves the font handle @fh to an actual font data structure.
510 * This includes loading the font if it hasn't been loaded yet, or if
511 * it's been evicted from the cache.
512 *
513 * Return value: RsvgFTFont structure.
514 **/
515static RsvgFTFont *
516rsvg_ft_font_resolve (RsvgFTCtx *ctx, RsvgFTFontHandle fh)
517{
518        RsvgFTFontCacheEntry *entry = NULL;
519        RsvgFTFont *font = NULL;
520
521        if (fh < 0 || fh >= ctx->n_font_list)
522                return NULL;
523        entry = ctx->font_list[fh];
524        if (entry->font == NULL) {
525                while (ctx->n_loaded_fonts >= ctx->n_loaded_fonts_max) {
526                        rsvg_ft_font_evict (ctx);
527                }
528                font = rsvg_ft_load (ctx, entry->fn);
529                if (font != NULL) {
530                        if (entry->fn_attached != NULL) {
531                                FT_Error error;
532
533                                error = FT_Attach_File (font->face,
534                                                        entry->fn_attached);
535                        }
536                        entry->font = font;
537                        ctx->n_loaded_fonts++;
538
539                        /* insert entry at front of list */
540                        entry->next = ctx->first;
541                        if (ctx->first != NULL)
542                                ctx->first->prev = entry;
543                        else
544                                ctx->last = entry;
545                        ctx->first = entry;
546                }
547        } else {
548                font = entry->font;
549
550                /* move entry to front of LRU list */
551                if (entry->prev != NULL) {
552                        entry->prev->next = entry->next;
553                        if (entry->next != NULL) {
554                                entry->next->prev = entry->prev;
555                        } else {
556                                ctx->last = entry->prev;
557                        }
558                        entry->prev = NULL;
559                        entry->next = ctx->first;
560                        ctx->first->prev = entry;
561                        ctx->first = entry;
562                }
563        }
564
565        return font;
566}
567
568/**
569 * rsvg_ft_glyph_composite: Composite glyph using saturation.
570 * @dst: Destination glyph over which to composite.
571 * @src: Source glyph for compositing.
572 * @dx: X offset of src glyph relative to dst.
573 * @dy: Y offset of src glyph relative to dst.
574 *
575 * Composites @src over @dst using saturation arithmetic. This results
576 * in "perfect" results when glyphs are disjoint (including the case
577 * when glyphs abut), but somewhat darker than "perfect" results for
578 * geometries involving overlap.
579 **/
580static void
581rsvg_ft_glyph_composite (RsvgFTGlyph *dst, const RsvgFTGlyph *src,
582                         int dx, int dy)
583{
584        int x, y;
585        int x0, y0;
586        int x1, y1;
587        int width;
588        guchar *dst_line, *src_line;
589
590        x0 = MAX (0, dx);
591        x1 = MIN (dst->width, dx + src->width);
592        width = x1 - x0;
593        if (width <= 0)
594                return;
595
596        y0 = MAX (0, dy);
597        y1 = MIN (dst->height, dy + src->height);
598        src_line = src->buf + (y0 - dy) * src->rowstride + x0 - dx;
599        dst_line = dst->buf + y0 * dst->rowstride + x0;
600        for (y = y0; y < y1; y++) {
601                for (x = 0; x < width; x++) {
602                        int v = src_line[x] + dst_line[x];
603                        v |= -(v >> 8); /* fast for v = v > 255 ? 255 : v */
604                        dst_line[x] = v;
605                }
606                src_line += src->rowstride;
607                dst_line += dst->rowstride;
608        }
609}
610
611/**
612 * rsvg_ft_get_glyph: Get a rendered glyph.
613 * @font: The font.
614 * @glyph_ix: Glyph index.
615 * @sx: Width of em in pixels.
616 * @sy: Height of em in pixels.
617 * @affine: Affine transformation.
618 * @xy: Where to store the top left coordinates.
619 *
620 * Note: The nominal resolution is 72 dpi. For rendering at other resolutions,
621 * scale the @affine by resolution/(72 dpi).
622 *
623 * Return value: The rendered glyph.
624 **/
625static RsvgFTGlyph *
626rsvg_ft_get_glyph (RsvgFTFont *font, FT_UInt glyph_ix, double sx, double sy,
627                   const double affine[6], int xy[2])
628{
629        RsvgFTGlyph *result;
630        FT_Error error;
631        FT_GlyphSlot glyph;
632        FT_Face face = font->face;
633        int x0, y0, x1, y1;
634        int width, height;
635        FT_Matrix matrix;
636        FT_Vector delta;
637        double expansion, scale;
638
639        result = NULL;
640
641        if (glyph_ix == 0)
642                return NULL;
643
644        expansion = art_affine_expansion (affine);
645        scale = 0x10000 / expansion;
646
647        error = FT_Set_Char_Size (face,
648                                  FT_FROMFLOAT(sx * expansion),
649                                  FT_FROMFLOAT(sy * expansion),
650                                  72, 72);
651
652        if (error)
653                return NULL;
654
655        matrix.xx = (int)floor (affine[0] * scale + 0.5);
656        matrix.yx = -(int)floor (affine[1] * scale + 0.5);
657        matrix.xy = -(int)floor (affine[2] * scale + 0.5);
658        matrix.yy = (int)floor (affine[3] * scale + 0.5);
659        delta.x = FT_FROMFLOAT(affine[4]);
660        delta.y = FT_FROMFLOAT(-affine[5]);
661
662        FT_Set_Transform (face, &matrix, &delta);
663
664        /* Tell freetype to never load bitmaps when loading glyphs.  We only
665         * use the outlines of scalable fonts.  This means our code will work
666         * even for glyphs that have embedded bitmaps.
667         */
668        error = FT_Load_Glyph (face, glyph_ix, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP);
669        if (error)
670                return NULL;
671
672        glyph = face->glyph;
673
674        x0 = FT_TRUNC(FT_FLOOR(glyph->metrics.horiBearingX));
675        x1 = FT_TRUNC(FT_CEIL(glyph->metrics.horiBearingX + glyph->metrics.width));
676        y0 = FT_TRUNC(FT_FLOOR(glyph->metrics.horiBearingY - glyph->metrics.height));
677        y1 = FT_TRUNC(FT_CEIL(glyph->metrics.horiBearingY));
678        width = x1 - x0;
679        height = y1 - y0;
680
681
682        if (glyph->format == ft_glyph_format_outline) {
683                FT_Bitmap *bitmap;
684                guchar *buf;
685                int bufsize;
686
687                error = FT_Render_Glyph (glyph, ft_render_mode_normal);
688                if (error) {
689                        return NULL;
690                }
691
692                bitmap = &glyph->bitmap;
693
694                result = g_new (RsvgFTGlyph, 1);
695                result->refcnt = 1;
696                xy[0] = glyph->bitmap_left;
697                xy[1] = -glyph->bitmap_top;
698                result->width = bitmap->width;
699                result->height = bitmap->rows;
700                result->xpen = FT_TOFLOAT (glyph->advance.x);
701                result->ypen = -FT_TOFLOAT (glyph->advance.y);
702                result->rowstride = bitmap->pitch;
703                bufsize = bitmap->pitch * bitmap->rows;
704                buf = g_malloc (bufsize);
705                memcpy (buf, bitmap->buffer, bufsize);
706                result->buf = buf;
707        }
708        return result;
709}
710
711/**
712 * rsvg_ft_get_glyph_cached: Get a rendered glyph, trying the cache.
713 * @ctx: The RsvgFT context.
714 * @fh: Font handle for the font.
715 * @cache_ix: Glyph index to use in the cache.
716 * @glyph_ix: Glyph index to use in the font.
717 * @sx: Width of em in pixels.
718 * @sy: Height of em in pixels.
719 * @affine: Affine transformation.
720 * @xy: Where to store the top left coordinates.
721 *
722 * Note: The nominal resolution is 72 dpi. For rendering at other resolutions,
723 * scale the @affine by resolution/(72 dpi).
724 *
725 * Return value: The rendered glyph.
726 **/
727static RsvgFTGlyph *
728rsvg_ft_get_glyph_cached (RsvgFTCtx *ctx, RsvgFTFontHandle fh,
729                          FT_UInt cache_ix, FT_UInt glyph_ix,
730                          double sx, double sy,
731                          const double affine[6], int xy[2])
732{
733        RsvgFTGlyphDesc desc;
734        RsvgFTFont *font;
735        RsvgFTGlyph *result;
736        int x_sp;
737
738        if (affine[1] != 0 || affine[2] != 0 || affine[0] != affine[3]) {
739                font = rsvg_ft_font_resolve (ctx, fh);
740                return rsvg_ft_get_glyph (font, glyph_ix, sx, sy, affine, xy);
741        }
742        desc.fh = fh;
743        desc.char_width = floor (sx * 64 + 0.5);
744        desc.char_height = floor (sy * 64 + 0.5);
745        desc.glyph_index = cache_ix;
746        x_sp = floor (SUBPIXEL_FRACTION * (affine[4] - floor (affine[4])));
747        desc.x_subpixel = x_sp;
748        desc.y_subpixel = 0;
749#ifdef VERBOSE
750        g_print ("affine[4] = %g, x subpix = %x\n",
751                 affine[4], desc.x_subpixel);
752#endif
753        result = rsvg_ft_glyph_lookup (ctx, &desc, xy);
754        if (result == NULL) {
755                int x0, y0;
756                double my_affine[6];
757
758                memcpy (my_affine, affine, sizeof(my_affine));
759                my_affine[4] = floor (affine[4]) +
760                        (1.0 / SUBPIXEL_FRACTION) * x_sp;
761                font = rsvg_ft_font_resolve (ctx, fh);
762                result = rsvg_ft_get_glyph (font, glyph_ix, sx, sy, my_affine, xy);
763                if (result == NULL)
764                        return NULL;
765                x0 = xy[0] - floor (affine[4]);
766                y0 = xy[1] - floor (affine[5]);
767                rsvg_ft_glyph_insert (ctx, &desc, result, x0, y0);
768        } else {
769                xy[0] += floor (affine[4]);
770                xy[1] += floor (affine[5]);
771        }
772        result->refcnt++;
773        return result;
774}
775
776/**
777 * rsvg_ft_measure_or_render_string: Render a string into a glyph image.
778 * @ctx: The Rsvg FT context.
779 * @fh: Font handle for the font.
780 * @str: String, in ISO-8859-1 encoding.
781 * @sx: Width of em in pixels.
782 * @sy: Height of em in pixels.
783 * @affine: Affine transformation.
784 * @xy: Where to store the top left coordinates.
785 * &do_render: A boolean value indicating whether we should render the string.
786 * &dimensions: Where to store the resulting glyph's dimensions.
787 *
788 * Return value: A glyph containing the rendered string.
789 *
790 * This function does the rendering of a string into a glyph.  It can
791 * also work in measure only mode, so that no time is spent compositing
792 * bitmaps.  This is useful for callers that only want to measure a
793 * string.
794 *
795 **/
796static RsvgFTGlyph *
797rsvg_ft_measure_or_render_string (RsvgFTCtx *ctx,
798                                  RsvgFTFontHandle fh,
799                                  const char *str,
800                                  unsigned int length,
801                                  double sx, double sy,
802                                  const double affine[6],
803                                  int xy[2],
804                                  gboolean do_render,
805                                  unsigned int dimensions[2])
806{
807        RsvgFTFont *font;
808        RsvgFTGlyph *result;
809        RsvgFTGlyph **glyphs;
810        int *glyph_xy;
811        guint i;
812        ArtIRect bbox, glyph_bbox;
813        int rowstride;
814        guchar *buf;
815        double glyph_affine[6];
816        FT_UInt glyph_index, cache_index;
817        FT_UInt last_glyph = 0; /* for kerning */
818        guint n_glyphs;
819        double init_x, init_y;
820        int pixel_width, pixel_height, pixel_baseline;
821        int pixel_underline_position, pixel_underline_thickness;
822        int wclength;
823        wchar_t *wcstr;
824        char *mbstr;
825
826        g_return_val_if_fail (ctx != NULL, NULL);
827        g_return_val_if_fail (str != NULL, NULL);
828        g_return_val_if_fail (length <= strlen (str), NULL);
829
830        dimensions[0] = 0;
831        dimensions[1] = 0;
832
833        font = rsvg_ft_font_resolve (ctx, fh);
834        if (font == NULL)
835                return NULL;
836
837        /* Set the font to the correct size then generate the
838         * vertical pixel positioning metrics we need. Use 72dpi
839         * so that pixels == points
840         */
841        FT_Set_Char_Size (font->face,
842                          FT_FROMFLOAT(sx),
843                          FT_FROMFLOAT(sy),
844                          72, 72);
845        pixel_height = FT_TOFLOAT (font->face->size->metrics.ascender
846                                   - font->face->size->metrics.descender) * affine[3];
847        pixel_baseline = FT_TOFLOAT (font->face->size->metrics.ascender) * affine[3];
848
849        pixel_underline_position = ((font->face->ascender
850                                     - font->face->underline_position
851                                     - font->face->underline_thickness / 2) * sy
852                                     / font->face->units_per_EM) * affine[3];
853
854        pixel_underline_thickness = (font->face->underline_thickness * sy
855                                     / font->face->units_per_EM) * affine[3];
856        pixel_underline_thickness = MAX (1, pixel_underline_thickness);
857
858        bbox.x0 = bbox.x1 = 0;
859        bbox.y0 = bbox.y1 = 0;
860
861        glyphs = g_new (RsvgFTGlyph *, length);
862        glyph_xy = g_new (int, length * 2);
863
864        for (i = 0; i < 6; i++)
865                glyph_affine[i] = affine[i];
866
867        init_x = affine[4];
868        init_y = affine[5];
869        n_glyphs = 0;
870
871        /* Since mbstowcs takes a NUL-terminated string, we must
872         * convert str into one before calling it.
873         */
874        wcstr = g_new (wchar_t, length);
875        mbstr = g_strndup (str, length);
876        wclength = mbstowcs (wcstr, mbstr, length);
877        g_free (mbstr);
878
879        /* mbstowcs fallback.  0 means not found any wide chars.
880         * -1 means an invalid sequence was found.  In either of
881         * these two cases we fill in the wide char array with
882         * the single byte chars.
883         */
884        if (wclength > 0) {
885                length = wclength;
886        } else {
887                for (i = 0; i < length; i++) {
888                        wcstr[i] = (unsigned char) str[i];
889                }
890        }
891       
892        for (i = 0; i < length; i++) {
893                RsvgFTGlyph *glyph;
894               
895                glyph_index = FT_Get_Char_Index (font->face, wcstr[i]);
896
897                /* FIXME bugzilla.eazel.com 2775: Need a better way to deal
898                 * with unknown characters.
899                 *
900                 * The following is just a band aid fix.
901                 */
902                if (glyph_index == 0) {
903                        glyph_index = FT_Get_Char_Index (font->face, '?');
904                }
905
906                if (last_glyph != 0 && glyph_index != 0) {
907                        FT_Vector kern;
908                        double kx, ky;
909
910                        /* note: ft_kerning_unscaled seems to do the
911                           right thing with non-trivial affine transformations.
912                           However, ft_kerning_default may be a better choice
913                           for straight text rendering. This probably needs
914                           a little more thought. */
915                        FT_Get_Kerning (font->face, last_glyph, glyph_index,
916                                        ft_kerning_unscaled,
917                                        &kern);
918/*                      fprintf (stderr, "kern = (%ld, %ld)\n", kern.x, kern.y); */
919                        kx = FT_TOFLOAT (kern.x);
920                        ky = FT_TOFLOAT (kern.y);
921                        glyph_affine[4] += glyph_affine[0] * kx +
922                                glyph_affine[2] * ky;
923                        glyph_affine[5] += glyph_affine[1] * kx +
924                                glyph_affine[3] * ky;
925                }
926                if (glyph_index != 0) {
927                        last_glyph = glyph_index;
928
929                        glyph = rsvg_ft_get_glyph_cached (ctx, fh, glyph_index,
930                                                          glyph_index,
931                                                          sx, sy, glyph_affine,
932                                                          glyph_xy + n_glyphs * 2);
933
934                        /* Evil hack to handle fonts that don't define glyphs
935                         * for ` ' characters. Ask for `-', zero the pixels, then
936                         * enter it in the cache under the glyph index of ` '
937                         *
938                         * (The reason that this is needed is that at least some
939                         * microsoft TrueType fonts give ` ' an index, but don't
940                         * give it an actual glyph definition. Presumably they
941                         * just use some kind of metric when spacing)
942                         */
943                        if (glyph == NULL && wcstr[i] == ' ') {
944                                cache_index = glyph_index;
945                                glyph_index = FT_Get_Char_Index (font->face, '-');
946                                if (glyph_index != 0) {
947                                        glyph = rsvg_ft_get_glyph_cached (ctx, fh, cache_index,
948                                                                          glyph_index, sx, sy,
949                                                                          glyph_affine,
950                                                                          glyph_xy + n_glyphs * 2);
951                                        if (glyph != NULL) {
952                                                memset (glyph->buf, 0, glyph->height * glyph->rowstride);
953                                        }
954                                }
955                        }
956
957                        if (glyph != NULL) {
958                                glyphs[n_glyphs] = glyph;
959
960                                glyph_bbox.x0 = glyph_xy[n_glyphs * 2];
961                                glyph_bbox.y0 = glyph_xy[n_glyphs * 2 + 1];
962                                glyph_bbox.x1 = glyph_bbox.x0 + glyph->width;
963                                glyph_bbox.y1 = glyph_bbox.y0 + glyph->height;
964
965                                art_irect_union (&bbox, &bbox, &glyph_bbox);
966
967#ifdef VERBOSE
968                                g_print ("char '%c' bbox: (%d, %d) - (%d, %d)\n",
969                                         str[i],
970                                         glyph_bbox.x0, glyph_bbox.y0,
971                                         glyph_bbox.x1, glyph_bbox.y1);
972#endif
973
974                                glyph_affine[4] += glyph->xpen;
975                                glyph_affine[5] += glyph->ypen;
976
977                                n_glyphs++;
978                        }
979                } else {
980                        g_print ("no glyph loaded for character '%c'\n",
981                                 str[i]);
982                }
983        }
984
985        xy[0] = bbox.x0;
986        xy[1] = bbox.y0;
987
988        /* Some callers of this function expect to get something with
989         * non-zero width and height. So force the returned glyph to
990         * be at least one pixel wide and tall.
991         */
992        pixel_width = MAX (1, bbox.x1 - bbox.x0);
993        pixel_height = MAX (1, pixel_height);
994
995        dimensions[0] = pixel_width;
996        dimensions[1] = pixel_height;
997
998        g_free (wcstr);
999       
1000        /* Skip the glyph compositing loop for the case when all we
1001         * are doing is measuring strings.
1002         */
1003        if (!do_render) {
1004                for (i = 0; i < n_glyphs; i++) {
1005                        rsvg_ft_glyph_unref (glyphs[i]);
1006                }
1007                g_free (glyphs);
1008                g_free (glyph_xy);
1009                return NULL;
1010        }
1011
1012        rowstride = (pixel_width + 3) & -4;
1013        buf = g_malloc0 (rowstride * pixel_height);
1014
1015        result = g_new (RsvgFTGlyph, 1);
1016        result->refcnt = 1;
1017        result->width = pixel_width;
1018        result->height = pixel_height;
1019        result->xpen = glyph_affine[4] - init_x;
1020        result->ypen = glyph_affine[5] - init_y;
1021        result->rowstride = rowstride;
1022        result->buf = buf;
1023        result->underline_position = pixel_underline_position;
1024        result->underline_thickness = pixel_underline_thickness;
1025
1026        for (i = 0; i < n_glyphs; i++) {
1027                rsvg_ft_glyph_composite (result, glyphs[i],
1028                                         glyph_xy[i * 2] - bbox.x0,
1029                                         glyph_xy[i * 2 + 1]
1030                                         + pixel_baseline - affine[5]);
1031                rsvg_ft_glyph_unref (glyphs[i]);
1032        }
1033
1034        g_free (glyphs);
1035        g_free (glyph_xy);
1036
1037        return result;
1038}
1039
1040/**
1041 * rsvg_ft_render_string: Render a string into a glyph image.
1042 * @ctx: The Rsvg FT context.
1043 * @fh: Font handle for the font.
1044 * @str: String, in ISO-8859-1 encoding.
1045 * @sx: Width of em in pixels.
1046 * @sy: Height of em in pixels.
1047 * @affine: Affine transformation.
1048 * @xy: Where to store the top left coordinates.
1049 *
1050 * Return value: A glyph containing the rendered string.
1051 **/
1052RsvgFTGlyph *
1053rsvg_ft_render_string (RsvgFTCtx *ctx, RsvgFTFontHandle fh,
1054                       const char *str,
1055                       unsigned int length,
1056                       double sx, double sy,
1057                       const double affine[6], int xy[2])
1058{
1059        unsigned int unused[2];
1060        return rsvg_ft_measure_or_render_string (ctx, fh, str, length,
1061                                                 sx, sy, affine, xy,
1062                                                 TRUE, unused);
1063}
1064
1065/**
1066 * rsvg_ft_measure_string: Measure a string.
1067 * @ctx: The Rsvg FT context.
1068 * @fh: Font handle for the font.
1069 * @str: String, in ISO-8859-1 encoding.
1070 * @sx: Width of em in pixels.
1071 * @sy: Height of em in pixels.
1072 * @affine: Affine transformation.
1073 * @xy: Where to store the top left coordinates.
1074 * @dimensions: Where to store the string dimensions.
1075 **/
1076void
1077rsvg_ft_measure_string (RsvgFTCtx *ctx, RsvgFTFontHandle fh,
1078                        const char *str,
1079                        unsigned int length,
1080                        double sx, double sy,
1081                        const double affine[6],
1082                        int xy[2],
1083                        unsigned int dimensions[2])
1084{
1085        rsvg_ft_measure_or_render_string (ctx, fh, str, length, sx, sy,
1086                                          affine, xy, FALSE, dimensions);
1087}
1088
1089#if 0
1090void
1091rsvg_ft_font_ref (RsvgFTFont *font)
1092{
1093        font->refcnt++;
1094}
1095
1096void
1097rsvg_ft_font_unref (RsvgFTFont *font)
1098{
1099        if (--font->refcnt == 0) {
1100                FT_Done_Face (font->face);
1101                g_free (font);
1102        }
1103}
1104#endif
1105
1106void
1107rsvg_ft_glyph_unref (RsvgFTGlyph *glyph)
1108{
1109        if (--glyph->refcnt == 0) {
1110                g_free (glyph->buf);
1111                g_free (glyph);
1112        }
1113}
Note: See TracBrowser for help on using the repository browser.