diff options
author | Graydon Hoare <graydon@redhat.com> | 2004-10-08 12:09:49 +0000 |
---|---|---|
committer | Graydon Hoare <graydon@redhat.com> | 2004-10-08 12:09:49 +0000 |
commit | 30131aa4638f9bba6148114d3c60770592d6583b (patch) | |
tree | 8d9fb3abb43f732b7cdf81ba2dafe8190a09b010 | |
parent | 56ccb883761ff0781954705795f78b8e5a1591d4 (diff) |
Add cairo_cache.c
Rewrite using temporary glyph arrays
New file.
Remove old glyph cache code. (_cairo_font_scale) (_cairo_font_transform): Remove font-transforming code. (_cairo_font_text_extents) (_cairo_font_text_bbox) (_cairo_font_show_text) (_cairo_font_text_path): Remove text-API code. (_cairo_font_cache_key_t): New structure type. (_font_cache_hash) (_font_cache_keys_equal) (_font_cache_create_entry) (_font_cache_destroy_entry) (_font_cache_destroy_cache): New font cache code. (_global_font_cache) (_lock_global_font_cache) (_unlock_global_font_cache) (_get_global_font_cache): New global font cache. (_cairo_font_text_to_glyphs) (_cairo_glyph_cache_hash) (_cairo_glyph_cache_keys_equal) (_image_glyph_cache_create_entry) (_image_glyph_cache_destroy_entry) (_image_glyph_cache_destroy_cache): New glyph cache code. (_global_image_glyph_cache) (_cairo_lock_global_image_glyph_cache) (_cairo_unlock_global_image_glyph_cache) (_cairo_get_global_image_glyph_cache): New global glyph cache. (_cairo_font_cache_backend): New structure. (_cairo_image_cache_backend): Likewise. (_cairo_font_create): Reimplement in terms of font cache. (_cairo_font_init): Remove matrix and glyph cache related code. (_cairo_font_copy): Likewise. (_cairo_font_show_glyphs): Delegate to surface when possible. (_cairo_font_glyph_extents) (_cairo_font_glyph_bbox) (_cairo_font_glyph_path) (_cairo_font_font_extents) (_cairo_font_show_glyphs): Rename to as cairo_unscaled_font_XXX, and add scale parameter.
New structure types. (_create_from_face) (_reference_font_val) (_destroy_font_val) (_create_from_library_and_pattern): New functions. (_ft_font_cache_hash) (_ft_font_cache_keys_equal) (_ft_font_cache_create_entry) (_ft_font_cache_destroy_entry) (_ft_font_cache_destroy_cache): New ft font cache code. (_global_ft_cache) (_lock_global_ft_cache) (_unlock_global_ft_cache) (_get_global_ft_cache): New global ft font cache. (_ft_font_cache_backend): New structure. (_cairo_ft_font_create): Rewrite to use cache. (_cairo_ft_font_destroy): Likewise. (_cairo_ft_font_copy): Remove. (_install_font_matrix): Rename as _install_font_scale. (_utf8_to_glyphs): Rename as _cairo_ft_font_text_to_glyphs. (_cairo_ft_font_text_to_glyphs): Use cache for metrics. (_cairo_ft_font_extents): Accept size, use scaled metrics. (_cairo_ft_font_glyph_extents) (_cairo_ft_font_glyph_bbox) (_cairo_ft_font_show_glyphs) (_cairo_ft_font_glyph_path): Modify to use size, cache. (_cairo_ft_font_text_extents) (_cairo_ft_font_text_bbox) (_cairo_ft_font_show_text) (_cairo_ft_font_text_path): Remove text-API code. (cairo_ft_font_create) (cairo_ft_font_create_for_ft_face) (cairo_ft_font_face) (cairo_ft_font_pattern): Rewrite using ft_font_val_t.
Just reference font. (_cairo_gstate_fini): Finalize font matrix. (_cairo_gstate_default_matrix): Initialize font matrix. (_cairo_gstate_clip): Re-enable clipping rectangle. (_cairo_gstate_select_font) (_cairo_gstate_set_font): Set font matrix to identity. (_cairo_gstate_scale_font): Scale font matrix, not font. (_cairo_gstate_transform_font): Transform font matrix, not font. (_cairo_gstate_set_font_transform): Install as font matrix, not in font. (_build_font_scale): New helper function. (_cairo_gstate_text_to_glyphs): New function. (_cairo_gstate_current_font_extents) (_cairo_gstate_glyph_extents) (_cairo_gstate_show_glyphs) (_cairo_gstate_glyph_path): Rewrite using font matrix and size. (_cairo_gstate_text_path (_cairo_gstate_text_extents) (_cairo_gstate_show_text): Remove text-API code.
Minor bug fix. (_cairo_xlib_surface_show_glyphs): New function. (_cairo_xlib_surface_backend): Add reference to new function. (glyphset_cache_t) (glyphset_cache_entry_t): New structure types. (_next_xlib_glyph): New helper function. (_xlib_glyphset_cache_create_value) (_xlib_glyphset_cache_destroy_cache) (_xlib_glyphset_cache_destroy_value) (_xlib_glyphset_cache_backend): New glyphset cache code. (_xlib_glyphset_caches) (_lock_xlib_glyphset_caches) (_unlock_xlib_glyphset_caches) (_get_glyphset_cache): New global glyphset cache.
Add NULL entry for show_glyphs.
Add NULL entry for show_glyphs.
Add NULL entry for show_glyphs.
Add NULL entry for show_glyphs.
Add NULL entry for show_glyphs.
New structure type. (cairo_cache_entry_base_t) (cairo_cache_arrangement_t) (cairo_cache_t): New structure types. (_cairo_cache_init) (_cairo_cache_reference) (_cairo_cache_destroy) (_cairo_cache_lookup) (_cairo_hash_string): New cache functions. (CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT) (CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT) (CAIRO_FONT_CACHE_NUM_FONTS_DEFAULT) (CAIRO_FT_CACHE_NUM_FONTS_DEFAULT): New constants. (cairo_font_scale_t) (cairo_glyph_cache_key_t) (cairo_image_glyph_cache_entry_t): New structure types. (_cairo_lock_global_image_glyph_cache) (_cairo_unlock_global_image_glyph_cache) (_cairo_get_global_image_glyph_cache) (_cairo_glyph_cache_hash) (_cairo_glyph_cache_keys_equal): New functions for glyph caches. (cairo_font_backend_t): Remove text-API calls, add scale params, remove copy call. (cairo_surface_backend_t): Add show_glyphs entry. (cairo_glyph_surface_t) (cairo_glyph_surface_node_t): Remove old glyph cache structures. (cairo_unscaled_font_t): New structure type. (cairo_font): Remove glyph cache member, add pointer to unscaled. (cairo_gstate): Add font_matrix member, change to hold unscaled. (_cairo_gstate_set_font_transform) (_cairo_gstate_current_font_transform) (_cairo_gstate_text_to_glyphs): New functions. (_cairo_gstate_text_path (_cairo_gstate_text_extents) (_cairo_gstate_show_text) (_cairo_font_text_extents) (_cairo_font_text_bbox) (_cairo_font_show_text) (_cairo_font_text_path): Remove text-API code. (_cairo_font_glyph_extents) (_cairo_font_glyph_bbox) (_cairo_font_glyph_path) (_cairo_font_font_extents) (_cairo_font_show_glyphs): Add scale parameter.
-rw-r--r-- | ChangeLog | 190 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/cairo-cache.c | 454 | ||||
-rw-r--r-- | src/cairo-font.c | 659 | ||||
-rw-r--r-- | src/cairo-ft-font.c | 965 | ||||
-rw-r--r-- | src/cairo-glitz-surface.c | 3 | ||||
-rw-r--r-- | src/cairo-gstate.c | 526 | ||||
-rw-r--r-- | src/cairo-hash.c | 454 | ||||
-rw-r--r-- | src/cairo-image-surface.c | 3 | ||||
-rw-r--r-- | src/cairo-ps-surface.c | 3 | ||||
-rw-r--r-- | src/cairo-xcb-surface.c | 3 | ||||
-rw-r--r-- | src/cairo-xlib-surface.c | 529 | ||||
-rw-r--r-- | src/cairo.c | 53 | ||||
-rw-r--r-- | src/cairo_cache.c | 454 | ||||
-rw-r--r-- | src/cairo_font.c | 659 | ||||
-rw-r--r-- | src/cairo_ft_font.c | 965 | ||||
-rw-r--r-- | src/cairo_glitz_surface.c | 3 | ||||
-rw-r--r-- | src/cairo_gstate.c | 526 | ||||
-rw-r--r-- | src/cairo_image_surface.c | 3 | ||||
-rw-r--r-- | src/cairo_png_surface.c | 3 | ||||
-rw-r--r-- | src/cairo_ps_surface.c | 3 | ||||
-rw-r--r-- | src/cairo_xcb_surface.c | 3 | ||||
-rw-r--r-- | src/cairo_xlib_surface.c | 529 | ||||
-rw-r--r-- | src/cairoint.h | 413 |
24 files changed, 5485 insertions, 1919 deletions
@@ -1,3 +1,193 @@ +2004-10-07 Graydon Hoare <graydon@redhat.com> + + * src/Makefile.am (libcairo_la_SOURCES): Add cairo_cache.c + + * src/cairo.c + (cairo_text_extents) + (cairo_show_text) + (cairo_text_path): Rewrite using temporary glyph arrays + + * src/cairo_cache.c: New file. + + * src/cairo_font.c (_cairo_glyph_cache_create) + (_cairo_glyph_cache_destroy) + (_cairo_glyph_cache_reference) + (_cairo_glyph_cache_pop_last) + (_cairo_glyph_surface_init) + (_cairo_font_lookup_glyph): Remove old glyph cache code. + (_cairo_font_scale) + (_cairo_font_transform): Remove font-transforming code. + (_cairo_font_text_extents) + (_cairo_font_text_bbox) + (_cairo_font_show_text) + (_cairo_font_text_path): Remove text-API code. + (_cairo_font_cache_key_t): New structure type. + (_font_cache_hash) + (_font_cache_keys_equal) + (_font_cache_create_entry) + (_font_cache_destroy_entry) + (_font_cache_destroy_cache): New font cache code. + (_global_font_cache) + (_lock_global_font_cache) + (_unlock_global_font_cache) + (_get_global_font_cache): New global font cache. + (_cairo_font_text_to_glyphs) + (_cairo_glyph_cache_hash) + (_cairo_glyph_cache_keys_equal) + (_image_glyph_cache_create_entry) + (_image_glyph_cache_destroy_entry) + (_image_glyph_cache_destroy_cache): New glyph cache code. + (_global_image_glyph_cache) + (_cairo_lock_global_image_glyph_cache) + (_cairo_unlock_global_image_glyph_cache) + (_cairo_get_global_image_glyph_cache): New global glyph cache. + (_cairo_font_cache_backend): New structure. + (_cairo_image_cache_backend): Likewise. + (_cairo_font_create): Reimplement in terms of font cache. + (_cairo_font_init): Remove matrix and glyph cache related code. + (_cairo_font_copy): Likewise. + (_cairo_font_show_glyphs): Delegate to surface when possible. + (_cairo_font_glyph_extents) + (_cairo_font_glyph_bbox) + (_cairo_font_glyph_path) + (_cairo_font_font_extents) + (_cairo_font_show_glyphs): Rename to as cairo_unscaled_font_XXX, + and add scale parameter. + + * src/cairo_ft_font.c + (ft_cache_t) + (ft_font_val_t) + (cairo_ft_cache_key_t) + (cairo_ft_cache_entry_t): New structure types. + (_create_from_face) + (_reference_font_val) + (_destroy_font_val) + (_create_from_library_and_pattern): New functions. + (_ft_font_cache_hash) + (_ft_font_cache_keys_equal) + (_ft_font_cache_create_entry) + (_ft_font_cache_destroy_entry) + (_ft_font_cache_destroy_cache): New ft font cache code. + (_global_ft_cache) + (_lock_global_ft_cache) + (_unlock_global_ft_cache) + (_get_global_ft_cache): New global ft font cache. + (_ft_font_cache_backend): New structure. + (_cairo_ft_font_create): Rewrite to use cache. + (_cairo_ft_font_destroy): Likewise. + (_cairo_ft_font_copy): Remove. + (_install_font_matrix): Rename as _install_font_scale. + (_utf8_to_glyphs): Rename as _cairo_ft_font_text_to_glyphs. + (_cairo_ft_font_text_to_glyphs): Use cache for metrics. + (_cairo_ft_font_extents): Accept size, use scaled metrics. + (_cairo_ft_font_glyph_extents) + (_cairo_ft_font_glyph_bbox) + (_cairo_ft_font_show_glyphs) + (_cairo_ft_font_glyph_path): Modify to use size, cache. + (_cairo_ft_font_text_extents) + (_cairo_ft_font_text_bbox) + (_cairo_ft_font_show_text) + (_cairo_ft_font_text_path): Remove text-API code. + (cairo_ft_font_create) + (cairo_ft_font_create_for_ft_face) + (cairo_ft_font_face) + (cairo_ft_font_pattern): Rewrite using ft_font_val_t. + + * src/cairo_gstate.c (cairo_gstate_init_copy): Just reference font. + (_cairo_gstate_fini): Finalize font matrix. + (_cairo_gstate_default_matrix): Initialize font matrix. + (_cairo_gstate_clip): Re-enable clipping rectangle. + (_cairo_gstate_select_font) + (_cairo_gstate_set_font): Set font matrix to identity. + (_cairo_gstate_scale_font): Scale font matrix, not font. + (_cairo_gstate_transform_font): Transform font matrix, not font. + (_cairo_gstate_set_font_transform): Install as font matrix, not in font. + (_build_font_scale): New helper function. + (_cairo_gstate_text_to_glyphs): New function. + (_cairo_gstate_current_font_extents) + (_cairo_gstate_glyph_extents) + (_cairo_gstate_show_glyphs) + (_cairo_gstate_glyph_path): Rewrite using font matrix and size. + (_cairo_gstate_text_path + (_cairo_gstate_text_extents) + (_cairo_gstate_show_text): Remove text-API code. + + * src/cairo_xlib_surface.c + (_cairo_xlib_surface_set_clip_region): Minor bug fix. + (_cairo_xlib_surface_show_glyphs): New function. + (_cairo_xlib_surface_backend): Add reference to new function. + (glyphset_cache_t) + (glyphset_cache_entry_t): New structure types. + (_next_xlib_glyph): New helper function. + (_xlib_glyphset_cache_create_value) + (_xlib_glyphset_cache_destroy_cache) + (_xlib_glyphset_cache_destroy_value) + (_xlib_glyphset_cache_backend): New glyphset cache code. + (_xlib_glyphset_caches) + (_lock_xlib_glyphset_caches) + (_unlock_xlib_glyphset_caches) + (_get_glyphset_cache): New global glyphset cache. + + * src/cairo_glitz_surface.c (cairo_glitz_surface_backend): + Add NULL entry for show_glyphs. + + * src/cairo_image_surface.c (cairo_image_surface_backend): + Add NULL entry for show_glyphs. + + * src/cairo_ps_surface.c (cairo_ps_surface_backend): + Add NULL entry for show_glyphs. + + * src/cairo_png_surface.c (cairo_png_surface_backend): + Add NULL entry for show_glyphs. + + * src/cairo_xcb_surface.c (cairo_xcb_surface_backend): + Add NULL entry for show_glyphs. + + * src/cairoint.h (cairo_cache_backend_t): New structure type. + (cairo_cache_entry_base_t) + (cairo_cache_arrangement_t) + (cairo_cache_t): New structure types. + (_cairo_cache_init) + (_cairo_cache_reference) + (_cairo_cache_destroy) + (_cairo_cache_lookup) + (_cairo_hash_string): New cache functions. + (CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT) + (CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT) + (CAIRO_FONT_CACHE_NUM_FONTS_DEFAULT) + (CAIRO_FT_CACHE_NUM_FONTS_DEFAULT): New constants. + (cairo_font_scale_t) + (cairo_glyph_cache_key_t) + (cairo_image_glyph_cache_entry_t): New structure types. + (_cairo_lock_global_image_glyph_cache) + (_cairo_unlock_global_image_glyph_cache) + (_cairo_get_global_image_glyph_cache) + (_cairo_glyph_cache_hash) + (_cairo_glyph_cache_keys_equal): New functions for glyph caches. + (cairo_font_backend_t): Remove text-API calls, add scale params, + remove copy call. + (cairo_surface_backend_t): Add show_glyphs entry. + (cairo_glyph_surface_t) + (cairo_glyph_surface_node_t): Remove old glyph cache structures. + (cairo_unscaled_font_t): New structure type. + (cairo_font): Remove glyph cache member, add pointer to unscaled. + (cairo_gstate): Add font_matrix member, change to hold unscaled. + (_cairo_gstate_set_font_transform) + (_cairo_gstate_current_font_transform) + (_cairo_gstate_text_to_glyphs): New functions. + (_cairo_gstate_text_path + (_cairo_gstate_text_extents) + (_cairo_gstate_show_text) + (_cairo_font_text_extents) + (_cairo_font_text_bbox) + (_cairo_font_show_text) + (_cairo_font_text_path): Remove text-API code. + (_cairo_font_glyph_extents) + (_cairo_font_glyph_bbox) + (_cairo_font_glyph_path) + (_cairo_font_font_extents) + (_cairo_font_show_glyphs): Add scale parameter. + 2004-10-04 David Reveman <c99drn@cs.umu.se> * configure.in: Require version 0.2.3 of glitz. diff --git a/src/Makefile.am b/src/Makefile.am index 5dfa079e..8f5f639a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,6 +31,7 @@ XRENDER_LIBS=@XRENDER_LIBS@ libcairo_la_SOURCES = \ cairo.c \ cairo.h \ + cairo_cache.c \ cairo_color.c \ cairo_fixed.c \ cairo_font.c \ diff --git a/src/cairo-cache.c b/src/cairo-cache.c new file mode 100644 index 00000000..c5ce3993 --- /dev/null +++ b/src/cairo-cache.c @@ -0,0 +1,454 @@ +/* cairo - a vector graphics library with display and print output + * + * This file is Copyright © 2004 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Keith Packard + * Graydon Hoare <graydon@redhat.com> + */ + +#include "cairoint.h" + +/* + * This structure, and accompanying table, is borrowed/modified from the + * file xserver/render/glyph.c in the freedesktop.org x server, with + * permission (and suggested modification of doubling sizes) by Keith + * Packard. + */ + +static cairo_cache_arrangement_t cache_arrangements [] = { + { 16, 43, 41 }, + { 32, 73, 71 }, + { 64, 151, 149 }, + { 128, 283, 281 }, + { 256, 571, 569 }, + { 512, 1153, 1151 }, + { 1024, 2269, 2267 }, + { 2048, 4519, 4517 }, + { 4096, 9013, 9011 }, + { 8192, 18043, 18041 }, + { 16384, 36109, 36107 }, + { 32768, 72091, 72089 }, + { 65536, 144409, 144407 }, + { 131072, 288361, 288359 }, + { 262144, 576883, 576881 }, + { 524288, 1153459, 1153457 }, + { 1048576, 2307163, 2307161 }, + { 2097152, 4613893, 4613891 }, + { 4194304, 9227641, 9227639 }, + { 8388608, 18455029, 18455027 }, + { 16777216, 36911011, 36911009 }, + { 33554432, 73819861, 73819859 }, + { 67108864, 147639589, 147639587 }, + { 134217728, 295279081, 295279079 }, + { 268435456, 590559793, 590559791 } +}; + +#define N_CACHE_SIZES (sizeof(cache_arrangements)/sizeof(cache_arrangements[0])) + +/* + * Entries 'e' are poiners, in one of 3 states: + * + * e == NULL: The entry has never had anything put in it + * e != DEAD_ENTRY: The entry has an active value in it currently + * e == DEAD_ENTRY: The entry *had* a value in it at some point, but the + * entry has been killed. Lookups requesting free space can + * reuse these entries; lookups requesting a precise match + * should neither return these entries nor stop searching when + * seeing these entries. + * + * We expect keys will not be destroyed frequently, so our table does not + * contain any explicit shrinking code nor any chain-coalescing code for + * entries randomly deleted by memory pressure (except during rehashing, of + * course). These assumptions are potentially bad, but they make the + * implementation straightforward. + * + * Revisit later if evidence appears that we're using excessive memory from + * a mostly-dead table. + * + * Generally you do not need to worry about freeing cache entries; the + * cache will expire entries randomly as it experiences memory pressure. + * There is currently no explicit entry-removing call, though one can be + * added easily. + * + * This table is open-addressed with double hashing. Each table size is a + * prime chosen to be a little more than double the high water mark for a + * given arrangement, so the tables should remain < 50% full. The table + * size makes for the "first" hash modulus; a second prime (2 less than the + * first prime) serves as the "second" hash modulus, which is co-prime and + * thus guarantees a complete permutation of table indices. + * + */ + +#define DEAD_ENTRY ((cairo_cache_entry_base_t *) 1) +#define NULL_ENTRY_P(cache, i) ((cache)->entries[i] == NULL) +#define DEAD_ENTRY_P(cache, i) ((cache)->entries[i] == DEAD_ENTRY) +#define LIVE_ENTRY_P(cache, i) \ + (!((NULL_ENTRY_P((cache),(i))) || (DEAD_ENTRY_P((cache),(i))))) + +#ifdef CAIRO_DO_SANITY_CHECKING +#include <assert.h> +static void +_cache_sane_state (cairo_cache_t *cache) +{ + assert (cache != NULL); + assert (cache->entries != NULL); + assert (cache->backend != NULL); + assert (cache->arrangement != NULL); + assert (cache->refcount > 0); + assert (cache->used_memory <= cache->max_memory); + assert (cache->live_entries <= cache->arrangement->size); +} +#else +#define _cache_sane_state(c) +#define assert(x) +#endif + +static void +_entry_destroy (cairo_cache_t *cache, unsigned long i) +{ + _cache_sane_state (cache); + + if (LIVE_ENTRY_P(cache, i)) + { + cairo_cache_entry_base_t *entry = cache->entries[i]; + assert(cache->live_entries > 0); + assert(cache->used_memory > entry->memory); + + cache->live_entries--; + cache->used_memory -= entry->memory; + cache->backend->destroy_entry (cache, entry); + cache->entries[i] = DEAD_ENTRY; + } +} + +static cairo_cache_entry_base_t ** +_cache_lookup (cairo_cache_t *cache, + void *key, + int (*predicate)(void*,void*,void*)) +{ + + cairo_cache_entry_base_t **probe; + unsigned long hash; + unsigned long table_size, i, idx, step; + + _cache_sane_state (cache); + assert (key != NULL); + + table_size = cache->arrangement->size; + hash = cache->backend->hash (cache, key); + idx = hash % table_size; + step = 0; + + for (i = 0; i < table_size; ++i) + { +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + cache->probes++; +#endif + assert(idx < table_size); + probe = cache->entries + idx; + + /* + * There are two lookup modes: searching for a free slot and searching + * for an exact entry. + */ + + if (predicate != NULL) + { + /* We are looking up an exact entry. */ + if (*probe != NULL + && *probe != DEAD_ENTRY + && (*probe)->hashcode == hash + && predicate (cache, key, *probe)) + return probe; + } + else + { + /* We are just looking for a free slot. */ + if (*probe == NULL + || *probe == DEAD_ENTRY) + return probe; + } + + if (step == 0) { + step = hash % cache->arrangement->rehash; + if (step == 0) + step = 1; + } + + idx += step; + if (idx >= table_size) + idx -= table_size; + } + + /* + * The table should not have permitted you to get here if you were just + * looking for a free slot: there should have been room. + */ + assert(predicate != NULL); + return NULL; +} + +static cairo_cache_entry_base_t ** +_find_available_entry_for (cairo_cache_t *cache, + void *key) +{ + return _cache_lookup (cache, key, NULL); +} + +static cairo_cache_entry_base_t ** +_find_exact_live_entry_for (cairo_cache_t *cache, + void *key) +{ + return _cache_lookup (cache, key, cache->backend->keys_equal); +} + + +static cairo_cache_arrangement_t * +_find_cache_arrangement (unsigned long proposed_size) +{ + unsigned long idx; + + for (idx = 0; idx < N_CACHE_SIZES; ++idx) + if (cache_arrangements[idx].high_water_mark >= proposed_size) + return &cache_arrangements[idx]; + return NULL; +} + +static cairo_status_t +_resize_cache (cairo_cache_t *cache, unsigned long proposed_size) +{ + cairo_cache_t tmp; + cairo_cache_entry_base_t **e; + unsigned long new_size, i; + + tmp = *cache; + tmp.arrangement = _find_cache_arrangement (proposed_size); + assert(tmp.arrangement != NULL); + if (tmp.arrangement == cache->arrangement) + return CAIRO_STATUS_SUCCESS; + + new_size = tmp.arrangement->size; + tmp.entries = calloc (new_size, sizeof (cairo_cache_entry_base_t *)); + if (tmp.entries == NULL) + return CAIRO_STATUS_NO_MEMORY; + + for (i = 0; i < cache->arrangement->size; ++i) { + if (LIVE_ENTRY_P(cache, i)) { + e = _find_available_entry_for (&tmp, cache->entries[i]); + assert (e != NULL); + *e = cache->entries[i]; + } + } + free (cache->entries); + cache->entries = tmp.entries; + cache->arrangement = tmp.arrangement; + return CAIRO_STATUS_SUCCESS; +} + + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE +static double +_load_factor (cairo_cache_t *cache) +{ + return ((double) cache->live_entries) + / ((double) cache->arrangement->size); +} +#endif + +static unsigned long +_random_live_entry (cairo_cache_t *cache) +{ + unsigned long idx; + assert(cache != NULL); + do { + idx = rand () % cache->arrangement->size; + } while (! LIVE_ENTRY_P(cache, idx)); + return idx; +} + + +/* public API follows */ + +cairo_status_t +_cairo_cache_init (cairo_cache_t *cache, + const cairo_cache_backend_t *backend, + unsigned long max_memory) +{ + assert(backend != NULL); + + if (cache != NULL){ + cache->arrangement = &cache_arrangements[0]; + cache->refcount = 1; + cache->max_memory = max_memory; + cache->used_memory = 0; + cache->live_entries = 0; + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + cache->hits = 0; + cache->misses = 0; + cache->probes = 0; +#endif + + cache->backend = backend; + cache->entries = calloc (sizeof(cairo_cache_entry_base_t *), + cache->arrangement->size); + if (cache->entries == NULL) + return CAIRO_STATUS_NO_MEMORY; + } + _cache_sane_state (cache); + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_cache_reference (cairo_cache_t *cache) +{ + _cache_sane_state (cache); + cache->refcount++; +} + +void +_cairo_cache_destroy (cairo_cache_t *cache) +{ + unsigned long i; + if (cache != NULL) { + + _cache_sane_state (cache); + + if (cache->refcount-- > 0) + return; + + for (i = 0; i < cache->arrangement->size; ++i) { + _entry_destroy (cache, i); + } + + free (cache->entries); + cache->entries = NULL; + cache->backend->destroy_cache (cache); + } +} + +cairo_status_t +_cairo_cache_lookup (cairo_cache_t *cache, + void *key, + void **entry_return) +{ + + unsigned long idx; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_cache_entry_base_t **slot = NULL, *new_entry; + + _cache_sane_state (cache); + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + if ((cache->hits + cache->misses) % 0xffff == 0) + printf("cache %p stats: size %ld, live %ld, load %.2f\n" + " mem %ld/%ld, hit %ld, miss %ld\n" + " probe %ld, %.2f probe/access\n", + cache, + cache->arrangement->size, + cache->live_entries, + _load_factor (cache), + cache->used_memory, + cache->max_memory, + cache->hits, + cache->misses, + cache->probes, + ((double) cache->probes) + / ((double) (cache->hits + + cache->misses + 1))); +#endif + + /* See if we have an entry in the table already. */ + slot = _find_exact_live_entry_for (cache, key); + if (slot != NULL) { +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + cache->hits++; +#endif + *entry_return = *slot; + return status; + } + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + cache->misses++; +#endif + + /* Build the new entry. */ + status = cache->backend->create_entry (cache, key, + entry_return); + if (status != CAIRO_STATUS_SUCCESS) + return status; + + new_entry = (cairo_cache_entry_base_t *) (*entry_return); + + /* Store the hash value in case the backend forgot. */ + new_entry->hashcode = cache->backend->hash (cache, key); + + /* Make some entries die if we're under memory pressure. */ + while (cache->live_entries > 0 && + ((cache->max_memory - cache->used_memory) < new_entry->memory)) { + idx = _random_live_entry (cache); + assert (idx < cache->arrangement->size); + _entry_destroy (cache, idx); + } + + assert(cache->max_memory >= (cache->used_memory + new_entry->memory)); + + /* Make room in the table for a new slot. */ + status = _resize_cache (cache, cache->live_entries + 1); + if (status != CAIRO_STATUS_SUCCESS) { + cache->backend->destroy_entry (cache, new_entry); + *entry_return = NULL; + return status; + } + + slot = _find_available_entry_for (cache, key); + assert(slot != NULL); + + /* Store entry in slot and increment statistics. */ + *slot = new_entry; + cache->live_entries++; + cache->used_memory += new_entry->memory; + + _cache_sane_state (cache); + + return status; +} + +unsigned long +_cairo_hash_string (const char *c) +{ + /* This is the djb2 hash. */ + unsigned long hash = 5381; + while (*c) + hash = ((hash << 5) + hash) + *c++; + return hash; +} + diff --git a/src/cairo-font.c b/src/cairo-font.c index 1b356698..c90a02bd 100644 --- a/src/cairo-font.c +++ b/src/cairo-font.c @@ -36,20 +36,60 @@ #include "cairoint.h" -static cairo_glyph_cache_t * -_cairo_glyph_cache_create (void); -static void -_cairo_glyph_cache_destroy (cairo_glyph_cache_t *glyph_cache); +/* First we implement a global font cache for named fonts. */ + +typedef struct { + cairo_cache_entry_base_t base; + const char *family; + cairo_font_slant_t slant; + cairo_font_weight_t weight; +} cairo_font_cache_key_t; + +typedef struct { + cairo_font_cache_key_t key; + cairo_unscaled_font_t *unscaled; +} cairo_font_cache_entry_t; + +static unsigned long +_font_cache_hash (void *cache, void *key) +{ + cairo_font_cache_key_t *in; + in = (cairo_font_cache_key_t *) key; + unsigned long hash; + + /* 1607 and 1451 are just a couple random primes. */ + hash = _cairo_hash_string (in->family); + hash += ((unsigned long) in->slant) * 1607; + hash += ((unsigned long) in->weight) * 1451; + return hash; +} -static void -_cairo_glyph_cache_reference (cairo_glyph_cache_t *glyph_cache); -cairo_font_t * -_cairo_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) +static int +_font_cache_keys_equal (void *cache, + void *k1, + void *k2) { + cairo_font_cache_key_t *a, *b; + a = (cairo_font_cache_key_t *) k1; + b = (cairo_font_cache_key_t *) k2; + + return (strcmp (a->family, b->family) == 0) + && (a->weight == b->weight) + && (a->slant == b->slant); +} + + +static cairo_status_t +_font_cache_create_entry (void *cache, + void *key, + void **return_value) +{ + cairo_font_cache_key_t *k; + cairo_font_cache_entry_t *entry; + k = (cairo_font_cache_key_t *) key; + const struct cairo_font_backend *backend = CAIRO_FONT_BACKEND_DEFAULT; /* XXX: The current freetype backend may return NULL, (for example @@ -58,365 +98,428 @@ _cairo_font_create (const char *family, * like to build in some sort fo font here, (even a really lame, * ugly one if necessary). */ - return backend->create (family, slant, weight); + entry = malloc (sizeof (cairo_font_cache_entry_t)); + if (entry == NULL) + goto FAIL; + + entry->key.slant = k->slant; + entry->key.weight = k->weight; + entry->key.family = strdup(k->family); + if (entry->key.family == NULL) + goto FREE_ENTRY; + + entry->unscaled = backend->create (k->family, k->slant, k->weight); + if (entry->unscaled == NULL) + goto FREE_FAMILY; + + /* Not sure how to measure backend font mem; use a simple count for now.*/ + entry->key.base.memory = 1; + *return_value = entry; + return CAIRO_STATUS_SUCCESS; + + FREE_FAMILY: + free ((void *) entry->key.family); + + FREE_ENTRY: + free (entry); + + FAIL: + return CAIRO_STATUS_NO_MEMORY; } -cairo_status_t -_cairo_font_init (cairo_font_t *font, - const struct cairo_font_backend *backend) +static void +_font_cache_destroy_entry (void *cache, + void *entry) { - cairo_matrix_set_identity (&font->matrix); - font->refcount = 1; - font->backend = backend; - font->glyph_cache = _cairo_glyph_cache_create (); - if (font->glyph_cache == NULL) - return CAIRO_STATUS_NO_MEMORY; + cairo_font_cache_entry_t *e; - return CAIRO_STATUS_SUCCESS; + e = (cairo_font_cache_entry_t *) entry; + _cairo_unscaled_font_destroy (e->unscaled); + free ((void *) e->key.family); + free (e); } -cairo_font_t * -_cairo_font_copy (cairo_font_t *font) +static void +_font_cache_destroy_cache (void *cache) { - cairo_font_t *newfont = NULL; - char *tmp = NULL; + free (cache); +} - if (font == NULL || font->backend->copy == NULL) - return NULL; - - newfont = font->backend->copy (font); - if (newfont == NULL) { - free (tmp); - return NULL; - } +const struct cairo_cache_backend cairo_font_cache_backend = { + _font_cache_hash, + _font_cache_keys_equal, + _font_cache_create_entry, + _font_cache_destroy_entry, + _font_cache_destroy_cache +}; - newfont->refcount = 1; - cairo_matrix_copy(&newfont->matrix, &font->matrix); - newfont->backend = font->backend; - if (newfont->glyph_cache) - _cairo_glyph_cache_destroy (newfont->glyph_cache); - - newfont->glyph_cache = font->glyph_cache; - _cairo_glyph_cache_reference (font->glyph_cache); - - return newfont; +static void +_lock_global_font_cache (void) +{ + /* FIXME: implement locking. */ } -cairo_status_t -_cairo_font_scale (cairo_font_t *font, double scale) +static void +_unlock_global_font_cache (void) { - return cairo_matrix_scale (&font->matrix, scale, scale); + /* FIXME: implement locking. */ } -cairo_status_t -_cairo_font_transform (cairo_font_t *font, cairo_matrix_t *matrix) +static cairo_cache_t * +_global_font_cache = NULL; + +static cairo_cache_t * +_get_global_font_cache (void) { - return cairo_matrix_multiply (&font->matrix, matrix, &font->matrix); + if (_global_font_cache == NULL) { + _global_font_cache = malloc (sizeof (cairo_cache_t)); + + if (_global_font_cache == NULL) + goto FAIL; + + if (_cairo_cache_init (_global_font_cache, + &cairo_font_cache_backend, + CAIRO_FONT_CACHE_NUM_FONTS_DEFAULT)) + goto FAIL; + } + + return _global_font_cache; + + FAIL: + if (_global_font_cache) + free (_global_font_cache); + _global_font_cache = NULL; + return NULL; } -cairo_status_t -_cairo_font_text_extents (cairo_font_t *font, - const unsigned char *utf8, - cairo_text_extents_t *extents) +/* Now the internal "unscaled + scale" font API */ + +cairo_unscaled_font_t * +_cairo_unscaled_font_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) { - return font->backend->text_extents(font, utf8, extents); + cairo_cache_t * cache; + cairo_font_cache_key_t key; + cairo_font_cache_entry_t *font; + cairo_status_t status; + + _lock_global_font_cache (); + cache = _get_global_font_cache (); + if (cache == NULL) { + _unlock_global_font_cache (); + return NULL; + } + + key.family = family; + key.slant = slant; + key.weight = weight; + + status = _cairo_cache_lookup (cache, &key, (void **) &font); + if (status) { + _unlock_global_font_cache (); + return NULL; + } + + _cairo_unscaled_font_reference (font->unscaled); + _unlock_global_font_cache (); + return font->unscaled; } -cairo_status_t -_cairo_font_glyph_extents (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) +void +_cairo_font_init (cairo_font_t *scaled, + cairo_font_scale_t *scale, + cairo_unscaled_font_t *unscaled) { - return font->backend->glyph_extents(font, glyphs, num_glyphs, extents); + scaled->scale = *scale; + scaled->unscaled = unscaled; + scaled->refcount = 1; } cairo_status_t -_cairo_font_text_bbox (cairo_font_t *font, - cairo_surface_t *surface, - double x, - double y, - const unsigned char *utf8, - cairo_box_t *bbox) +_cairo_unscaled_font_init (cairo_unscaled_font_t *font, + const struct cairo_font_backend *backend) { - return font->backend->text_bbox (font, surface, x, y, utf8, bbox); + font->refcount = 1; + font->backend = backend; + return CAIRO_STATUS_SUCCESS; } + cairo_status_t -_cairo_font_glyph_bbox (cairo_font_t *font, - cairo_surface_t *surface, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) +_cairo_unscaled_font_text_to_glyphs (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) { - return font->backend->glyph_bbox (font, surface, glyphs, num_glyphs, bbox); + return font->backend->text_to_glyphs (font, scale, utf8, glyphs, num_glyphs); } cairo_status_t -_cairo_font_show_text (cairo_font_t *font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - double x, - double y, - const unsigned char *utf8) +_cairo_unscaled_font_glyph_extents (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) { - return font->backend->show_text(font, operator, source, - surface, source_x, source_y, x, y, utf8); + return font->backend->glyph_extents(font, scale, glyphs, num_glyphs, extents); } + cairo_status_t -_cairo_font_show_glyphs (cairo_font_t *font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_unscaled_font_glyph_bbox (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) { - return font->backend->show_glyphs(font, operator, source, - surface, source_x, source_y, - glyphs, num_glyphs); + return font->backend->glyph_bbox (font, scale, glyphs, num_glyphs, bbox); } cairo_status_t -_cairo_font_text_path (cairo_font_t *font, - double x, - double y, - const unsigned char *utf8, - cairo_path_t *path) +_cairo_unscaled_font_show_glyphs (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + cairo_surface_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + cairo_glyph_t *glyphs, + int num_glyphs) { - return font->backend->text_path(font, x, y, utf8, path); + cairo_status_t status; + if (surface->backend->show_glyphs != NULL) { + status = surface->backend->show_glyphs (font, scale, operator, source, + surface, source_x, source_y, + glyphs, num_glyphs); + if (status == CAIRO_STATUS_SUCCESS) + return status; + } + + /* Surface display routine either does not exist or failed. */ + return font->backend->show_glyphs (font, scale, operator, source, + surface, source_x, source_y, + glyphs, num_glyphs); } cairo_status_t -_cairo_font_glyph_path (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) +_cairo_unscaled_font_glyph_path (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path) { - return font->backend->glyph_path(font, glyphs, num_glyphs, path); + return font->backend->glyph_path (font, scale, glyphs, num_glyphs, path); } cairo_status_t -_cairo_font_font_extents (cairo_font_t *font, - cairo_font_extents_t *extents) +_cairo_unscaled_font_font_extents (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_font_extents_t *extents) { - return font->backend->font_extents(font, extents); + return font->backend->font_extents(font, scale, extents); } -static void -_cairo_glyph_cache_pop_last (cairo_glyph_cache_t *glyph_cache) +void +_cairo_unscaled_font_reference (cairo_unscaled_font_t *font) { - if (glyph_cache->last) { - cairo_glyph_surface_node_t *remove = glyph_cache->last; - - cairo_surface_destroy (remove->s.surface); - glyph_cache->last = remove->prev; - if (glyph_cache->last) - glyph_cache->last->next = NULL; + font->refcount++; +} - free (remove); - glyph_cache->n_nodes--; - } +void +_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font) +{ + if (--(font->refcount) > 0) + return; + + if (font->backend) + font->backend->destroy (font); } -static cairo_glyph_cache_t * -_cairo_glyph_cache_create (void) -{ - cairo_glyph_cache_t *glyph_cache; - - glyph_cache = malloc (sizeof (cairo_glyph_cache_t)); - if (glyph_cache == NULL) - return NULL; - - glyph_cache->n_nodes = 0; - glyph_cache->first = NULL; - glyph_cache->last = NULL; - glyph_cache->cache_size = CAIRO_FONT_CACHE_SIZE_DEFAULT; - glyph_cache->ref_count = 1; - return glyph_cache; + +/* Public font API follows. */ + +void +cairo_font_reference (cairo_font_t *font) +{ + font->refcount++; } -static void -_cairo_glyph_cache_reference (cairo_glyph_cache_t *glyph_cache) +void +cairo_font_destroy (cairo_font_t *font) { - if (glyph_cache == NULL) + if (--(font->refcount) > 0) return; - glyph_cache->ref_count++; + if (font->unscaled) + _cairo_unscaled_font_destroy (font->unscaled); + + free (font); } -static void -_cairo_glyph_cache_destroy (cairo_glyph_cache_t *glyph_cache) +void +cairo_font_set_transform (cairo_font_t *font, + cairo_matrix_t *matrix) { - if (glyph_cache == NULL) - return; + double dummy; + cairo_matrix_get_affine (matrix, + &font->scale.matrix[0][0], + &font->scale.matrix[0][1], + &font->scale.matrix[1][0], + &font->scale.matrix[1][1], + &dummy, &dummy); +} - glyph_cache->ref_count--; - if (glyph_cache->ref_count) - return; +void +cairo_font_current_transform (cairo_font_t *font, + cairo_matrix_t *matrix) +{ + cairo_matrix_set_affine (matrix, + font->scale.matrix[0][0], + font->scale.matrix[0][1], + font->scale.matrix[1][0], + font->scale.matrix[1][1], + 0, 0); +} - while (glyph_cache->last) - _cairo_glyph_cache_pop_last (glyph_cache); - free (glyph_cache); +/* Now we implement functions to access a default global image & metrics + * cache. + */ + +unsigned long +_cairo_glyph_cache_hash (void *cache, void *key) +{ + cairo_glyph_cache_key_t *in; + in = (cairo_glyph_cache_key_t *) key; + return + ((unsigned long) in->unscaled) + ^ ((unsigned long) in->scale.matrix[0][0]) + ^ ((unsigned long) in->scale.matrix[0][1]) + ^ ((unsigned long) in->scale.matrix[1][0]) + ^ ((unsigned long) in->scale.matrix[1][1]) + ^ in->index; } -static void -_cairo_glyph_surface_init (cairo_font_t *font, - cairo_surface_t *surface, - const cairo_glyph_t *glyph, - cairo_glyph_surface_t *glyph_surface) +int +_cairo_glyph_cache_keys_equal (void *cache, + void *k1, + void *k2) { - cairo_surface_t *image; - - glyph_surface->surface = NULL; - glyph_surface->index = glyph->index; - glyph_surface->matrix[0][0] = font->matrix.m[0][0]; - glyph_surface->matrix[0][1] = font->matrix.m[0][1]; - glyph_surface->matrix[1][0] = font->matrix.m[1][0]; - glyph_surface->matrix[1][1] = font->matrix.m[1][1]; - - image = font->backend->create_glyph (font, glyph, &glyph_surface->size); - if (image == NULL) - return; - - if (surface->backend != image->backend) { - cairo_status_t status; - - glyph_surface->surface = - _cairo_surface_create_similar_scratch (surface, - CAIRO_FORMAT_A8, 0, - glyph_surface->size.width, - glyph_surface->size.height); - if (glyph_surface->surface == NULL) { - glyph_surface->surface = image; - return; - } - - status = _cairo_surface_set_image (glyph_surface->surface, - (cairo_image_surface_t *) image); - if (status) { - cairo_surface_destroy (glyph_surface->surface); - glyph_surface->surface = NULL; - } - cairo_surface_destroy (image); - } else - glyph_surface->surface = image; + cairo_glyph_cache_key_t *a, *b; + a = (cairo_glyph_cache_key_t *) k1; + b = (cairo_glyph_cache_key_t *) k2; + return (a->index == b->index) + && (a->unscaled == b->unscaled) + && (a->scale.matrix[0][0] == b->scale.matrix[0][0]) + && (a->scale.matrix[0][1] == b->scale.matrix[0][1]) + && (a->scale.matrix[1][0] == b->scale.matrix[1][0]) + && (a->scale.matrix[1][1] == b->scale.matrix[1][1]); } -cairo_surface_t * -_cairo_font_lookup_glyph (cairo_font_t *font, - cairo_surface_t *surface, - const cairo_glyph_t *glyph, - cairo_glyph_size_t *return_size) + +static cairo_status_t +_image_glyph_cache_create_entry (void *cache, + void *key, + void **return_value) { - cairo_glyph_surface_t glyph_surface; - cairo_glyph_cache_t *cache = font->glyph_cache; - cairo_glyph_surface_node_t *node; - - for (node = cache->first; node != NULL; node = node->next) { - cairo_glyph_surface_t *s = &node->s; - - if ((s->surface == NULL || s->surface->backend == surface->backend) && - s->index == glyph->index && - s->matrix[0][0] == font->matrix.m[0][0] && - s->matrix[0][1] == font->matrix.m[0][1] && - s->matrix[1][0] == font->matrix.m[1][0] && - s->matrix[1][1] == font->matrix.m[1][1]) { - - /* move node first in cache */ - if (node->prev) { - if (node->next == NULL) { - cache->last = node->prev; - node->prev->next = NULL; - } else { - node->prev->next = node->next; - node->next->prev = node->prev; - } - - node->prev = NULL; - node->next = cache->first; - cache->first = node; - if (node->next) - node->next->prev = node; - else - cache->last = node; - } - - cairo_surface_reference (s->surface); - *return_size = s->size; - - return s->surface; - } - } - - _cairo_glyph_surface_init (font, surface, glyph, &glyph_surface); + cairo_glyph_cache_key_t *k = (cairo_glyph_cache_key_t *) key; + cairo_image_glyph_cache_entry_t *im; + cairo_status_t status; - *return_size = glyph_surface.size; - - if (cache->cache_size > 0) { - if (cache->n_nodes == cache->cache_size) - _cairo_glyph_cache_pop_last (cache); - - node = malloc (sizeof (cairo_glyph_surface_node_t)); - if (node) { - cairo_surface_reference (glyph_surface.surface); - - /* insert node first in cache */ - node->s = glyph_surface; - node->prev = NULL; - node->next = cache->first; - cache->first = node; - if (node->next) - node->next->prev = node; - else - cache->last = node; - - cache->n_nodes++; - } + im = calloc (1, sizeof (cairo_image_glyph_cache_entry_t)); + if (im == NULL) + return CAIRO_STATUS_NO_MEMORY; + + im->key = *k; + status = im->key.unscaled->backend->create_glyph (im); + + if (status != CAIRO_STATUS_SUCCESS) { + free (im); + return status; } - - return glyph_surface.surface; + + _cairo_unscaled_font_reference (im->key.unscaled); + + im->key.base.memory = + sizeof (cairo_image_glyph_cache_entry_t) + + (im->image ? + sizeof (cairo_image_surface_t) + + 28 * sizeof (int) /* rough guess at size of pixman image structure */ + + (im->image->height * im->image->stride) : 0); + + *return_value = im; + + return CAIRO_STATUS_SUCCESS; } -/* public font interface follows */ -void -cairo_font_reference (cairo_font_t *font) +static void +_image_glyph_cache_destroy_entry (void *cache, + void *value) { - font->refcount++; + cairo_image_glyph_cache_entry_t *im; + + im = (cairo_image_glyph_cache_entry_t *) value; + _cairo_unscaled_font_destroy (im->key.unscaled); + cairo_surface_destroy (&(im->image->base)); + free (im); } -void -cairo_font_destroy (cairo_font_t *font) +static void +_image_glyph_cache_destroy_cache (void *cache) { - if (--(font->refcount) > 0) - return; + free (cache); +} - _cairo_glyph_cache_destroy (font->glyph_cache); +const cairo_cache_backend_t cairo_image_cache_backend = { + _cairo_glyph_cache_hash, + _cairo_glyph_cache_keys_equal, + _image_glyph_cache_create_entry, + _image_glyph_cache_destroy_entry, + _image_glyph_cache_destroy_cache +}; - if (font->backend->destroy) - font->backend->destroy (font); -} void -cairo_font_set_transform (cairo_font_t *font, - cairo_matrix_t *matrix) +_cairo_lock_global_image_glyph_cache() { - cairo_matrix_copy (&(font->matrix), matrix); + /* FIXME: implement locking. */ } void -cairo_font_current_transform (cairo_font_t *font, - cairo_matrix_t *matrix) +_cairo_unlock_global_image_glyph_cache() { - cairo_matrix_copy (matrix, &(font->matrix)); + /* FIXME: implement locking. */ +} + +static cairo_cache_t * +_global_image_glyph_cache = NULL; + +cairo_cache_t * +_cairo_get_global_image_glyph_cache () +{ + if (_global_image_glyph_cache == NULL) { + _global_image_glyph_cache = malloc (sizeof (cairo_cache_t)); + + if (_global_image_glyph_cache == NULL) + goto FAIL; + + if (_cairo_cache_init (_global_image_glyph_cache, + &cairo_image_cache_backend, + CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT)) + goto FAIL; + } + + return _global_image_glyph_cache; + + FAIL: + if (_global_image_glyph_cache) + free (_global_image_glyph_cache); + _global_image_glyph_cache = NULL; + return NULL; } diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index 5b0a7f64..74061350 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -31,116 +31,294 @@ #include FT_OUTLINE_H #include FT_IMAGE_H -typedef struct { - cairo_font_t base; +#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0)) +#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0) +#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) +#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) - FT_Library ft_library; - int owns_ft_library; +/* + * First we make a private, sharable implementation object which can be + * stored both in a private cache and in public font objects (including + * those connected to fonts we don't own) + */ + +typedef struct { + int refcount; FT_Face face; int owns_face; - FcPattern *pattern; -} cairo_ft_font_t; +} ft_font_val_t; -#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0)) -#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0) -#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) -#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) +static ft_font_val_t * +_create_from_face (FT_Face face, int owns_face) +{ + ft_font_val_t *tmp = malloc (sizeof(ft_font_val_t)); + if (tmp) { + tmp->refcount = 1; + tmp->face = face; + tmp->owns_face = owns_face; + FT_Set_Char_Size (face, + DOUBLE_TO_26_6 (1.0), + DOUBLE_TO_26_6 (1.0), + 0, 0); + } + return tmp; +} -/* implement the platform-specific interface */ +static void +_reference_font_val (ft_font_val_t *f) +{ + f->refcount++; +} -cairo_font_t * -cairo_ft_font_create (FT_Library ft_library, FcPattern *pattern) +static void +_destroy_font_val (ft_font_val_t *f) { - cairo_ft_font_t *f = NULL; + if (--(f->refcount) > 0) + return; + + if (f->owns_face) + FT_Done_Face (f->face); + + free (f); +} + +static ft_font_val_t * +_create_from_library_and_pattern (FT_Library ft_library, FcPattern *pattern) +{ + ft_font_val_t *f = NULL; char *filename = NULL; - FT_Face face = NULL; int owns_face = 0; + FT_Face face = NULL; FcPattern *resolved = NULL; FcResult result = FcResultMatch; - + + if (pattern == NULL) + goto FAIL; + FcConfigSubstitute (0, pattern, FcMatchPattern); FcDefaultSubstitute (pattern); resolved = FcFontMatch (0, pattern, &result); + if (!resolved) + goto FAIL; + if (result != FcResultMatch) - { - if (resolved) - FcPatternDestroy (resolved); - return NULL; - } + goto FREE_RESOLVED; /* If the pattern has an FT_Face object, use that. */ if (FcPatternGetFTFace (resolved, FC_FT_FACE, 0, &face) != FcResultMatch || face == NULL) { - /* otherwise it had better have a filename */ - int open_res = 0; - owns_face = 1; result = FcPatternGetString (resolved, FC_FILE, 0, (FcChar8 **)(&filename)); if (result == FcResultMatch) - open_res = FT_New_Face (ft_library, filename, 0, &face); + if (FT_New_Face (ft_library, filename, 0, &face)) + goto FREE_RESOLVED; if (face == NULL) - return NULL; + goto FREE_RESOLVED; + + owns_face = 1; } - f = (cairo_ft_font_t *) cairo_ft_font_create_for_ft_face (face); - if (f != NULL) - f->pattern = FcPatternDuplicate (resolved); + f = _create_from_face (face, owns_face); + + FcPatternDestroy (resolved); + return f; + + FREE_RESOLVED: + if (resolved) + FcPatternDestroy (resolved); + + FAIL: + return NULL; +} - f->ft_library = ft_library; - f->owns_ft_library = 0; - f->owns_face = owns_face; +/* + * We then make the user-exposed structure out of one of these impls, such + * that it is reasonably cheap to copy and/or destroy. Unfortunately this + * duplicates a certain amount of the caching machinery in the font cache, + * but that's unavoidable as we also provide an FcPattern resolution API, + * which is not part of cairo's generic font finding system. + */ - FcPatternDestroy (resolved); - return (cairo_font_t *) f; +typedef struct { + cairo_unscaled_font_t base; + FcPattern *pattern; + ft_font_val_t *val; +} cairo_ft_font_t; + +/* + * We then make a key and entry type which are compatible with the generic + * cache system. This cache serves to share single ft_font_val_t instances + * between fonts (or between font lifecycles). + */ + +typedef struct { + cairo_cache_entry_base_t base; + FcPattern *pattern; +} cairo_ft_cache_key_t; + +typedef struct { + cairo_ft_cache_key_t key; + ft_font_val_t *val; +} cairo_ft_cache_entry_t; + +/* + * Then we create a cache which maps FcPattern keys to the refcounted + * ft_font_val_t values. + */ + +typedef struct { + cairo_cache_t base; + FT_Library lib; +} ft_cache_t; + + +static unsigned long +_ft_font_cache_hash (void *cache, void *key) +{ + cairo_ft_cache_key_t *in; + in = (cairo_ft_cache_key_t *) key; + return FcPatternHash (in->pattern); } -FT_Face -cairo_ft_font_face (cairo_font_t *abstract_font) +static int +_ft_font_cache_keys_equal (void *cache, + void *k1, + void *k2) { - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + cairo_ft_cache_key_t *a; + cairo_ft_cache_key_t *b; + a = (cairo_ft_cache_key_t *) k1; + b = (cairo_ft_cache_key_t *) k2; + + return FcPatternEqual (a->pattern, b->pattern); +} - if (font == NULL) - return NULL; - return font->face; +static cairo_status_t +_ft_font_cache_create_entry (void *cache, + void *key, + void **return_entry) +{ + ft_cache_t *ftcache = (ft_cache_t *) cache; + cairo_ft_cache_key_t *k = (cairo_ft_cache_key_t *) key; + cairo_ft_cache_entry_t *entry; + + entry = malloc (sizeof (cairo_ft_cache_entry_t)); + if (entry == NULL) + return CAIRO_STATUS_NO_MEMORY; + + entry->key.pattern = FcPatternDuplicate (k->pattern); + if (!entry->key.pattern) { + free (entry); + return CAIRO_STATUS_NO_MEMORY; + } + + entry->val = _create_from_library_and_pattern (ftcache->lib, entry->key.pattern); + entry->key.base.memory = 1; + + *return_entry = entry; + + return CAIRO_STATUS_SUCCESS; } -FcPattern * -cairo_ft_font_pattern (cairo_font_t *abstract_font) +static void +_ft_font_cache_destroy_entry (void *cache, + void *entry) +{ + cairo_ft_cache_entry_t *e = (cairo_ft_cache_entry_t *) entry; + FcPatternDestroy (e->key.pattern); + _destroy_font_val (e->val); + free (e); +} + +static void +_ft_font_cache_destroy_cache (void *cache) { - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + ft_cache_t *fc = (ft_cache_t *) cache; + FT_Done_FreeType (fc->lib); + free (fc); +} - if (font == NULL) - return NULL; +const struct cairo_cache_backend _ft_font_cache_backend = { + _ft_font_cache_hash, + _ft_font_cache_keys_equal, + _ft_font_cache_create_entry, + _ft_font_cache_destroy_entry, + _ft_font_cache_destroy_cache +}; - return font->pattern; + +static ft_cache_t *_global_ft_cache = NULL; + +static void +_lock_global_ft_cache (void) +{ + /* FIXME: Perform locking here. */ +} + +static void +_unlock_global_ft_cache (void) +{ + /* FIXME: Perform locking here. */ +} + +static cairo_cache_t * +_get_global_ft_cache (void) +{ + if (_global_ft_cache == NULL) + { + _global_ft_cache = malloc (sizeof(ft_cache_t)); + if (!_global_ft_cache) + goto FAIL; + + if (_cairo_cache_init (&_global_ft_cache->base, + &_ft_font_cache_backend, + CAIRO_FT_CACHE_NUM_FONTS_DEFAULT)) + goto FAIL; + + if (FT_Init_FreeType (&_global_ft_cache->lib)) + goto FAIL; + } + return &_global_ft_cache->base; + + FAIL: + if (_global_ft_cache) + free (_global_ft_cache); + _global_ft_cache = NULL; + return NULL; } /* implement the backend interface */ -static cairo_font_t * +const struct cairo_font_backend cairo_ft_font_backend; + +static cairo_unscaled_font_t * _cairo_ft_font_create (const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { - cairo_ft_font_t *ft_font = NULL; - cairo_font_t *font = NULL; - FcPattern * pat = NULL; + cairo_status_t status; + cairo_ft_font_t *font = NULL; int fcslant; int fcweight; - FT_Library ft_library; - FT_Error error; + cairo_cache_t *cache; + cairo_ft_cache_entry_t *entry; + cairo_ft_cache_key_t key; - pat = FcPatternCreate (); - if (pat == NULL) - return NULL; + key.pattern = FcPatternCreate (); + if (key.pattern == NULL) + goto FAIL; + + font = malloc (sizeof (cairo_ft_font_t)); + if (font == NULL) + goto FREE_PATTERN; switch (weight) { @@ -167,52 +345,44 @@ _cairo_ft_font_create (const char *family, break; } - FcPatternAddString (pat, FC_FAMILY, family); - FcPatternAddInteger (pat, FC_SLANT, fcslant); - FcPatternAddInteger (pat, FC_WEIGHT, fcweight); + FcPatternAddString (key.pattern, FC_FAMILY, family); + FcPatternAddInteger (key.pattern, FC_SLANT, fcslant); + FcPatternAddInteger (key.pattern, FC_WEIGHT, fcweight); - error = FT_Init_FreeType (&ft_library); - if (error) { - FcPatternDestroy (pat); - return NULL; - } + if (_cairo_unscaled_font_init (&font->base, &cairo_ft_font_backend)) + goto FREE_PATTERN; - font = cairo_ft_font_create (ft_library, pat); - if (font == NULL) - return NULL; + _lock_global_ft_cache (); + cache = _get_global_ft_cache (); + if (cache == NULL) { + _unlock_global_ft_cache (); + goto FREE_PATTERN; + } - ft_font = (cairo_ft_font_t *) font; + status = _cairo_cache_lookup (cache, &key, (void **) &entry); + _unlock_global_ft_cache (); - ft_font->owns_ft_library = 1; + if (status) + goto FREE_PATTERN; - FT_Set_Char_Size (ft_font->face, - DOUBLE_TO_26_6 (1.0), - DOUBLE_TO_26_6 (1.0), - 0, 0); - - FcPatternDestroy (pat); - return font; -} + font->pattern = FcPatternDuplicate (entry->key.pattern); + if (font->pattern == NULL) + goto FREE_PATTERN; -static cairo_font_t * -_cairo_ft_font_copy (void *abstract_font) -{ - cairo_ft_font_t * font_new = NULL; - cairo_ft_font_t * font = abstract_font; - - if (font->base.backend != &cairo_ft_font_backend) - return NULL; + font->val = entry->val; + _reference_font_val (font->val); + + return &font->base; - font_new = (cairo_ft_font_t *) cairo_ft_font_create_for_ft_face (font->face); - if (font_new == NULL) - return NULL; + FREE_PATTERN: + FcPatternDestroy (key.pattern); - if (font_new != NULL && font->pattern != NULL) - font_new->pattern = FcPatternDuplicate (font->pattern); + FAIL: + return NULL; - return (cairo_font_t *) font_new; } + static void _cairo_ft_font_destroy (void *abstract_font) { @@ -220,15 +390,11 @@ _cairo_ft_font_destroy (void *abstract_font) if (font == NULL) return; - - if (font->face != NULL && font->owns_face) - FT_Done_Face (font->face); if (font->pattern != NULL) FcPatternDestroy (font->pattern); - if (font->ft_library && font->owns_ft_library) - FT_Done_FreeType (font->ft_library); + _destroy_font_val (font->val); free (font); } @@ -268,7 +434,7 @@ _utf8_to_ucs4 (char const *utf8, } static void -_install_font_matrix(cairo_matrix_t *matrix, FT_Face face) +_install_font_scale (cairo_font_scale_t *sc, FT_Face face) { cairo_matrix_t normalized; double scale_x, scale_y; @@ -276,16 +442,20 @@ _install_font_matrix(cairo_matrix_t *matrix, FT_Face face) FT_Matrix mat; /* The font matrix has x and y "scale" components which we extract and - * use as pixel scale values. These influence the way freetype chooses - * hints, as well as selecting different bitmaps in hand-rendered - * fonts. We also copy the normalized matrix to freetype's - * transformation. + * use as character scale values. These influence the way freetype + * chooses hints, as well as selecting different bitmaps in + * hand-rendered fonts. We also copy the normalized matrix to + * freetype's transformation. */ - _cairo_matrix_compute_scale_factors (matrix, &scale_x, &scale_y); - - cairo_matrix_copy (&normalized, matrix); + cairo_matrix_set_affine (&normalized, + sc->matrix[0][0], + sc->matrix[0][1], + sc->matrix[1][0], + sc->matrix[1][1], + 0, 0); + _cairo_matrix_compute_scale_factors (&normalized, &scale_x, &scale_y); cairo_matrix_scale (&normalized, 1.0 / scale_x, 1.0 / scale_y); cairo_matrix_get_affine (&normalized, &xx /* 00 */ , &yx /* 01 */, @@ -298,21 +468,28 @@ _install_font_matrix(cairo_matrix_t *matrix, FT_Face face) mat.yy = DOUBLE_TO_16_16(yy); FT_Set_Transform(face, &mat, NULL); - FT_Set_Char_Size(face, - DOUBLE_TO_26_6(scale_x), - DOUBLE_TO_26_6(scale_y), - 0, 0); + + FT_Set_Pixel_Sizes(face, + (FT_UInt) scale_x, + (FT_UInt) scale_y); } -static int -_utf8_to_glyphs (cairo_ft_font_t *font, - const unsigned char *utf8, - double x0, - double y0, - cairo_glyph_t **glyphs, - size_t *nglyphs) +static cairo_status_t +_cairo_ft_font_text_to_glyphs (void *abstract_font, + cairo_font_scale_t *sc, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *nglyphs) { - FT_Face face = font->face; + cairo_ft_font_t *font = abstract_font; + FT_Face face = font->val->face; + cairo_glyph_cache_key_t key; + cairo_image_glyph_cache_entry_t *val; + cairo_cache_t *cache; + + key.unscaled = &font->base; + key.scale = *sc; + double x = 0., y = 0.; size_t i; FT_ULong *ucs4 = NULL; @@ -320,56 +497,70 @@ _utf8_to_glyphs (cairo_ft_font_t *font, _utf8_to_ucs4 (utf8, &ucs4, nglyphs); if (ucs4 == NULL) - return 0; + return CAIRO_STATUS_NO_MEMORY; *glyphs = (cairo_glyph_t *) malloc ((*nglyphs) * (sizeof (cairo_glyph_t))); if (*glyphs == NULL) { free (ucs4); - return 0; + return CAIRO_STATUS_NO_MEMORY; } - _install_font_matrix (&font->base.matrix, face); + _cairo_lock_global_image_glyph_cache (); + cache = _cairo_get_global_image_glyph_cache (); + if (cache == NULL) { + _cairo_unlock_global_image_glyph_cache (); + return CAIRO_STATUS_NO_MEMORY; + } for (i = 0; i < *nglyphs; i++) { (*glyphs)[i].index = FT_Get_Char_Index (face, ucs4[i]); - (*glyphs)[i].x = x0 + x; - (*glyphs)[i].y = y0 + y; - - FT_Load_Glyph (face, (*glyphs)[i].index, FT_LOAD_DEFAULT); + (*glyphs)[i].x = x; + (*glyphs)[i].y = y; - x += DOUBLE_FROM_26_6 (face->glyph->advance.x); - y -= DOUBLE_FROM_26_6 (face->glyph->advance.y); + val = NULL; + key.index = (*glyphs)[i].index; + + if (_cairo_cache_lookup (cache, &key, (void **) &val) + != CAIRO_STATUS_SUCCESS || val == NULL) + continue; + + x += val->extents.x_advance; + y -= val->extents.y_advance; } + _cairo_unlock_global_image_glyph_cache (); free (ucs4); - return 1; + return CAIRO_STATUS_SUCCESS; } + static cairo_status_t _cairo_ft_font_font_extents (void *abstract_font, + cairo_font_scale_t *sc, cairo_font_extents_t *extents) { cairo_ft_font_t *font = abstract_font; - FT_Face face = font->face; - double scale_x, scale_y; + FT_Face face = font->val->face; + FT_Size_Metrics *metrics = &face->size->metrics; - double upm = face->units_per_EM; + _install_font_scale (sc, face); - _cairo_matrix_compute_scale_factors (&font->base.matrix, &scale_x, &scale_y); + extents->ascent = DOUBLE_FROM_26_6(metrics->ascender); + extents->descent = DOUBLE_FROM_26_6(metrics->descender); + extents->height = DOUBLE_FROM_26_6(metrics->height); + extents->max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance); - extents->ascent = face->ascender / upm * scale_y; - extents->descent = face->descender / upm * scale_y; - extents->height = face->height / upm * scale_y; - extents->max_x_advance = face->max_advance_width / upm * scale_x; - extents->max_y_advance = face->max_advance_height / upm * scale_y; + /* FIXME: this doesn't do vertical layout atm. */ + extents->max_y_advance = 0.0; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_ft_font_glyph_extents (void *abstract_font, + cairo_font_scale_t *sc, cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents) @@ -379,10 +570,10 @@ _cairo_ft_font_glyph_extents (void *abstract_font, cairo_point_double_t origin; cairo_point_double_t glyph_min, glyph_max; cairo_point_double_t total_min, total_max; - FT_Error error; - FT_Face face = font->face; - FT_GlyphSlot glyph = face->glyph; - FT_Glyph_Metrics *metrics = &glyph->metrics; + + cairo_image_glyph_cache_entry_t *img = NULL; + cairo_cache_t *cache; + cairo_glyph_cache_key_t key; if (num_glyphs == 0) { @@ -399,24 +590,33 @@ _cairo_ft_font_glyph_extents (void *abstract_font, origin.x = glyphs[0].x; origin.y = glyphs[0].y; - _install_font_matrix (&font->base.matrix, face); + _cairo_lock_global_image_glyph_cache (); + cache = _cairo_get_global_image_glyph_cache (); + if (cache == NULL) { + _cairo_unlock_global_image_glyph_cache (); + return CAIRO_STATUS_NO_MEMORY; + } + + key.unscaled = &font->base; + key.scale = *sc; for (i = 0; i < num_glyphs; i++) { - error = FT_Load_Glyph (face, glyphs[i].index, FT_LOAD_DEFAULT); - /* XXX: What to do in this error case? */ - if (error) + img = NULL; + key.index = glyphs[i].index; + if (_cairo_cache_lookup (cache, &key, (void **) &img) + != CAIRO_STATUS_SUCCESS || img == NULL) continue; - + /* XXX: Need to add code here to check the font's FcPattern for FC_VERTICAL_LAYOUT and if set get vertBearingX/Y instead. This will require that cairo_ft_font_create_for_ft_face accept an FcPattern. */ - glyph_min.x = glyphs[i].x + DOUBLE_FROM_26_6 (metrics->horiBearingX); - glyph_min.y = glyphs[i].y - DOUBLE_FROM_26_6 (metrics->horiBearingY); - glyph_max.x = glyph_min.x + DOUBLE_FROM_26_6 (metrics->width); - glyph_max.y = glyph_min.y + DOUBLE_FROM_26_6 (metrics->height); + glyph_min.x = glyphs[i].x + img->extents.x_bearing; + glyph_min.y = glyphs[i].y - img->extents.y_bearing; + glyph_max.x = glyph_min.x + img->extents.width; + glyph_max.y = glyph_min.y + img->extents.height; if (i==0) { total_min = glyph_min; @@ -433,12 +633,13 @@ _cairo_ft_font_glyph_extents (void *abstract_font, total_max.y = glyph_max.y; } } + _cairo_unlock_global_image_glyph_cache (); extents->x_bearing = total_min.x - origin.x; extents->y_bearing = total_min.y - origin.y; extents->width = total_max.x - total_min.x; extents->height = total_max.y - total_min.y; - extents->x_advance = glyphs[i-1].x + DOUBLE_FROM_26_6 (metrics->horiAdvance) - origin.x; + extents->x_advance = glyphs[i-1].x + (img == NULL ? 0 : img->extents.x_advance) - origin.x; extents->y_advance = glyphs[i-1].y + 0 - origin.y; return CAIRO_STATUS_SUCCESS; @@ -446,34 +647,16 @@ _cairo_ft_font_glyph_extents (void *abstract_font, static cairo_status_t -_cairo_ft_font_text_extents (void *abstract_font, - const unsigned char *utf8, - cairo_text_extents_t *extents) -{ - cairo_ft_font_t *font = abstract_font; - cairo_glyph_t *glyphs; - size_t nglyphs; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (_utf8_to_glyphs (font, utf8, 0, 0, &glyphs, &nglyphs)) - { - status = _cairo_ft_font_glyph_extents (font, glyphs, nglyphs, - extents); - free (glyphs); - } - return status; -} - -static cairo_status_t -_cairo_ft_font_glyph_bbox (void *abstract_font, - cairo_surface_t *surface, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) +_cairo_ft_font_glyph_bbox (void *abstract_font, + cairo_font_scale_t *sc, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) { + cairo_image_glyph_cache_entry_t *img; + cairo_cache_t *cache; + cairo_glyph_cache_key_t key; cairo_ft_font_t *font = abstract_font; - cairo_surface_t *mask = NULL; - cairo_glyph_size_t size; cairo_fixed_t x1, y1, x2, y2; int i; @@ -481,22 +664,33 @@ _cairo_ft_font_glyph_bbox (void *abstract_font, bbox->p1.x = bbox->p1.y = CAIRO_MAXSHORT << 16; bbox->p2.x = bbox->p2.y = CAIRO_MINSHORT << 16; - if (font == NULL - || surface == NULL - || glyphs == NULL) + _cairo_lock_global_image_glyph_cache (); + cache = _cairo_get_global_image_glyph_cache(); + + if (cache == NULL + || font == NULL + || glyphs == NULL) { + _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_NO_MEMORY; + } + + key.unscaled = &font->base; + key.scale = *sc; for (i = 0; i < num_glyphs; i++) { - mask = _cairo_font_lookup_glyph (&font->base, surface, - &glyphs[i], &size); - if (mask == NULL) + + img = NULL; + key.index = glyphs[i].index; + + if (_cairo_cache_lookup (cache, &key, (void **) &img) + != CAIRO_STATUS_SUCCESS || img == NULL) continue; - x1 = _cairo_fixed_from_double (glyphs[i].x + size.x); - y1 = _cairo_fixed_from_double (glyphs[i].y - size.y); - x2 = x1 + _cairo_fixed_from_double (size.width); - y2 = y1 + _cairo_fixed_from_double (size.height); + x1 = _cairo_fixed_from_double (glyphs[i].x + img->size.x); + y1 = _cairo_fixed_from_double (glyphs[i].y - img->size.y); + x2 = x1 + _cairo_fixed_from_double (img->size.width); + y2 = y1 + _cairo_fixed_from_double (img->size.height); if (x1 < bbox->p1.x) bbox->p1.x = x1; @@ -509,117 +703,82 @@ _cairo_ft_font_glyph_bbox (void *abstract_font, if (y2 > bbox->p2.y) bbox->p2.y = y2; - - if (mask) - cairo_surface_destroy (mask); } + _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_ft_font_text_bbox (void *abstract_font, - cairo_surface_t *surface, - double x0, - double y0, - const unsigned char *utf8, - cairo_box_t *bbox) -{ - cairo_ft_font_t *font = abstract_font; - cairo_glyph_t *glyphs; - size_t num_glyphs; - - if (_utf8_to_glyphs (font, utf8, x0, y0, &glyphs, &num_glyphs)) - { - cairo_status_t res; - res = _cairo_ft_font_glyph_bbox (font, surface, - glyphs, num_glyphs, bbox); - free (glyphs); - return res; - } - else - return CAIRO_STATUS_NO_MEMORY; -} static cairo_status_t -_cairo_ft_font_show_glyphs (void *abstract_font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - const cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_ft_font_show_glyphs (void *abstract_font, + cairo_font_scale_t *sc, + cairo_operator_t operator, + cairo_surface_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + int num_glyphs) { + cairo_image_glyph_cache_entry_t *img; + cairo_cache_t *cache; + cairo_glyph_cache_key_t key; cairo_ft_font_t *font = abstract_font; cairo_status_t status; - cairo_surface_t *mask = NULL; - cairo_glyph_size_t size; double x, y; int i; - if (font == NULL + _cairo_lock_global_image_glyph_cache (); + cache = _cairo_get_global_image_glyph_cache(); + + if (cache == NULL + || font == NULL || source == NULL || surface == NULL - || glyphs == NULL) + || glyphs == NULL) { + _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_NO_MEMORY; + } + + key.unscaled = &font->base; + key.scale = *sc; for (i = 0; i < num_glyphs; i++) { - mask = _cairo_font_lookup_glyph (&font->base, surface, - &glyphs[i], &size); - if (mask == NULL) + img = NULL; + key.index = glyphs[i].index; + + if (_cairo_cache_lookup (cache, &key, (void **) &img) + != CAIRO_STATUS_SUCCESS + || img == NULL + || img->image == NULL) continue; x = glyphs[i].x; y = glyphs[i].y; - status = _cairo_surface_composite (operator, source, mask, surface, - source_x + x + size.x, - source_y + y - size.y, + status = _cairo_surface_composite (operator, source, + &(img->image->base), + surface, + source_x + x + img->size.x, + source_y + y - img->size.y, 0, 0, - x + size.x, - y - size.y, - (double) size.width, - (double) size.height); - - cairo_surface_destroy (mask); + x + img->size.x, + y - img->size.y, + (double) img->size.width, + (double) img->size.height); - if (status) + if (status) { + _cairo_unlock_global_image_glyph_cache (); return status; + } } + _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_ft_font_show_text (void *abstract_font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - double x0, - double y0, - const unsigned char *utf8) -{ - cairo_ft_font_t *font = abstract_font; - cairo_glyph_t *glyphs; - size_t num_glyphs; - - if (_utf8_to_glyphs (font, utf8, x0, y0, &glyphs, &num_glyphs)) - { - cairo_status_t res; - res = _cairo_ft_font_show_glyphs (font, operator, - source, surface, - source_x, source_y, - glyphs, num_glyphs); - free (glyphs); - return res; - } - else - return CAIRO_STATUS_NO_MEMORY; -} static int _move_to (FT_Vector *to, void *closure) @@ -699,10 +858,11 @@ _cubic_to (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *closur } static cairo_status_t -_cairo_ft_font_glyph_path (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) +_cairo_ft_font_glyph_path (void *abstract_font, + cairo_font_scale_t *sc, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path) { int i; cairo_ft_font_t *font = abstract_font; @@ -717,8 +877,9 @@ _cairo_ft_font_glyph_path (void *abstract_font, 0, /* delta */ }; - glyph = font->face->glyph; - _install_font_matrix (&font->base.matrix, font->face); + glyph = font->val->face->glyph; + + _install_font_scale (sc, font->val->face); for (i = 0; i < num_glyphs; i++) { @@ -727,7 +888,7 @@ _cairo_ft_font_glyph_path (void *abstract_font, 0, DOUBLE_TO_16_16 (-1.0), }; - error = FT_Load_Glyph (font->face, glyphs[i].index, FT_LOAD_DEFAULT); + error = FT_Load_Glyph (font->val->face, glyphs[i].index, FT_LOAD_DEFAULT); /* XXX: What to do in this error case? */ if (error) continue; @@ -747,130 +908,218 @@ _cairo_ft_font_glyph_path (void *abstract_font, return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_ft_font_text_path (void *abstract_font, - double x, - double y, - const unsigned char *utf8, - cairo_path_t *path) -{ - cairo_ft_font_t *font = abstract_font; - cairo_glyph_t *glyphs; - size_t nglyphs; - - if (_utf8_to_glyphs (font, utf8, x, y, &glyphs, &nglyphs)) - { - cairo_status_t res; - res = _cairo_ft_font_glyph_path (font, glyphs, nglyphs, path); - free (glyphs); - return res; - } - else - return CAIRO_STATUS_NO_MEMORY; -} - -cairo_font_t * -cairo_ft_font_create_for_ft_face (FT_Face face) -{ - cairo_ft_font_t *f = NULL; - - f = malloc (sizeof (cairo_ft_font_t)); - if (f == NULL) - return NULL; - memset (f, 0, sizeof (cairo_ft_font_t)); - - _cairo_font_init (&f->base, - &cairo_ft_font_backend); - - f->ft_library = NULL; - f->owns_ft_library = 0; - - f->face = face; - f->owns_face = 0; - return (cairo_font_t *) f; -} - -static cairo_surface_t * -_cairo_ft_font_create_glyph (void *abstract_font, - const cairo_glyph_t *glyph, - cairo_glyph_size_t *return_size) +static cairo_status_t +_cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) { - cairo_ft_font_t *font = abstract_font; - cairo_image_surface_t *image; + cairo_ft_font_t *font = (cairo_ft_font_t *)val->key.unscaled; FT_GlyphSlot glyphslot; unsigned int width, height, stride; FT_Outline *outline; FT_BBox cbox; FT_Bitmap bitmap; - - glyphslot = font->face->glyph; - _install_font_matrix (&font->base.matrix, font->face); + FT_Glyph_Metrics *metrics; - FT_Load_Glyph (font->face, glyph->index, FT_LOAD_DEFAULT); + glyphslot = font->val->face->glyph; + metrics = &glyphslot->metrics; - outline = &glyphslot->outline; + _install_font_scale (&val->key.scale, font->val->face); + + if (FT_Load_Glyph (font->val->face, val->key.index, FT_LOAD_DEFAULT) != 0) + return CAIRO_STATUS_NO_MEMORY; + + val->extents.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX); + val->extents.y_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingY); + val->extents.width = DOUBLE_FROM_26_6 (metrics->width); + val->extents.height = DOUBLE_FROM_26_6 (metrics->height); + val->extents.x_advance = DOUBLE_FROM_26_6 (font->val->face->glyph->advance.x); + val->extents.y_advance = DOUBLE_FROM_26_6 (font->val->face->glyph->advance.y); + outline = &glyphslot->outline; + FT_Outline_Get_CBox (outline, &cbox); cbox.xMin &= -64; cbox.yMin &= -64; cbox.xMax = (cbox.xMax + 63) & -64; cbox.yMax = (cbox.yMax + 63) & -64; - + width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6); height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6); stride = (width + 3) & -4; - bitmap.pixel_mode = ft_pixel_mode_grays; - bitmap.num_grays = 256; - bitmap.width = width; - bitmap.rows = height; - bitmap.pitch = stride; - if (width * height == 0) - return NULL; - - bitmap.buffer = malloc (stride * height); - if (bitmap.buffer == NULL) - return NULL; - - memset (bitmap.buffer, 0x0, stride * height); + val->image = NULL; + else + { - FT_Outline_Translate (outline, -cbox.xMin, -cbox.yMin); - FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap); - - image = (cairo_image_surface_t *) + bitmap.pixel_mode = ft_pixel_mode_grays; + bitmap.num_grays = 256; + bitmap.width = width; + bitmap.rows = height; + bitmap.pitch = stride; + bitmap.buffer = calloc (1, stride * height); + + if (bitmap.buffer == NULL) { + return CAIRO_STATUS_NO_MEMORY; + }; + + FT_Outline_Translate (outline, -cbox.xMin, -cbox.yMin); + + if (FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap) != 0) { + free (bitmap.buffer); + return CAIRO_STATUS_NO_MEMORY; + } + + val->image = (cairo_image_surface_t *) cairo_image_surface_create_for_data ((char *) bitmap.buffer, CAIRO_FORMAT_A8, width, height, stride); - if (image == NULL) { - free (bitmap.buffer); - return NULL; + if (val->image == NULL) { + free (bitmap.buffer); + return CAIRO_STATUS_NO_MEMORY; + } + + _cairo_image_surface_assume_ownership_of_data (val->image); } - - _cairo_image_surface_assume_ownership_of_data (image); - - return_size->width = (unsigned short) width; - return_size->height = (unsigned short) height; - return_size->x = (short) (cbox.xMin >> 6); - return_size->y = (short) (cbox.yMax >> 6); - return &image->base; + val->size.width = (unsigned short) width; + val->size.height = (unsigned short) height; + val->size.x = (short) (cbox.xMin >> 6); + val->size.y = (short) (cbox.yMax >> 6); + + return CAIRO_STATUS_SUCCESS; } const struct cairo_font_backend cairo_ft_font_backend = { _cairo_ft_font_create, - _cairo_ft_font_copy, _cairo_ft_font_destroy, _cairo_ft_font_font_extents, - _cairo_ft_font_text_extents, + _cairo_ft_font_text_to_glyphs, _cairo_ft_font_glyph_extents, - _cairo_ft_font_text_bbox, _cairo_ft_font_glyph_bbox, - _cairo_ft_font_show_text, _cairo_ft_font_show_glyphs, - _cairo_ft_font_text_path, _cairo_ft_font_glyph_path, _cairo_ft_font_create_glyph }; + + +/* implement the platform-specific interface */ + +cairo_font_t * +cairo_ft_font_create (FT_Library ft_library, FcPattern *pattern) +{ + cairo_font_scale_t scale; + cairo_font_t *scaled; + cairo_ft_font_t *f = NULL; + ft_font_val_t *v = NULL; + FcPattern *dup; + + scale.matrix[0][0] = 1.; + scale.matrix[0][1] = 0.; + scale.matrix[1][0] = 0.; + scale.matrix[1][1] = 1.; + + scaled = malloc (sizeof (cairo_font_t)); + if (scaled == NULL) + goto FAIL; + + dup = FcPatternDuplicate(pattern); + if (dup == NULL) + goto FREE_SCALED; + + v = _create_from_library_and_pattern (ft_library, pattern); + if (v == NULL) + goto FREE_PATTERN; + + f = malloc (sizeof(cairo_ft_font_t)); + if (f == NULL) + goto FREE_VAL; + + if (_cairo_unscaled_font_init (&f->base, &cairo_ft_font_backend)) + goto FREE_VAL; + + f->pattern = dup; + f->val = v; + + _cairo_font_init (scaled, &scale, &f->base); + + return scaled; + + FREE_VAL: + _destroy_font_val (v); + + FREE_PATTERN: + FcPatternDestroy (dup); + + FREE_SCALED: + free (scaled); + + FAIL: + return NULL; +} + +cairo_font_t * +cairo_ft_font_create_for_ft_face (FT_Face face) +{ + cairo_font_scale_t scale; + cairo_font_t *scaled; + cairo_ft_font_t *f = NULL; + ft_font_val_t *v = NULL; + + scale.matrix[0][0] = 1.; + scale.matrix[0][1] = 0.; + scale.matrix[1][0] = 0.; + scale.matrix[1][1] = 1.; + + scaled = malloc (sizeof (cairo_font_t)); + if (scaled == NULL) + goto FAIL; + + v = _create_from_face (face, 0); + if (v == NULL) + goto FREE_SCALED; + + f = malloc (sizeof(cairo_ft_font_t)); + if (f == NULL) + goto FREE_VAL; + + _cairo_unscaled_font_init (&f->base, &cairo_ft_font_backend); + f->pattern = NULL; + f->val = v; + + _cairo_font_init (scaled, &scale, &f->base); + + return scaled; + + FREE_VAL: + _destroy_font_val (v); + + FREE_SCALED: + free (scaled); + + FAIL: + return NULL; +} + +FT_Face +cairo_ft_font_face (cairo_font_t *abstract_font) +{ + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font->unscaled; + + if (font == NULL || font->val == NULL) + return NULL; + + return font->val->face; +} + +FcPattern * +cairo_ft_font_pattern (cairo_font_t *abstract_font) +{ + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font->unscaled; + + if (font == NULL) + return NULL; + + return font->pattern; +} diff --git a/src/cairo-glitz-surface.c b/src/cairo-glitz-surface.c index 8d18280b..d351108d 100644 --- a/src/cairo-glitz-surface.c +++ b/src/cairo-glitz-surface.c @@ -900,7 +900,8 @@ static const struct cairo_surface_backend cairo_glitz_surface_backend = { _cairo_glitz_surface_copy_page, _cairo_glitz_surface_show_page, _cairo_glitz_surface_set_clip_region, - _cairo_glitz_surface_create_pattern + _cairo_glitz_surface_create_pattern, + NULL /* show_glyphs */ }; cairo_surface_t * diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index c89d6376..fabb53ef 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -77,9 +77,9 @@ _cairo_gstate_init (cairo_gstate_t *gstate) gstate->num_dashes = 0; gstate->dash_offset = 0.0; - gstate->font = _cairo_font_create (CAIRO_FONT_FAMILY_DEFAULT, - CAIRO_FONT_SLANT_DEFAULT, - CAIRO_FONT_WEIGHT_DEFAULT); + gstate->font = _cairo_unscaled_font_create (CAIRO_FONT_FAMILY_DEFAULT, + CAIRO_FONT_SLANT_DEFAULT, + CAIRO_FONT_WEIGHT_DEFAULT); gstate->surface = NULL; @@ -119,11 +119,8 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) } if (other->font) { - gstate->font = _cairo_font_copy (other->font); - if (!gstate->font) { - status = CAIRO_STATUS_NO_MEMORY; - goto CLEANUP_DASHES; - } + gstate->font = other->font; + _cairo_unscaled_font_reference (gstate->font); } if (other->clip.region) @@ -149,9 +146,10 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) CLEANUP_PATH: _cairo_path_fini (&gstate->path); + CLEANUP_FONT: - cairo_font_destroy (gstate->font); - CLEANUP_DASHES: + _cairo_unscaled_font_destroy (gstate->font); + free (gstate->dash); gstate->dash = NULL; @@ -161,7 +159,7 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) void _cairo_gstate_fini (cairo_gstate_t *gstate) { - cairo_font_destroy (gstate->font); + _cairo_unscaled_font_destroy (gstate->font); if (gstate->surface) cairo_surface_destroy (gstate->surface); @@ -177,6 +175,8 @@ _cairo_gstate_fini (cairo_gstate_t *gstate) cairo_pattern_destroy (gstate->pattern); + _cairo_matrix_fini (&gstate->font_matrix); + _cairo_matrix_fini (&gstate->ctm); _cairo_matrix_fini (&gstate->ctm_inverse); @@ -627,6 +627,8 @@ _cairo_gstate_default_matrix (cairo_gstate_t *gstate) if (scale == 0) scale = 1; + cairo_matrix_set_identity (&gstate->font_matrix); + cairo_matrix_set_identity (&gstate->ctm); cairo_matrix_scale (&gstate->ctm, scale, scale); cairo_matrix_copy (&gstate->ctm_inverse, &gstate->ctm); @@ -1676,15 +1678,6 @@ extract_transformed_rectangle(cairo_matrix_t *mat, double a, b, c, d, tx, ty; cairo_status_t st; - /* XXX: Something in the rectangle-based clipping support is - * broken. See cairo_snippets/xxx_clip_rectangle which - * demonstrates no clipping at all. - * - * For now, I'm am disabling this optimization completely until it - * can be fixed. - */ - return 0; - st = cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty); if (!(st == CAIRO_STATUS_SUCCESS && b == 0. && c == 0.)) return 0; @@ -1992,16 +1985,28 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, return CAIRO_STATUS_SUCCESS; } + cairo_status_t _cairo_gstate_select_font (cairo_gstate_t *gstate, const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { - if (gstate->font != NULL) - cairo_font_destroy (gstate->font); + cairo_unscaled_font_t *tmp; + + tmp = _cairo_unscaled_font_create (family, slant, weight); - gstate->font = _cairo_font_create (family, slant, weight); + if (tmp == NULL) + return CAIRO_STATUS_NO_MEMORY; + + if (gstate->font != tmp) + { + if (gstate->font != NULL) + _cairo_unscaled_font_destroy (gstate->font); + + cairo_matrix_set_identity (&gstate->font_matrix); + gstate->font = tmp; + } return CAIRO_STATUS_SUCCESS; } @@ -2010,212 +2015,294 @@ cairo_status_t _cairo_gstate_scale_font (cairo_gstate_t *gstate, double scale) { - return _cairo_font_scale (gstate->font, scale); + return cairo_matrix_scale (&gstate->font_matrix, scale, scale); } cairo_status_t _cairo_gstate_transform_font (cairo_gstate_t *gstate, cairo_matrix_t *matrix) { - return _cairo_font_transform (gstate->font, matrix); + cairo_matrix_t tmp; + double a, b, c, d, tx, ty; + cairo_matrix_get_affine (matrix, &a, &b, &c, &d, &tx, &ty); + cairo_matrix_set_affine (&tmp, a, b, c, d, 0, 0); + return cairo_matrix_multiply (&gstate->font_matrix, &gstate->font_matrix, &tmp); } + cairo_status_t _cairo_gstate_current_font (cairo_gstate_t *gstate, cairo_font_t **font) { - *font = gstate->font; + cairo_font_scale_t scale; + cairo_font_t *scaled; + double dummy; + + scaled = malloc (sizeof (cairo_font_t)); + if (scaled == NULL) + return CAIRO_STATUS_NO_MEMORY; + + cairo_matrix_get_affine (&gstate->font_matrix, + &scale.matrix[0][0], + &scale.matrix[0][1], + &scale.matrix[1][0], + &scale.matrix[1][1], + &dummy, &dummy); + + _cairo_font_init (scaled, &scale, gstate->font); + _cairo_unscaled_font_reference (gstate->font); + + *font = scaled; return CAIRO_STATUS_SUCCESS; } -cairo_status_t -_cairo_gstate_current_font_extents (cairo_gstate_t *gstate, - cairo_font_extents_t *extents) +void +_cairo_gstate_set_font_transform (cairo_gstate_t *gstate, + cairo_matrix_t *matrix) { - cairo_int_status_t status; - cairo_matrix_t saved_font_matrix; - - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); - - status = _cairo_font_font_extents (gstate->font, extents); - - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + cairo_matrix_copy (&gstate->font_matrix, matrix); +} - return status; +void +_cairo_gstate_current_font_transform (cairo_gstate_t *gstate, + cairo_matrix_t *matrix) +{ + cairo_matrix_copy (matrix, &gstate->font_matrix); } +/* + * Like everything else in this file, fonts involve Too Many Coordinate Spaces; + * it is easy to get confused about what's going on. + * + * The user's view + * --------------- + * + * Users ask for things in user space. When cairo starts, a user space unit + * is about 1/96 inch, which is similar to (but importantly different from) + * the normal "point" units most users think in terms of. When a user + * selects a font, its scale is set to "one user unit". The user can then + * independently scale the user coordinate system *or* the font matrix, in + * order to adjust the rendered size of the font. + * + * If the user asks for a permanent reference to "a font", they are given a + * handle to a structure holding a scale matrix and an unscaled font. This + * effectively decouples the font from further changes to user space. Even + * if the user then "sets" the current cairo_t font to the handle they were + * passed, further changes to the cairo_t CTM will not affect externally + * held references to the font. + * + * + * The font's view + * --------------- + * + * Fonts are designed and stored (in say .ttf files) in "font space", which + * describes an "EM Square" (a design tile) and has some abstract number + * such as 1000, 1024, or 2048 units per "EM". This is basically an + * uninteresting space for us, but we need to remember that it exists. + * + * Font resources (from libraries or operating systems) render themselves + * to a particular device. Since they do not want to make most programmers + * worry about the font design space, the scaling API is simplified to + * involve just telling the font the required pixel size of the EM square + * (that is, in device space). + * + * + * Cairo's gstate view + * ------------------- + * + * In addition to the CTM and CTM inverse, we keep a matrix in the gstate + * called the "font matrix" which describes the user's most recent + * font-scaling or font-transforming request. This is kept in terms of an + * abstract scale factor, composed with the CTM and used to set the font's + * pixel size. So if the user asks to "scale the font by 12", the matrix + * is: + * + * [ 12.0, 0.0, 0.0, 12.0, 0.0, 0.0 ] + * + * It is an affine matrix, like all cairo matrices, but its tx and ty + * components are always set to zero; we don't permit "nudging" fonts + * around. + * + * In order to perform any action on a font, we must build an object + * called a cairo_font_scale_t; this contains the central 2x2 matrix + * resulting from "font matrix * CTM". + * + * We pass this to the font when making requests of it, which causes it to + * reply for a particular [user request, device] combination, under the CTM + * (to accomodate the "zoom in" == "bigger fonts" issue above). + * + * The other terms in our communication with the font are therefore in + * device space. When we ask it to perform text->glyph conversion, it will + * produce a glyph string in device space. Glyph vectors we pass to it for + * measuring or rendering should be in device space. The metrics which we + * get back from the font will be in device space. The contents of the + * global glyph image cache will be in device space. + * + * + * Cairo's public view + * ------------------- + * + * Since the values entering and leaving via public API calls are in user + * space, the gstate functions typically need to multiply argumens by the + * CTM (for user-input glyph vectors), and return values by the CTM inverse + * (for font responses such as metrics or glyph vectors). + * + */ -cairo_status_t -_cairo_gstate_set_font (cairo_gstate_t *gstate, - cairo_font_t *font) +static void +_build_font_scale (cairo_gstate_t *gstate, + cairo_font_scale_t *sc) { - if (gstate->font != NULL) - cairo_font_destroy (gstate->font); - gstate->font = font; - cairo_font_reference (gstate->font); - return CAIRO_STATUS_SUCCESS; + cairo_matrix_t tmp; + double dummy; + cairo_matrix_multiply (&tmp, &gstate->font_matrix, &gstate->ctm); + cairo_matrix_get_affine (&tmp, + &sc->matrix[0][0], + &sc->matrix[0][1], + &sc->matrix[1][0], + &sc->matrix[1][1], + &dummy, &dummy); } cairo_status_t -_cairo_gstate_text_extents (cairo_gstate_t *gstate, - const unsigned char *utf8, - cairo_text_extents_t *extents) +_cairo_gstate_current_font_extents (cairo_gstate_t *gstate, + cairo_font_extents_t *extents) { - cairo_matrix_t saved_font_matrix; - cairo_status_t status; - double scale_x, scale_y; - - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - _cairo_matrix_compute_scale_factors (&gstate->ctm, &scale_x, &scale_y); - cairo_matrix_scale (&gstate->font->matrix, scale_x, scale_y); - - status = _cairo_font_text_extents (gstate->font, - utf8, extents); - - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + cairo_int_status_t status; + cairo_font_scale_t sc; + double dummy = 0.0; - extents->x_bearing /= scale_x; - extents->y_bearing /= scale_y; - extents->width /= scale_x; - extents->height /= scale_y; - extents->x_advance /= scale_x; - extents->y_advance /= scale_y; + _build_font_scale (gstate, &sc); - return status; -} + status = _cairo_unscaled_font_font_extents (gstate->font, &sc, extents); -cairo_status_t -_cairo_gstate_glyph_extents (cairo_gstate_t *gstate, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - cairo_status_t status; - cairo_matrix_t saved_font_matrix; - double scale_x, scale_y; + /* The font responded in device space; convert to user space. */ - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - _cairo_matrix_compute_scale_factors (&gstate->ctm, &scale_x, &scale_y); - cairo_matrix_scale (&gstate->font->matrix, scale_x, scale_y); + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &dummy, + &extents->ascent); - status = _cairo_font_glyph_extents (gstate->font, - glyphs, num_glyphs, - extents); + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &dummy, + &extents->descent); - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &dummy, + &extents->height); - extents->x_bearing /= scale_x; - extents->y_bearing /= scale_y; - extents->width /= scale_x; - extents->height /= scale_y; - extents->x_advance /= scale_x; - extents->y_advance /= scale_y; + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &extents->max_x_advance, + &extents->max_y_advance); return status; } cairo_status_t -_cairo_gstate_show_text (cairo_gstate_t *gstate, - const unsigned char *utf8) +_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *nglyphs) { cairo_status_t status; - cairo_point_t point; - double x, y; - cairo_matrix_t saved_font_matrix; - cairo_pattern_t pattern; - cairo_box_t bbox; + cairo_font_scale_t sc; + + cairo_point_t point; + double dev_x, dev_y; + int i; + + _build_font_scale (gstate, &sc); status = _cairo_path_current_point (&gstate->path, &point); if (status == CAIRO_STATUS_NO_CURRENT_POINT) { - x = 0; - y = 0; - cairo_matrix_transform_point (&gstate->ctm, &x, &y); + dev_x = 0.0; + dev_y = 0.0; } else { - x = _cairo_fixed_to_double (point.x); - y = _cairo_fixed_to_double (point.y); + dev_x = _cairo_fixed_to_double (point.x); + dev_y = _cairo_fixed_to_double (point.y); } - - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); - _cairo_pattern_init_copy (&pattern, gstate->pattern); - - status = _cairo_font_text_bbox (gstate->font, gstate->surface, - x, y, utf8, &bbox); - if (status) - return status; - - status = _cairo_gstate_create_pattern (gstate, &pattern, &bbox); - if (status) + status = _cairo_unscaled_font_text_to_glyphs (gstate->font, + &sc, utf8, glyphs, nglyphs); + + if (status || !glyphs || !nglyphs || !(*glyphs) || !(nglyphs)) return status; - - if (gstate->clip.surface) - { - cairo_surface_t *intermediate; - cairo_color_t empty_color; - _cairo_color_init (&empty_color); - _cairo_color_set_alpha (&empty_color, .0); - intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, - CAIRO_FORMAT_A8, - gstate->clip.width, - gstate->clip.height, - &empty_color); + /* The font responded in device space, starting from (0,0); add any + current point offset in device space, and convert to user space. */ - status = _cairo_font_show_text (gstate->font, - CAIRO_OPERATOR_ADD, pattern.source, - intermediate, - gstate->clip.x - pattern.source_offset.x, - gstate->clip.y - pattern.source_offset.y, - x - gstate->clip.x, - y - gstate->clip.y, utf8); + for (i = 0; i < *nglyphs; ++i) { + (*glyphs)[i].x += dev_x; + (*glyphs)[i].y += dev_y; + cairo_matrix_transform_point (&gstate->ctm_inverse, + &((*glyphs)[i].x), + &((*glyphs)[i].y)); + } + + return CAIRO_STATUS_SUCCESS; +} - if (status) - goto BAIL; - - status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - gstate->clip.surface, - NULL, - intermediate, - 0, 0, - 0, 0, - 0, 0, - gstate->clip.width, - gstate->clip.height); +cairo_status_t +_cairo_gstate_set_font (cairo_gstate_t *gstate, + cairo_font_t *font) +{ + if (gstate->font != NULL) + _cairo_unscaled_font_destroy (gstate->font); + gstate->font = font->unscaled; + _cairo_unscaled_font_reference (gstate->font); + cairo_matrix_set_affine (&gstate->font_matrix, + font->scale.matrix[0][0], + font->scale.matrix[0][1], + font->scale.matrix[1][0], + font->scale.matrix[1][1], + 0, 0); + return CAIRO_STATUS_SUCCESS; +} - if (status) - goto BAIL; +cairo_status_t +_cairo_gstate_glyph_extents (cairo_gstate_t *gstate, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + cairo_status_t status; + cairo_glyph_t *transformed_glyphs; + cairo_font_scale_t sc; + int i; - status = _cairo_surface_composite (gstate->operator, - pattern.source, - intermediate, - gstate->surface, - 0, 0, - 0, 0, - gstate->clip.x, - gstate->clip.y, - gstate->clip.width, - gstate->clip.height); + _build_font_scale (gstate, &sc); - BAIL: - cairo_surface_destroy (intermediate); - - } - else + transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); + if (transformed_glyphs == NULL) + return CAIRO_STATUS_NO_MEMORY; + + for (i = 0; i < num_glyphs; ++i) { - status = _cairo_font_show_text (gstate->font, - gstate->operator, pattern.source, - gstate->surface, - -pattern.source_offset.x, - -pattern.source_offset.y, - x, y, utf8); + transformed_glyphs[i] = glyphs[i]; + cairo_matrix_transform_point (&gstate->ctm, + &transformed_glyphs[i].x, + &transformed_glyphs[i].y); } - - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - _cairo_pattern_fini (&pattern); + status = _cairo_unscaled_font_glyph_extents (gstate->font, &sc, + transformed_glyphs, num_glyphs, + extents); + + /* The font responded in device space; convert to user space. */ + + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &extents->x_bearing, + &extents->y_bearing); + + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &extents->width, + &extents->height); + + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &extents->x_advance, + &extents->y_advance); + + free (transformed_glyphs); return status; } @@ -2226,12 +2313,14 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, int num_glyphs) { cairo_status_t status; - cairo_matrix_t saved_font_matrix; int i; cairo_glyph_t *transformed_glyphs = NULL; + cairo_font_scale_t sc; cairo_pattern_t pattern; cairo_box_t bbox; + _build_font_scale (gstate, &sc); + transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); if (transformed_glyphs == NULL) return CAIRO_STATUS_NO_MEMORY; @@ -2240,16 +2329,14 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, { transformed_glyphs[i] = glyphs[i]; cairo_matrix_transform_point (&gstate->ctm, - &(transformed_glyphs[i].x), - &(transformed_glyphs[i].y)); + &transformed_glyphs[i].x, + &transformed_glyphs[i].y); } - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); - _cairo_pattern_init_copy (&pattern, gstate->pattern); - status = _cairo_font_glyph_bbox (gstate->font, gstate->surface, - transformed_glyphs, num_glyphs, &bbox); + status = _cairo_unscaled_font_glyph_bbox (gstate->font, &sc, + transformed_glyphs, num_glyphs, + &bbox); if (status) return status; @@ -2277,12 +2364,13 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, transformed_glyphs[i].y -= gstate->clip.y; } - status = _cairo_font_show_glyphs (gstate->font, - CAIRO_OPERATOR_ADD, - pattern.source, intermediate, - gstate->clip.x - pattern.source_offset.x, - gstate->clip.y - pattern.source_offset.y, - transformed_glyphs, num_glyphs); + status = _cairo_unscaled_font_show_glyphs (gstate->font, + &sc, + CAIRO_OPERATOR_ADD, + pattern.source, intermediate, + gstate->clip.x - pattern.source_offset.x, + gstate->clip.y - pattern.source_offset.y, + transformed_glyphs, num_glyphs); if (status) goto BAIL; @@ -2317,16 +2405,15 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, } else { - status = _cairo_font_show_glyphs (gstate->font, - gstate->operator, pattern.source, - gstate->surface, - -pattern.source_offset.x, - -pattern.source_offset.y, - transformed_glyphs, num_glyphs); + status = _cairo_unscaled_font_show_glyphs (gstate->font, + &sc, + gstate->operator, pattern.source, + gstate->surface, + -pattern.source_offset.x, + -pattern.source_offset.y, + transformed_glyphs, num_glyphs); } - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - _cairo_pattern_fini (&pattern); free (transformed_glyphs); @@ -2334,40 +2421,6 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, return status; } - -cairo_status_t -_cairo_gstate_text_path (cairo_gstate_t *gstate, - const unsigned char *utf8) -{ - cairo_status_t status; - cairo_matrix_t saved_font_matrix; - cairo_point_t point; - double x, y; - - status = _cairo_path_current_point (&gstate->path, &point); - if (status == CAIRO_STATUS_NO_CURRENT_POINT) { - x = 0; - y = 0; - cairo_matrix_transform_point (&gstate->ctm, &x, &y); - } else { - x = _cairo_fixed_to_double (point.x); - y = _cairo_fixed_to_double (point.y); - } - - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); - - status = _cairo_font_text_path (gstate->font, - x, y, - utf8, - &gstate->path); - - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - - return status; -} - - cairo_status_t _cairo_gstate_glyph_path (cairo_gstate_t *gstate, cairo_glyph_t *glyphs, @@ -2376,7 +2429,9 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, cairo_status_t status; int i; cairo_glyph_t *transformed_glyphs = NULL; - cairo_matrix_t saved_font_matrix; + cairo_font_scale_t sc; + + _build_font_scale (gstate, &sc); transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); if (transformed_glyphs == NULL) @@ -2390,14 +2445,9 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, &(transformed_glyphs[i].y)); } - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); - - status = _cairo_font_glyph_path (gstate->font, - transformed_glyphs, num_glyphs, - &gstate->path); - - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + status = _cairo_unscaled_font_glyph_path (gstate->font, &sc, + transformed_glyphs, num_glyphs, + &gstate->path); free (transformed_glyphs); return status; diff --git a/src/cairo-hash.c b/src/cairo-hash.c new file mode 100644 index 00000000..c5ce3993 --- /dev/null +++ b/src/cairo-hash.c @@ -0,0 +1,454 @@ +/* cairo - a vector graphics library with display and print output + * + * This file is Copyright © 2004 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Keith Packard + * Graydon Hoare <graydon@redhat.com> + */ + +#include "cairoint.h" + +/* + * This structure, and accompanying table, is borrowed/modified from the + * file xserver/render/glyph.c in the freedesktop.org x server, with + * permission (and suggested modification of doubling sizes) by Keith + * Packard. + */ + +static cairo_cache_arrangement_t cache_arrangements [] = { + { 16, 43, 41 }, + { 32, 73, 71 }, + { 64, 151, 149 }, + { 128, 283, 281 }, + { 256, 571, 569 }, + { 512, 1153, 1151 }, + { 1024, 2269, 2267 }, + { 2048, 4519, 4517 }, + { 4096, 9013, 9011 }, + { 8192, 18043, 18041 }, + { 16384, 36109, 36107 }, + { 32768, 72091, 72089 }, + { 65536, 144409, 144407 }, + { 131072, 288361, 288359 }, + { 262144, 576883, 576881 }, + { 524288, 1153459, 1153457 }, + { 1048576, 2307163, 2307161 }, + { 2097152, 4613893, 4613891 }, + { 4194304, 9227641, 9227639 }, + { 8388608, 18455029, 18455027 }, + { 16777216, 36911011, 36911009 }, + { 33554432, 73819861, 73819859 }, + { 67108864, 147639589, 147639587 }, + { 134217728, 295279081, 295279079 }, + { 268435456, 590559793, 590559791 } +}; + +#define N_CACHE_SIZES (sizeof(cache_arrangements)/sizeof(cache_arrangements[0])) + +/* + * Entries 'e' are poiners, in one of 3 states: + * + * e == NULL: The entry has never had anything put in it + * e != DEAD_ENTRY: The entry has an active value in it currently + * e == DEAD_ENTRY: The entry *had* a value in it at some point, but the + * entry has been killed. Lookups requesting free space can + * reuse these entries; lookups requesting a precise match + * should neither return these entries nor stop searching when + * seeing these entries. + * + * We expect keys will not be destroyed frequently, so our table does not + * contain any explicit shrinking code nor any chain-coalescing code for + * entries randomly deleted by memory pressure (except during rehashing, of + * course). These assumptions are potentially bad, but they make the + * implementation straightforward. + * + * Revisit later if evidence appears that we're using excessive memory from + * a mostly-dead table. + * + * Generally you do not need to worry about freeing cache entries; the + * cache will expire entries randomly as it experiences memory pressure. + * There is currently no explicit entry-removing call, though one can be + * added easily. + * + * This table is open-addressed with double hashing. Each table size is a + * prime chosen to be a little more than double the high water mark for a + * given arrangement, so the tables should remain < 50% full. The table + * size makes for the "first" hash modulus; a second prime (2 less than the + * first prime) serves as the "second" hash modulus, which is co-prime and + * thus guarantees a complete permutation of table indices. + * + */ + +#define DEAD_ENTRY ((cairo_cache_entry_base_t *) 1) +#define NULL_ENTRY_P(cache, i) ((cache)->entries[i] == NULL) +#define DEAD_ENTRY_P(cache, i) ((cache)->entries[i] == DEAD_ENTRY) +#define LIVE_ENTRY_P(cache, i) \ + (!((NULL_ENTRY_P((cache),(i))) || (DEAD_ENTRY_P((cache),(i))))) + +#ifdef CAIRO_DO_SANITY_CHECKING +#include <assert.h> +static void +_cache_sane_state (cairo_cache_t *cache) +{ + assert (cache != NULL); + assert (cache->entries != NULL); + assert (cache->backend != NULL); + assert (cache->arrangement != NULL); + assert (cache->refcount > 0); + assert (cache->used_memory <= cache->max_memory); + assert (cache->live_entries <= cache->arrangement->size); +} +#else +#define _cache_sane_state(c) +#define assert(x) +#endif + +static void +_entry_destroy (cairo_cache_t *cache, unsigned long i) +{ + _cache_sane_state (cache); + + if (LIVE_ENTRY_P(cache, i)) + { + cairo_cache_entry_base_t *entry = cache->entries[i]; + assert(cache->live_entries > 0); + assert(cache->used_memory > entry->memory); + + cache->live_entries--; + cache->used_memory -= entry->memory; + cache->backend->destroy_entry (cache, entry); + cache->entries[i] = DEAD_ENTRY; + } +} + +static cairo_cache_entry_base_t ** +_cache_lookup (cairo_cache_t *cache, + void *key, + int (*predicate)(void*,void*,void*)) +{ + + cairo_cache_entry_base_t **probe; + unsigned long hash; + unsigned long table_size, i, idx, step; + + _cache_sane_state (cache); + assert (key != NULL); + + table_size = cache->arrangement->size; + hash = cache->backend->hash (cache, key); + idx = hash % table_size; + step = 0; + + for (i = 0; i < table_size; ++i) + { +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + cache->probes++; +#endif + assert(idx < table_size); + probe = cache->entries + idx; + + /* + * There are two lookup modes: searching for a free slot and searching + * for an exact entry. + */ + + if (predicate != NULL) + { + /* We are looking up an exact entry. */ + if (*probe != NULL + && *probe != DEAD_ENTRY + && (*probe)->hashcode == hash + && predicate (cache, key, *probe)) + return probe; + } + else + { + /* We are just looking for a free slot. */ + if (*probe == NULL + || *probe == DEAD_ENTRY) + return probe; + } + + if (step == 0) { + step = hash % cache->arrangement->rehash; + if (step == 0) + step = 1; + } + + idx += step; + if (idx >= table_size) + idx -= table_size; + } + + /* + * The table should not have permitted you to get here if you were just + * looking for a free slot: there should have been room. + */ + assert(predicate != NULL); + return NULL; +} + +static cairo_cache_entry_base_t ** +_find_available_entry_for (cairo_cache_t *cache, + void *key) +{ + return _cache_lookup (cache, key, NULL); +} + +static cairo_cache_entry_base_t ** +_find_exact_live_entry_for (cairo_cache_t *cache, + void *key) +{ + return _cache_lookup (cache, key, cache->backend->keys_equal); +} + + +static cairo_cache_arrangement_t * +_find_cache_arrangement (unsigned long proposed_size) +{ + unsigned long idx; + + for (idx = 0; idx < N_CACHE_SIZES; ++idx) + if (cache_arrangements[idx].high_water_mark >= proposed_size) + return &cache_arrangements[idx]; + return NULL; +} + +static cairo_status_t +_resize_cache (cairo_cache_t *cache, unsigned long proposed_size) +{ + cairo_cache_t tmp; + cairo_cache_entry_base_t **e; + unsigned long new_size, i; + + tmp = *cache; + tmp.arrangement = _find_cache_arrangement (proposed_size); + assert(tmp.arrangement != NULL); + if (tmp.arrangement == cache->arrangement) + return CAIRO_STATUS_SUCCESS; + + new_size = tmp.arrangement->size; + tmp.entries = calloc (new_size, sizeof (cairo_cache_entry_base_t *)); + if (tmp.entries == NULL) + return CAIRO_STATUS_NO_MEMORY; + + for (i = 0; i < cache->arrangement->size; ++i) { + if (LIVE_ENTRY_P(cache, i)) { + e = _find_available_entry_for (&tmp, cache->entries[i]); + assert (e != NULL); + *e = cache->entries[i]; + } + } + free (cache->entries); + cache->entries = tmp.entries; + cache->arrangement = tmp.arrangement; + return CAIRO_STATUS_SUCCESS; +} + + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE +static double +_load_factor (cairo_cache_t *cache) +{ + return ((double) cache->live_entries) + / ((double) cache->arrangement->size); +} +#endif + +static unsigned long +_random_live_entry (cairo_cache_t *cache) +{ + unsigned long idx; + assert(cache != NULL); + do { + idx = rand () % cache->arrangement->size; + } while (! LIVE_ENTRY_P(cache, idx)); + return idx; +} + + +/* public API follows */ + +cairo_status_t +_cairo_cache_init (cairo_cache_t *cache, + const cairo_cache_backend_t *backend, + unsigned long max_memory) +{ + assert(backend != NULL); + + if (cache != NULL){ + cache->arrangement = &cache_arrangements[0]; + cache->refcount = 1; + cache->max_memory = max_memory; + cache->used_memory = 0; + cache->live_entries = 0; + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + cache->hits = 0; + cache->misses = 0; + cache->probes = 0; +#endif + + cache->backend = backend; + cache->entries = calloc (sizeof(cairo_cache_entry_base_t *), + cache->arrangement->size); + if (cache->entries == NULL) + return CAIRO_STATUS_NO_MEMORY; + } + _cache_sane_state (cache); + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_cache_reference (cairo_cache_t *cache) +{ + _cache_sane_state (cache); + cache->refcount++; +} + +void +_cairo_cache_destroy (cairo_cache_t *cache) +{ + unsigned long i; + if (cache != NULL) { + + _cache_sane_state (cache); + + if (cache->refcount-- > 0) + return; + + for (i = 0; i < cache->arrangement->size; ++i) { + _entry_destroy (cache, i); + } + + free (cache->entries); + cache->entries = NULL; + cache->backend->destroy_cache (cache); + } +} + +cairo_status_t +_cairo_cache_lookup (cairo_cache_t *cache, + void *key, + void **entry_return) +{ + + unsigned long idx; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_cache_entry_base_t **slot = NULL, *new_entry; + + _cache_sane_state (cache); + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + if ((cache->hits + cache->misses) % 0xffff == 0) + printf("cache %p stats: size %ld, live %ld, load %.2f\n" + " mem %ld/%ld, hit %ld, miss %ld\n" + " probe %ld, %.2f probe/access\n", + cache, + cache->arrangement->size, + cache->live_entries, + _load_factor (cache), + cache->used_memory, + cache->max_memory, + cache->hits, + cache->misses, + cache->probes, + ((double) cache->probes) + / ((double) (cache->hits + + cache->misses + 1))); +#endif + + /* See if we have an entry in the table already. */ + slot = _find_exact_live_entry_for (cache, key); + if (slot != NULL) { +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + cache->hits++; +#endif + *entry_return = *slot; + return status; + } + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + cache->misses++; +#endif + + /* Build the new entry. */ + status = cache->backend->create_entry (cache, key, + entry_return); + if (status != CAIRO_STATUS_SUCCESS) + return status; + + new_entry = (cairo_cache_entry_base_t *) (*entry_return); + + /* Store the hash value in case the backend forgot. */ + new_entry->hashcode = cache->backend->hash (cache, key); + + /* Make some entries die if we're under memory pressure. */ + while (cache->live_entries > 0 && + ((cache->max_memory - cache->used_memory) < new_entry->memory)) { + idx = _random_live_entry (cache); + assert (idx < cache->arrangement->size); + _entry_destroy (cache, idx); + } + + assert(cache->max_memory >= (cache->used_memory + new_entry->memory)); + + /* Make room in the table for a new slot. */ + status = _resize_cache (cache, cache->live_entries + 1); + if (status != CAIRO_STATUS_SUCCESS) { + cache->backend->destroy_entry (cache, new_entry); + *entry_return = NULL; + return status; + } + + slot = _find_available_entry_for (cache, key); + assert(slot != NULL); + + /* Store entry in slot and increment statistics. */ + *slot = new_entry; + cache->live_entries++; + cache->used_memory += new_entry->memory; + + _cache_sane_state (cache); + + return status; +} + +unsigned long +_cairo_hash_string (const char *c) +{ + /* This is the djb2 hash. */ + unsigned long hash = 5381; + while (*c) + hash = ((hash << 5) + hash) + *c++; + return hash; +} + diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index e2ee69c5..9e6abb2a 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -525,5 +525,6 @@ static const cairo_surface_backend_t cairo_image_surface_backend = { _cairo_image_surface_copy_page, _cairo_image_surface_show_page, _cairo_image_abstract_surface_set_clip_region, - _cairo_image_abstract_surface_create_pattern + _cairo_image_abstract_surface_create_pattern, + NULL /* show_glyphs */ }; diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index 4296d0d4..9b3096b0 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -436,5 +436,6 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = { _cairo_ps_surface_copy_page, _cairo_ps_surface_show_page, _cairo_ps_surface_set_clip_region, - _cairo_ps_surface_create_pattern + _cairo_ps_surface_create_pattern, + NULL /* show_glyphs */ }; diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c index 36aa1573..bc37d204 100644 --- a/src/cairo-xcb-surface.c +++ b/src/cairo-xcb-surface.c @@ -745,7 +745,8 @@ static const struct cairo_surface_backend cairo_xcb_surface_backend = { _cairo_xcb_surface_copy_page, _cairo_xcb_surface_show_page, _cairo_xcb_surface_set_clip_region, - _cairo_xcb_surface_create_pattern + _cairo_xcb_surface_create_pattern, + NULL /* show_glyphs */ }; static void diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index 8513ae4e..fca7f33e 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -631,6 +631,8 @@ _cairo_xlib_surface_set_clip_region (void *abstract_surface, xr.height = surf->height; XUnionRectWithRegion (&xr, xregion, xregion); rects = malloc(sizeof(XRectangle)); + if (rects == NULL) + return CAIRO_STATUS_NO_MEMORY; rects[0] = xr; m = 1; @@ -641,6 +643,8 @@ _cairo_xlib_surface_set_clip_region (void *abstract_surface, if (n == 0) return CAIRO_STATUS_SUCCESS; rects = malloc(sizeof(XRectangle) * n); + if (rects == NULL) + return CAIRO_STATUS_NO_MEMORY; box = pixman_region_rects (region); xregion = XCreateRegion(); @@ -661,7 +665,7 @@ _cairo_xlib_surface_set_clip_region (void *abstract_surface, XSetClipRectangles(surf->dpy, surf->gc, 0, 0, rects, m, Unsorted); free(rects); if (surf->picture) - XRenderSetPictureClipRegion (surf->dpy, surf->picture, xregion); + XRenderSetPictureClipRegion (surf->dpy, surf->picture, xregion); XDestroyRegion(xregion); XSetGraphicsExposures(surf->dpy, surf->gc, gc_values.graphics_exposures); return CAIRO_STATUS_SUCCESS; @@ -675,6 +679,17 @@ _cairo_xlib_surface_create_pattern (void *abstract_surface, return CAIRO_INT_STATUS_UNSUPPORTED; } +static cairo_status_t +_cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + cairo_surface_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + int num_glyphs); + static const struct cairo_surface_backend cairo_xlib_surface_backend = { _cairo_xlib_surface_create_similar, _cairo_xlib_surface_destroy, @@ -690,7 +705,8 @@ static const struct cairo_surface_backend cairo_xlib_surface_backend = { _cairo_xlib_surface_copy_page, _cairo_xlib_surface_show_page, _cairo_xlib_surface_set_clip_region, - _cairo_xlib_surface_create_pattern + _cairo_xlib_surface_create_pattern, + _cairo_xlib_surface_show_glyphs }; cairo_surface_t * @@ -761,3 +777,512 @@ cairo_xlib_surface_create (Display *dpy, return (cairo_surface_t *) surface; } DEPRECATE (cairo_surface_create_for_drawable, cairo_xlib_surface_create); + + +/* RENDER glyphset cache code */ + +typedef struct glyphset_cache { + cairo_cache_t base; + struct glyphset_cache *next; + Display *display; + XRenderPictFormat *a8_pict_format; + GlyphSet glyphset; + Glyph counter; +} glyphset_cache_t; + +typedef struct { + cairo_glyph_cache_key_t key; + Glyph glyph; + XGlyphInfo info; +} glyphset_cache_entry_t; + +static Glyph +_next_xlib_glyph (glyphset_cache_t *cache) +{ + return ++(cache->counter); +} + + +static cairo_status_t +_xlib_glyphset_cache_create_entry (void *cache, + void *key, + void **return_entry) +{ + glyphset_cache_t *g = (glyphset_cache_t *) cache; + cairo_glyph_cache_key_t *k = (cairo_glyph_cache_key_t *)key; + glyphset_cache_entry_t *v; + + cairo_status_t status; + + cairo_cache_t *im_cache; + cairo_image_glyph_cache_entry_t *im; + + v = malloc (sizeof (glyphset_cache_entry_t)); + _cairo_lock_global_image_glyph_cache (); + im_cache = _cairo_get_global_image_glyph_cache (); + + if (g == NULL || v == NULL ||g == NULL || im_cache == NULL) { + _cairo_unlock_global_image_glyph_cache (); + return CAIRO_STATUS_NO_MEMORY; + } + + status = _cairo_cache_lookup (im_cache, key, (void **) (&im)); + if (status != CAIRO_STATUS_SUCCESS || im == NULL) { + _cairo_unlock_global_image_glyph_cache (); + return CAIRO_STATUS_NO_MEMORY; + } + + v->key = *k; + _cairo_unscaled_font_reference (v->key.unscaled); + + v->glyph = _next_xlib_glyph (g); + + v->info.width = im->image ? im->image->stride : im->size.width; + v->info.height = im->size.height; + v->info.x = - im->extents.x_bearing; + v->info.y = im->extents.y_bearing; + v->info.xOff = 0; + v->info.yOff = 0; + + XRenderAddGlyphs (g->display, g->glyphset, + &(v->glyph), &(v->info), 1, + im->image ? im->image->data : NULL, + im->image ? v->info.height * v->info.width : 0); + + v->key.base.memory = im->image ? im->image->width * im->image->stride : 0; + *return_entry = v; + _cairo_unlock_global_image_glyph_cache (); + return CAIRO_STATUS_SUCCESS; +} + +static void +_xlib_glyphset_cache_destroy_cache (void *cache) +{ + /* FIXME: will we ever release glyphset caches? */ +} + +static void +_xlib_glyphset_cache_destroy_entry (void *cache, void *entry) +{ + glyphset_cache_t *g; + glyphset_cache_entry_t *v; + + g = (glyphset_cache_t *) cache; + v = (glyphset_cache_entry_t *) entry; + + _cairo_unscaled_font_destroy (v->key.unscaled); + XRenderFreeGlyphs (g->display, g->glyphset, &(v->glyph), 1); + free (v); +} + +const cairo_cache_backend_t _xlib_glyphset_cache_backend = { + _cairo_glyph_cache_hash, + _cairo_glyph_cache_keys_equal, + _xlib_glyphset_cache_create_entry, + _xlib_glyphset_cache_destroy_entry, + _xlib_glyphset_cache_destroy_cache +}; + + +static glyphset_cache_t * +_xlib_glyphset_caches = NULL; + +static void +_lock_xlib_glyphset_caches (void) +{ + /* FIXME: implement locking */ +} + +static void +_unlock_xlib_glyphset_caches (void) +{ + /* FIXME: implement locking */ +} + +static glyphset_cache_t * +_get_glyphset_cache (Display *d) +{ + /* + * There should usually only be one, or a very small number, of + * displays. So we just do a linear scan. + */ + + glyphset_cache_t *g; + + for (g = _xlib_glyphset_caches; g != NULL; g = g->next) { + if (g->display == d) + return g; + } + + g = malloc (sizeof (glyphset_cache_t)); + if (g == NULL) + goto ERR; + + g->counter = 0; + g->display = d; + g->a8_pict_format = XRenderFindStandardFormat (d, PictStandardA8); + if (g->a8_pict_format == NULL) + goto ERR; + + if (_cairo_cache_init (&g->base, + &_xlib_glyphset_cache_backend, + CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT)) + goto FREE_GLYPHSET_CACHE; + + g->glyphset = XRenderCreateGlyphSet (d, g->a8_pict_format); + g->next = _xlib_glyphset_caches; + _xlib_glyphset_caches = g; + return g; + + FREE_GLYPHSET_CACHE: + free (g); + + ERR: + return NULL; +} + +#define N_STACK_BUF 1024 + +static cairo_status_t +_cairo_xlib_surface_show_glyphs32 (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + glyphset_cache_t *g, + cairo_glyph_cache_key_t *key, + cairo_xlib_surface_t *src, + cairo_xlib_surface_t *self, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + glyphset_cache_entry_t **entries, + int num_glyphs) +{ + XGlyphElt32 *elts = NULL; + XGlyphElt32 stack_elts [N_STACK_BUF]; + + unsigned int *chars = NULL; + unsigned int stack_chars [N_STACK_BUF]; + + int i; + int lastX = 0, lastY = 0; + + /* Acquire arrays of suitable sizes. */ + if (num_glyphs < N_STACK_BUF) { + elts = stack_elts; + chars = stack_chars; + + } else { + elts = malloc (num_glyphs * sizeof (XGlyphElt32)); + if (elts == NULL) + goto FAIL; + + chars = malloc (num_glyphs * sizeof (unsigned int)); + if (chars == NULL) + goto FREE_ELTS; + + } + + for (i = 0; i < num_glyphs; ++i) { + chars[i] = entries[i]->glyph; + elts[i].chars = &(chars[i]); + elts[i].nchars = 1; + elts[i].glyphset = g->glyphset; + elts[i].xOff = glyphs[i].x - lastX; + elts[i].yOff = glyphs[i].y - lastY; + lastX = glyphs[i].x; + lastY = glyphs[i].y; + } + + XRenderCompositeText32 (self->dpy, + _render_operator (operator), + src->picture, + self->picture, + g->a8_pict_format, + source_x, source_y, + 0, 0, + elts, num_glyphs); + + if (num_glyphs >= N_STACK_BUF) { + free (chars); + free (elts); + } + + return CAIRO_STATUS_SUCCESS; + + FREE_ELTS: + if (num_glyphs >= N_STACK_BUF) + free (elts); + + FAIL: + return CAIRO_STATUS_NO_MEMORY; +} + + +static cairo_status_t +_cairo_xlib_surface_show_glyphs16 (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + glyphset_cache_t *g, + cairo_glyph_cache_key_t *key, + cairo_xlib_surface_t *src, + cairo_xlib_surface_t *self, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + glyphset_cache_entry_t **entries, + int num_glyphs) +{ + XGlyphElt16 *elts = NULL; + XGlyphElt16 stack_elts [N_STACK_BUF]; + + unsigned short *chars = NULL; + unsigned short stack_chars [N_STACK_BUF]; + + int i; + int lastX = 0, lastY = 0; + + /* Acquire arrays of suitable sizes. */ + if (num_glyphs < N_STACK_BUF) { + elts = stack_elts; + chars = stack_chars; + + } else { + elts = malloc (num_glyphs * sizeof (XGlyphElt16)); + if (elts == NULL) + goto FAIL; + + chars = malloc (num_glyphs * sizeof (unsigned short)); + if (chars == NULL) + goto FREE_ELTS; + + } + + for (i = 0; i < num_glyphs; ++i) { + chars[i] = entries[i]->glyph; + elts[i].chars = &(chars[i]); + elts[i].nchars = 1; + elts[i].glyphset = g->glyphset; + elts[i].xOff = glyphs[i].x - lastX; + elts[i].yOff = glyphs[i].y - lastY; + lastX = glyphs[i].x; + lastY = glyphs[i].y; + } + + XRenderCompositeText16 (self->dpy, + _render_operator (operator), + src->picture, + self->picture, + g->a8_pict_format, + source_x, source_y, + 0, 0, + elts, num_glyphs); + + if (num_glyphs >= N_STACK_BUF) { + free (chars); + free (elts); + } + + return CAIRO_STATUS_SUCCESS; + + FREE_ELTS: + if (num_glyphs >= N_STACK_BUF) + free (elts); + + FAIL: + return CAIRO_STATUS_NO_MEMORY; +} + +static cairo_status_t +_cairo_xlib_surface_show_glyphs8 (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + glyphset_cache_t *g, + cairo_glyph_cache_key_t *key, + cairo_xlib_surface_t *src, + cairo_xlib_surface_t *self, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + glyphset_cache_entry_t **entries, + int num_glyphs) +{ + XGlyphElt8 *elts = NULL; + XGlyphElt8 stack_elts [N_STACK_BUF]; + + char *chars = NULL; + char stack_chars [N_STACK_BUF]; + + int i; + int lastX = 0, lastY = 0; + + /* Acquire arrays of suitable sizes. */ + if (num_glyphs < N_STACK_BUF) { + elts = stack_elts; + chars = stack_chars; + + } else { + elts = malloc (num_glyphs * sizeof (XGlyphElt8)); + if (elts == NULL) + goto FAIL; + + chars = malloc (num_glyphs * sizeof (char)); + if (chars == NULL) + goto FREE_ELTS; + + } + + for (i = 0; i < num_glyphs; ++i) { + chars[i] = entries[i]->glyph; + elts[i].chars = &(chars[i]); + elts[i].nchars = 1; + elts[i].glyphset = g->glyphset; + elts[i].xOff = glyphs[i].x - lastX; + elts[i].yOff = glyphs[i].y - lastY; + lastX = glyphs[i].x; + lastY = glyphs[i].y; + } + + XRenderCompositeText8 (self->dpy, + _render_operator (operator), + src->picture, + self->picture, + g->a8_pict_format, + source_x, source_y, + 0, 0, + elts, num_glyphs); + + if (num_glyphs >= N_STACK_BUF) { + free (chars); + free (elts); + } + + return CAIRO_STATUS_SUCCESS; + + FREE_ELTS: + if (num_glyphs >= N_STACK_BUF) + free (elts); + + FAIL: + return CAIRO_STATUS_NO_MEMORY; +} + + +static cairo_status_t +_cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + cairo_surface_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + unsigned int elt_size; + cairo_xlib_surface_t *self = (cairo_xlib_surface_t *) surface; + cairo_image_surface_t *tmp = NULL; + cairo_xlib_surface_t *src = NULL; + glyphset_cache_t *g; + cairo_status_t status; + cairo_glyph_cache_key_t key; + glyphset_cache_entry_t **entries; + glyphset_cache_entry_t *stack_entries [N_STACK_BUF]; + int i; + + /* Acquire an entry array of suitable size. */ + if (num_glyphs < N_STACK_BUF) { + entries = stack_entries; + + } else { + entries = malloc (num_glyphs * sizeof (glyphset_cache_entry_t *)); + if (entries == NULL) + goto FAIL; + } + + /* prep the source surface. */ + if (source->backend == surface->backend) { + src = (cairo_xlib_surface_t *) source; + + } else { + tmp = _cairo_surface_get_image (source); + if (tmp == NULL) + goto FREE_ENTRIES; + + src = (cairo_xlib_surface_t *) + _cairo_surface_create_similar_scratch (surface, self->format, 1, + tmp->width, tmp->height); + + if (src == NULL) + goto FREE_TMP; + + if (_cairo_surface_set_image (&(src->base), tmp) != CAIRO_STATUS_SUCCESS) + goto FREE_SRC; + } + + _lock_xlib_glyphset_caches (); + g = _get_glyphset_cache (self->dpy); + if (g == NULL) + goto UNLOCK; + + /* Work out the index size to use. */ + elt_size = 8; + key.scale = *scale; + key.unscaled = font; + + for (i = 0; i < num_glyphs; ++i) { + key.index = glyphs[i].index; + status = _cairo_cache_lookup (&g->base, &key, (void **) (&entries[i])); + if (status != CAIRO_STATUS_SUCCESS || entries[i] == NULL) + goto UNLOCK; + + if (elt_size == 8 && entries[i]->glyph > 0xff) + elt_size = 16; + if (elt_size == 16 && entries[i]->glyph > 0xffff) { + elt_size = 32; + break; + } + } + + /* Call the appropriate sub-function. */ + + if (elt_size == 8) + status = _cairo_xlib_surface_show_glyphs8 (font, scale, operator, g, &key, src, self, + source_x, source_y, + glyphs, entries, num_glyphs); + else if (elt_size == 16) + status = _cairo_xlib_surface_show_glyphs16 (font, scale, operator, g, &key, src, self, + source_x, source_y, + glyphs, entries, num_glyphs); + else + status = _cairo_xlib_surface_show_glyphs32 (font, scale, operator, g, &key, src, self, + source_x, source_y, + glyphs, entries, num_glyphs); + + _unlock_xlib_glyphset_caches (); + + if (tmp != NULL) { + cairo_surface_destroy (&(src->base)); + cairo_surface_destroy (&(tmp->base)); + } + + if (num_glyphs >= N_STACK_BUF) + free (entries); + + return status; + + UNLOCK: + _unlock_xlib_glyphset_caches (); + + FREE_SRC: + cairo_surface_destroy (&(src->base)); + + FREE_TMP: + cairo_surface_destroy (&(tmp->base)); + + FREE_ENTRIES: + if (num_glyphs >= N_STACK_BUF) + free (entries); + + FAIL: + return CAIRO_STATUS_NO_MEMORY; +} diff --git a/src/cairo.c b/src/cairo.c index bd3ea90f..8bd5f201 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -900,12 +900,27 @@ cairo_text_extents (cairo_t *cr, const unsigned char *utf8, cairo_text_extents_t *extents) { + cairo_glyph_t *glyphs = NULL; + int nglyphs; + CAIRO_CHECK_SANITY (cr); if (cr->status) return; - - cr->status = _cairo_gstate_text_extents (cr->gstate, utf8, extents); + + cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8, &glyphs, &nglyphs); + CAIRO_CHECK_SANITY (cr); + + if (cr->status) { + if (glyphs) + free (glyphs); + return; + } + + cr->status = _cairo_gstate_glyph_extents (cr->gstate, glyphs, nglyphs, extents); CAIRO_CHECK_SANITY (cr); + + if (glyphs) + free (glyphs); } void @@ -926,6 +941,9 @@ cairo_glyph_extents (cairo_t *cr, void cairo_show_text (cairo_t *cr, const unsigned char *utf8) { + cairo_glyph_t *glyphs = NULL; + int nglyphs; + CAIRO_CHECK_SANITY (cr); if (cr->status) return; @@ -933,8 +951,20 @@ cairo_show_text (cairo_t *cr, const unsigned char *utf8) if (utf8 == NULL) return; - cr->status = _cairo_gstate_show_text (cr->gstate, utf8); + cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8, &glyphs, &nglyphs); CAIRO_CHECK_SANITY (cr); + + if (cr->status) { + if (glyphs) + free (glyphs); + return; + } + + cr->status = _cairo_gstate_show_glyphs (cr->gstate, glyphs, nglyphs); + CAIRO_CHECK_SANITY (cr); + + if (glyphs) + free (glyphs); } void @@ -951,12 +981,27 @@ cairo_show_glyphs (cairo_t *cr, cairo_glyph_t *glyphs, int num_glyphs) void cairo_text_path (cairo_t *cr, const unsigned char *utf8) { + cairo_glyph_t *glyphs = NULL; + int nglyphs; + CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_text_path (cr->gstate, utf8); + cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8, &glyphs, &nglyphs); CAIRO_CHECK_SANITY (cr); + + if (cr->status) { + if (glyphs) + free (glyphs); + return; + } + + cr->status = _cairo_gstate_glyph_path (cr->gstate, glyphs, nglyphs); + CAIRO_CHECK_SANITY (cr); + + if (glyphs) + free (glyphs); } void diff --git a/src/cairo_cache.c b/src/cairo_cache.c new file mode 100644 index 00000000..c5ce3993 --- /dev/null +++ b/src/cairo_cache.c @@ -0,0 +1,454 @@ +/* cairo - a vector graphics library with display and print output + * + * This file is Copyright © 2004 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Keith Packard + * Graydon Hoare <graydon@redhat.com> + */ + +#include "cairoint.h" + +/* + * This structure, and accompanying table, is borrowed/modified from the + * file xserver/render/glyph.c in the freedesktop.org x server, with + * permission (and suggested modification of doubling sizes) by Keith + * Packard. + */ + +static cairo_cache_arrangement_t cache_arrangements [] = { + { 16, 43, 41 }, + { 32, 73, 71 }, + { 64, 151, 149 }, + { 128, 283, 281 }, + { 256, 571, 569 }, + { 512, 1153, 1151 }, + { 1024, 2269, 2267 }, + { 2048, 4519, 4517 }, + { 4096, 9013, 9011 }, + { 8192, 18043, 18041 }, + { 16384, 36109, 36107 }, + { 32768, 72091, 72089 }, + { 65536, 144409, 144407 }, + { 131072, 288361, 288359 }, + { 262144, 576883, 576881 }, + { 524288, 1153459, 1153457 }, + { 1048576, 2307163, 2307161 }, + { 2097152, 4613893, 4613891 }, + { 4194304, 9227641, 9227639 }, + { 8388608, 18455029, 18455027 }, + { 16777216, 36911011, 36911009 }, + { 33554432, 73819861, 73819859 }, + { 67108864, 147639589, 147639587 }, + { 134217728, 295279081, 295279079 }, + { 268435456, 590559793, 590559791 } +}; + +#define N_CACHE_SIZES (sizeof(cache_arrangements)/sizeof(cache_arrangements[0])) + +/* + * Entries 'e' are poiners, in one of 3 states: + * + * e == NULL: The entry has never had anything put in it + * e != DEAD_ENTRY: The entry has an active value in it currently + * e == DEAD_ENTRY: The entry *had* a value in it at some point, but the + * entry has been killed. Lookups requesting free space can + * reuse these entries; lookups requesting a precise match + * should neither return these entries nor stop searching when + * seeing these entries. + * + * We expect keys will not be destroyed frequently, so our table does not + * contain any explicit shrinking code nor any chain-coalescing code for + * entries randomly deleted by memory pressure (except during rehashing, of + * course). These assumptions are potentially bad, but they make the + * implementation straightforward. + * + * Revisit later if evidence appears that we're using excessive memory from + * a mostly-dead table. + * + * Generally you do not need to worry about freeing cache entries; the + * cache will expire entries randomly as it experiences memory pressure. + * There is currently no explicit entry-removing call, though one can be + * added easily. + * + * This table is open-addressed with double hashing. Each table size is a + * prime chosen to be a little more than double the high water mark for a + * given arrangement, so the tables should remain < 50% full. The table + * size makes for the "first" hash modulus; a second prime (2 less than the + * first prime) serves as the "second" hash modulus, which is co-prime and + * thus guarantees a complete permutation of table indices. + * + */ + +#define DEAD_ENTRY ((cairo_cache_entry_base_t *) 1) +#define NULL_ENTRY_P(cache, i) ((cache)->entries[i] == NULL) +#define DEAD_ENTRY_P(cache, i) ((cache)->entries[i] == DEAD_ENTRY) +#define LIVE_ENTRY_P(cache, i) \ + (!((NULL_ENTRY_P((cache),(i))) || (DEAD_ENTRY_P((cache),(i))))) + +#ifdef CAIRO_DO_SANITY_CHECKING +#include <assert.h> +static void +_cache_sane_state (cairo_cache_t *cache) +{ + assert (cache != NULL); + assert (cache->entries != NULL); + assert (cache->backend != NULL); + assert (cache->arrangement != NULL); + assert (cache->refcount > 0); + assert (cache->used_memory <= cache->max_memory); + assert (cache->live_entries <= cache->arrangement->size); +} +#else +#define _cache_sane_state(c) +#define assert(x) +#endif + +static void +_entry_destroy (cairo_cache_t *cache, unsigned long i) +{ + _cache_sane_state (cache); + + if (LIVE_ENTRY_P(cache, i)) + { + cairo_cache_entry_base_t *entry = cache->entries[i]; + assert(cache->live_entries > 0); + assert(cache->used_memory > entry->memory); + + cache->live_entries--; + cache->used_memory -= entry->memory; + cache->backend->destroy_entry (cache, entry); + cache->entries[i] = DEAD_ENTRY; + } +} + +static cairo_cache_entry_base_t ** +_cache_lookup (cairo_cache_t *cache, + void *key, + int (*predicate)(void*,void*,void*)) +{ + + cairo_cache_entry_base_t **probe; + unsigned long hash; + unsigned long table_size, i, idx, step; + + _cache_sane_state (cache); + assert (key != NULL); + + table_size = cache->arrangement->size; + hash = cache->backend->hash (cache, key); + idx = hash % table_size; + step = 0; + + for (i = 0; i < table_size; ++i) + { +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + cache->probes++; +#endif + assert(idx < table_size); + probe = cache->entries + idx; + + /* + * There are two lookup modes: searching for a free slot and searching + * for an exact entry. + */ + + if (predicate != NULL) + { + /* We are looking up an exact entry. */ + if (*probe != NULL + && *probe != DEAD_ENTRY + && (*probe)->hashcode == hash + && predicate (cache, key, *probe)) + return probe; + } + else + { + /* We are just looking for a free slot. */ + if (*probe == NULL + || *probe == DEAD_ENTRY) + return probe; + } + + if (step == 0) { + step = hash % cache->arrangement->rehash; + if (step == 0) + step = 1; + } + + idx += step; + if (idx >= table_size) + idx -= table_size; + } + + /* + * The table should not have permitted you to get here if you were just + * looking for a free slot: there should have been room. + */ + assert(predicate != NULL); + return NULL; +} + +static cairo_cache_entry_base_t ** +_find_available_entry_for (cairo_cache_t *cache, + void *key) +{ + return _cache_lookup (cache, key, NULL); +} + +static cairo_cache_entry_base_t ** +_find_exact_live_entry_for (cairo_cache_t *cache, + void *key) +{ + return _cache_lookup (cache, key, cache->backend->keys_equal); +} + + +static cairo_cache_arrangement_t * +_find_cache_arrangement (unsigned long proposed_size) +{ + unsigned long idx; + + for (idx = 0; idx < N_CACHE_SIZES; ++idx) + if (cache_arrangements[idx].high_water_mark >= proposed_size) + return &cache_arrangements[idx]; + return NULL; +} + +static cairo_status_t +_resize_cache (cairo_cache_t *cache, unsigned long proposed_size) +{ + cairo_cache_t tmp; + cairo_cache_entry_base_t **e; + unsigned long new_size, i; + + tmp = *cache; + tmp.arrangement = _find_cache_arrangement (proposed_size); + assert(tmp.arrangement != NULL); + if (tmp.arrangement == cache->arrangement) + return CAIRO_STATUS_SUCCESS; + + new_size = tmp.arrangement->size; + tmp.entries = calloc (new_size, sizeof (cairo_cache_entry_base_t *)); + if (tmp.entries == NULL) + return CAIRO_STATUS_NO_MEMORY; + + for (i = 0; i < cache->arrangement->size; ++i) { + if (LIVE_ENTRY_P(cache, i)) { + e = _find_available_entry_for (&tmp, cache->entries[i]); + assert (e != NULL); + *e = cache->entries[i]; + } + } + free (cache->entries); + cache->entries = tmp.entries; + cache->arrangement = tmp.arrangement; + return CAIRO_STATUS_SUCCESS; +} + + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE +static double +_load_factor (cairo_cache_t *cache) +{ + return ((double) cache->live_entries) + / ((double) cache->arrangement->size); +} +#endif + +static unsigned long +_random_live_entry (cairo_cache_t *cache) +{ + unsigned long idx; + assert(cache != NULL); + do { + idx = rand () % cache->arrangement->size; + } while (! LIVE_ENTRY_P(cache, idx)); + return idx; +} + + +/* public API follows */ + +cairo_status_t +_cairo_cache_init (cairo_cache_t *cache, + const cairo_cache_backend_t *backend, + unsigned long max_memory) +{ + assert(backend != NULL); + + if (cache != NULL){ + cache->arrangement = &cache_arrangements[0]; + cache->refcount = 1; + cache->max_memory = max_memory; + cache->used_memory = 0; + cache->live_entries = 0; + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + cache->hits = 0; + cache->misses = 0; + cache->probes = 0; +#endif + + cache->backend = backend; + cache->entries = calloc (sizeof(cairo_cache_entry_base_t *), + cache->arrangement->size); + if (cache->entries == NULL) + return CAIRO_STATUS_NO_MEMORY; + } + _cache_sane_state (cache); + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_cache_reference (cairo_cache_t *cache) +{ + _cache_sane_state (cache); + cache->refcount++; +} + +void +_cairo_cache_destroy (cairo_cache_t *cache) +{ + unsigned long i; + if (cache != NULL) { + + _cache_sane_state (cache); + + if (cache->refcount-- > 0) + return; + + for (i = 0; i < cache->arrangement->size; ++i) { + _entry_destroy (cache, i); + } + + free (cache->entries); + cache->entries = NULL; + cache->backend->destroy_cache (cache); + } +} + +cairo_status_t +_cairo_cache_lookup (cairo_cache_t *cache, + void *key, + void **entry_return) +{ + + unsigned long idx; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_cache_entry_base_t **slot = NULL, *new_entry; + + _cache_sane_state (cache); + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + if ((cache->hits + cache->misses) % 0xffff == 0) + printf("cache %p stats: size %ld, live %ld, load %.2f\n" + " mem %ld/%ld, hit %ld, miss %ld\n" + " probe %ld, %.2f probe/access\n", + cache, + cache->arrangement->size, + cache->live_entries, + _load_factor (cache), + cache->used_memory, + cache->max_memory, + cache->hits, + cache->misses, + cache->probes, + ((double) cache->probes) + / ((double) (cache->hits + + cache->misses + 1))); +#endif + + /* See if we have an entry in the table already. */ + slot = _find_exact_live_entry_for (cache, key); + if (slot != NULL) { +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + cache->hits++; +#endif + *entry_return = *slot; + return status; + } + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + cache->misses++; +#endif + + /* Build the new entry. */ + status = cache->backend->create_entry (cache, key, + entry_return); + if (status != CAIRO_STATUS_SUCCESS) + return status; + + new_entry = (cairo_cache_entry_base_t *) (*entry_return); + + /* Store the hash value in case the backend forgot. */ + new_entry->hashcode = cache->backend->hash (cache, key); + + /* Make some entries die if we're under memory pressure. */ + while (cache->live_entries > 0 && + ((cache->max_memory - cache->used_memory) < new_entry->memory)) { + idx = _random_live_entry (cache); + assert (idx < cache->arrangement->size); + _entry_destroy (cache, idx); + } + + assert(cache->max_memory >= (cache->used_memory + new_entry->memory)); + + /* Make room in the table for a new slot. */ + status = _resize_cache (cache, cache->live_entries + 1); + if (status != CAIRO_STATUS_SUCCESS) { + cache->backend->destroy_entry (cache, new_entry); + *entry_return = NULL; + return status; + } + + slot = _find_available_entry_for (cache, key); + assert(slot != NULL); + + /* Store entry in slot and increment statistics. */ + *slot = new_entry; + cache->live_entries++; + cache->used_memory += new_entry->memory; + + _cache_sane_state (cache); + + return status; +} + +unsigned long +_cairo_hash_string (const char *c) +{ + /* This is the djb2 hash. */ + unsigned long hash = 5381; + while (*c) + hash = ((hash << 5) + hash) + *c++; + return hash; +} + diff --git a/src/cairo_font.c b/src/cairo_font.c index 1b356698..c90a02bd 100644 --- a/src/cairo_font.c +++ b/src/cairo_font.c @@ -36,20 +36,60 @@ #include "cairoint.h" -static cairo_glyph_cache_t * -_cairo_glyph_cache_create (void); -static void -_cairo_glyph_cache_destroy (cairo_glyph_cache_t *glyph_cache); +/* First we implement a global font cache for named fonts. */ + +typedef struct { + cairo_cache_entry_base_t base; + const char *family; + cairo_font_slant_t slant; + cairo_font_weight_t weight; +} cairo_font_cache_key_t; + +typedef struct { + cairo_font_cache_key_t key; + cairo_unscaled_font_t *unscaled; +} cairo_font_cache_entry_t; + +static unsigned long +_font_cache_hash (void *cache, void *key) +{ + cairo_font_cache_key_t *in; + in = (cairo_font_cache_key_t *) key; + unsigned long hash; + + /* 1607 and 1451 are just a couple random primes. */ + hash = _cairo_hash_string (in->family); + hash += ((unsigned long) in->slant) * 1607; + hash += ((unsigned long) in->weight) * 1451; + return hash; +} -static void -_cairo_glyph_cache_reference (cairo_glyph_cache_t *glyph_cache); -cairo_font_t * -_cairo_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) +static int +_font_cache_keys_equal (void *cache, + void *k1, + void *k2) { + cairo_font_cache_key_t *a, *b; + a = (cairo_font_cache_key_t *) k1; + b = (cairo_font_cache_key_t *) k2; + + return (strcmp (a->family, b->family) == 0) + && (a->weight == b->weight) + && (a->slant == b->slant); +} + + +static cairo_status_t +_font_cache_create_entry (void *cache, + void *key, + void **return_value) +{ + cairo_font_cache_key_t *k; + cairo_font_cache_entry_t *entry; + k = (cairo_font_cache_key_t *) key; + const struct cairo_font_backend *backend = CAIRO_FONT_BACKEND_DEFAULT; /* XXX: The current freetype backend may return NULL, (for example @@ -58,365 +98,428 @@ _cairo_font_create (const char *family, * like to build in some sort fo font here, (even a really lame, * ugly one if necessary). */ - return backend->create (family, slant, weight); + entry = malloc (sizeof (cairo_font_cache_entry_t)); + if (entry == NULL) + goto FAIL; + + entry->key.slant = k->slant; + entry->key.weight = k->weight; + entry->key.family = strdup(k->family); + if (entry->key.family == NULL) + goto FREE_ENTRY; + + entry->unscaled = backend->create (k->family, k->slant, k->weight); + if (entry->unscaled == NULL) + goto FREE_FAMILY; + + /* Not sure how to measure backend font mem; use a simple count for now.*/ + entry->key.base.memory = 1; + *return_value = entry; + return CAIRO_STATUS_SUCCESS; + + FREE_FAMILY: + free ((void *) entry->key.family); + + FREE_ENTRY: + free (entry); + + FAIL: + return CAIRO_STATUS_NO_MEMORY; } -cairo_status_t -_cairo_font_init (cairo_font_t *font, - const struct cairo_font_backend *backend) +static void +_font_cache_destroy_entry (void *cache, + void *entry) { - cairo_matrix_set_identity (&font->matrix); - font->refcount = 1; - font->backend = backend; - font->glyph_cache = _cairo_glyph_cache_create (); - if (font->glyph_cache == NULL) - return CAIRO_STATUS_NO_MEMORY; + cairo_font_cache_entry_t *e; - return CAIRO_STATUS_SUCCESS; + e = (cairo_font_cache_entry_t *) entry; + _cairo_unscaled_font_destroy (e->unscaled); + free ((void *) e->key.family); + free (e); } -cairo_font_t * -_cairo_font_copy (cairo_font_t *font) +static void +_font_cache_destroy_cache (void *cache) { - cairo_font_t *newfont = NULL; - char *tmp = NULL; + free (cache); +} - if (font == NULL || font->backend->copy == NULL) - return NULL; - - newfont = font->backend->copy (font); - if (newfont == NULL) { - free (tmp); - return NULL; - } +const struct cairo_cache_backend cairo_font_cache_backend = { + _font_cache_hash, + _font_cache_keys_equal, + _font_cache_create_entry, + _font_cache_destroy_entry, + _font_cache_destroy_cache +}; - newfont->refcount = 1; - cairo_matrix_copy(&newfont->matrix, &font->matrix); - newfont->backend = font->backend; - if (newfont->glyph_cache) - _cairo_glyph_cache_destroy (newfont->glyph_cache); - - newfont->glyph_cache = font->glyph_cache; - _cairo_glyph_cache_reference (font->glyph_cache); - - return newfont; +static void +_lock_global_font_cache (void) +{ + /* FIXME: implement locking. */ } -cairo_status_t -_cairo_font_scale (cairo_font_t *font, double scale) +static void +_unlock_global_font_cache (void) { - return cairo_matrix_scale (&font->matrix, scale, scale); + /* FIXME: implement locking. */ } -cairo_status_t -_cairo_font_transform (cairo_font_t *font, cairo_matrix_t *matrix) +static cairo_cache_t * +_global_font_cache = NULL; + +static cairo_cache_t * +_get_global_font_cache (void) { - return cairo_matrix_multiply (&font->matrix, matrix, &font->matrix); + if (_global_font_cache == NULL) { + _global_font_cache = malloc (sizeof (cairo_cache_t)); + + if (_global_font_cache == NULL) + goto FAIL; + + if (_cairo_cache_init (_global_font_cache, + &cairo_font_cache_backend, + CAIRO_FONT_CACHE_NUM_FONTS_DEFAULT)) + goto FAIL; + } + + return _global_font_cache; + + FAIL: + if (_global_font_cache) + free (_global_font_cache); + _global_font_cache = NULL; + return NULL; } -cairo_status_t -_cairo_font_text_extents (cairo_font_t *font, - const unsigned char *utf8, - cairo_text_extents_t *extents) +/* Now the internal "unscaled + scale" font API */ + +cairo_unscaled_font_t * +_cairo_unscaled_font_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) { - return font->backend->text_extents(font, utf8, extents); + cairo_cache_t * cache; + cairo_font_cache_key_t key; + cairo_font_cache_entry_t *font; + cairo_status_t status; + + _lock_global_font_cache (); + cache = _get_global_font_cache (); + if (cache == NULL) { + _unlock_global_font_cache (); + return NULL; + } + + key.family = family; + key.slant = slant; + key.weight = weight; + + status = _cairo_cache_lookup (cache, &key, (void **) &font); + if (status) { + _unlock_global_font_cache (); + return NULL; + } + + _cairo_unscaled_font_reference (font->unscaled); + _unlock_global_font_cache (); + return font->unscaled; } -cairo_status_t -_cairo_font_glyph_extents (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) +void +_cairo_font_init (cairo_font_t *scaled, + cairo_font_scale_t *scale, + cairo_unscaled_font_t *unscaled) { - return font->backend->glyph_extents(font, glyphs, num_glyphs, extents); + scaled->scale = *scale; + scaled->unscaled = unscaled; + scaled->refcount = 1; } cairo_status_t -_cairo_font_text_bbox (cairo_font_t *font, - cairo_surface_t *surface, - double x, - double y, - const unsigned char *utf8, - cairo_box_t *bbox) +_cairo_unscaled_font_init (cairo_unscaled_font_t *font, + const struct cairo_font_backend *backend) { - return font->backend->text_bbox (font, surface, x, y, utf8, bbox); + font->refcount = 1; + font->backend = backend; + return CAIRO_STATUS_SUCCESS; } + cairo_status_t -_cairo_font_glyph_bbox (cairo_font_t *font, - cairo_surface_t *surface, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) +_cairo_unscaled_font_text_to_glyphs (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) { - return font->backend->glyph_bbox (font, surface, glyphs, num_glyphs, bbox); + return font->backend->text_to_glyphs (font, scale, utf8, glyphs, num_glyphs); } cairo_status_t -_cairo_font_show_text (cairo_font_t *font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - double x, - double y, - const unsigned char *utf8) +_cairo_unscaled_font_glyph_extents (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) { - return font->backend->show_text(font, operator, source, - surface, source_x, source_y, x, y, utf8); + return font->backend->glyph_extents(font, scale, glyphs, num_glyphs, extents); } + cairo_status_t -_cairo_font_show_glyphs (cairo_font_t *font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_unscaled_font_glyph_bbox (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) { - return font->backend->show_glyphs(font, operator, source, - surface, source_x, source_y, - glyphs, num_glyphs); + return font->backend->glyph_bbox (font, scale, glyphs, num_glyphs, bbox); } cairo_status_t -_cairo_font_text_path (cairo_font_t *font, - double x, - double y, - const unsigned char *utf8, - cairo_path_t *path) +_cairo_unscaled_font_show_glyphs (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + cairo_surface_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + cairo_glyph_t *glyphs, + int num_glyphs) { - return font->backend->text_path(font, x, y, utf8, path); + cairo_status_t status; + if (surface->backend->show_glyphs != NULL) { + status = surface->backend->show_glyphs (font, scale, operator, source, + surface, source_x, source_y, + glyphs, num_glyphs); + if (status == CAIRO_STATUS_SUCCESS) + return status; + } + + /* Surface display routine either does not exist or failed. */ + return font->backend->show_glyphs (font, scale, operator, source, + surface, source_x, source_y, + glyphs, num_glyphs); } cairo_status_t -_cairo_font_glyph_path (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) +_cairo_unscaled_font_glyph_path (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path) { - return font->backend->glyph_path(font, glyphs, num_glyphs, path); + return font->backend->glyph_path (font, scale, glyphs, num_glyphs, path); } cairo_status_t -_cairo_font_font_extents (cairo_font_t *font, - cairo_font_extents_t *extents) +_cairo_unscaled_font_font_extents (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_font_extents_t *extents) { - return font->backend->font_extents(font, extents); + return font->backend->font_extents(font, scale, extents); } -static void -_cairo_glyph_cache_pop_last (cairo_glyph_cache_t *glyph_cache) +void +_cairo_unscaled_font_reference (cairo_unscaled_font_t *font) { - if (glyph_cache->last) { - cairo_glyph_surface_node_t *remove = glyph_cache->last; - - cairo_surface_destroy (remove->s.surface); - glyph_cache->last = remove->prev; - if (glyph_cache->last) - glyph_cache->last->next = NULL; + font->refcount++; +} - free (remove); - glyph_cache->n_nodes--; - } +void +_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font) +{ + if (--(font->refcount) > 0) + return; + + if (font->backend) + font->backend->destroy (font); } -static cairo_glyph_cache_t * -_cairo_glyph_cache_create (void) -{ - cairo_glyph_cache_t *glyph_cache; - - glyph_cache = malloc (sizeof (cairo_glyph_cache_t)); - if (glyph_cache == NULL) - return NULL; - - glyph_cache->n_nodes = 0; - glyph_cache->first = NULL; - glyph_cache->last = NULL; - glyph_cache->cache_size = CAIRO_FONT_CACHE_SIZE_DEFAULT; - glyph_cache->ref_count = 1; - return glyph_cache; + +/* Public font API follows. */ + +void +cairo_font_reference (cairo_font_t *font) +{ + font->refcount++; } -static void -_cairo_glyph_cache_reference (cairo_glyph_cache_t *glyph_cache) +void +cairo_font_destroy (cairo_font_t *font) { - if (glyph_cache == NULL) + if (--(font->refcount) > 0) return; - glyph_cache->ref_count++; + if (font->unscaled) + _cairo_unscaled_font_destroy (font->unscaled); + + free (font); } -static void -_cairo_glyph_cache_destroy (cairo_glyph_cache_t *glyph_cache) +void +cairo_font_set_transform (cairo_font_t *font, + cairo_matrix_t *matrix) { - if (glyph_cache == NULL) - return; + double dummy; + cairo_matrix_get_affine (matrix, + &font->scale.matrix[0][0], + &font->scale.matrix[0][1], + &font->scale.matrix[1][0], + &font->scale.matrix[1][1], + &dummy, &dummy); +} - glyph_cache->ref_count--; - if (glyph_cache->ref_count) - return; +void +cairo_font_current_transform (cairo_font_t *font, + cairo_matrix_t *matrix) +{ + cairo_matrix_set_affine (matrix, + font->scale.matrix[0][0], + font->scale.matrix[0][1], + font->scale.matrix[1][0], + font->scale.matrix[1][1], + 0, 0); +} - while (glyph_cache->last) - _cairo_glyph_cache_pop_last (glyph_cache); - free (glyph_cache); +/* Now we implement functions to access a default global image & metrics + * cache. + */ + +unsigned long +_cairo_glyph_cache_hash (void *cache, void *key) +{ + cairo_glyph_cache_key_t *in; + in = (cairo_glyph_cache_key_t *) key; + return + ((unsigned long) in->unscaled) + ^ ((unsigned long) in->scale.matrix[0][0]) + ^ ((unsigned long) in->scale.matrix[0][1]) + ^ ((unsigned long) in->scale.matrix[1][0]) + ^ ((unsigned long) in->scale.matrix[1][1]) + ^ in->index; } -static void -_cairo_glyph_surface_init (cairo_font_t *font, - cairo_surface_t *surface, - const cairo_glyph_t *glyph, - cairo_glyph_surface_t *glyph_surface) +int +_cairo_glyph_cache_keys_equal (void *cache, + void *k1, + void *k2) { - cairo_surface_t *image; - - glyph_surface->surface = NULL; - glyph_surface->index = glyph->index; - glyph_surface->matrix[0][0] = font->matrix.m[0][0]; - glyph_surface->matrix[0][1] = font->matrix.m[0][1]; - glyph_surface->matrix[1][0] = font->matrix.m[1][0]; - glyph_surface->matrix[1][1] = font->matrix.m[1][1]; - - image = font->backend->create_glyph (font, glyph, &glyph_surface->size); - if (image == NULL) - return; - - if (surface->backend != image->backend) { - cairo_status_t status; - - glyph_surface->surface = - _cairo_surface_create_similar_scratch (surface, - CAIRO_FORMAT_A8, 0, - glyph_surface->size.width, - glyph_surface->size.height); - if (glyph_surface->surface == NULL) { - glyph_surface->surface = image; - return; - } - - status = _cairo_surface_set_image (glyph_surface->surface, - (cairo_image_surface_t *) image); - if (status) { - cairo_surface_destroy (glyph_surface->surface); - glyph_surface->surface = NULL; - } - cairo_surface_destroy (image); - } else - glyph_surface->surface = image; + cairo_glyph_cache_key_t *a, *b; + a = (cairo_glyph_cache_key_t *) k1; + b = (cairo_glyph_cache_key_t *) k2; + return (a->index == b->index) + && (a->unscaled == b->unscaled) + && (a->scale.matrix[0][0] == b->scale.matrix[0][0]) + && (a->scale.matrix[0][1] == b->scale.matrix[0][1]) + && (a->scale.matrix[1][0] == b->scale.matrix[1][0]) + && (a->scale.matrix[1][1] == b->scale.matrix[1][1]); } -cairo_surface_t * -_cairo_font_lookup_glyph (cairo_font_t *font, - cairo_surface_t *surface, - const cairo_glyph_t *glyph, - cairo_glyph_size_t *return_size) + +static cairo_status_t +_image_glyph_cache_create_entry (void *cache, + void *key, + void **return_value) { - cairo_glyph_surface_t glyph_surface; - cairo_glyph_cache_t *cache = font->glyph_cache; - cairo_glyph_surface_node_t *node; - - for (node = cache->first; node != NULL; node = node->next) { - cairo_glyph_surface_t *s = &node->s; - - if ((s->surface == NULL || s->surface->backend == surface->backend) && - s->index == glyph->index && - s->matrix[0][0] == font->matrix.m[0][0] && - s->matrix[0][1] == font->matrix.m[0][1] && - s->matrix[1][0] == font->matrix.m[1][0] && - s->matrix[1][1] == font->matrix.m[1][1]) { - - /* move node first in cache */ - if (node->prev) { - if (node->next == NULL) { - cache->last = node->prev; - node->prev->next = NULL; - } else { - node->prev->next = node->next; - node->next->prev = node->prev; - } - - node->prev = NULL; - node->next = cache->first; - cache->first = node; - if (node->next) - node->next->prev = node; - else - cache->last = node; - } - - cairo_surface_reference (s->surface); - *return_size = s->size; - - return s->surface; - } - } - - _cairo_glyph_surface_init (font, surface, glyph, &glyph_surface); + cairo_glyph_cache_key_t *k = (cairo_glyph_cache_key_t *) key; + cairo_image_glyph_cache_entry_t *im; + cairo_status_t status; - *return_size = glyph_surface.size; - - if (cache->cache_size > 0) { - if (cache->n_nodes == cache->cache_size) - _cairo_glyph_cache_pop_last (cache); - - node = malloc (sizeof (cairo_glyph_surface_node_t)); - if (node) { - cairo_surface_reference (glyph_surface.surface); - - /* insert node first in cache */ - node->s = glyph_surface; - node->prev = NULL; - node->next = cache->first; - cache->first = node; - if (node->next) - node->next->prev = node; - else - cache->last = node; - - cache->n_nodes++; - } + im = calloc (1, sizeof (cairo_image_glyph_cache_entry_t)); + if (im == NULL) + return CAIRO_STATUS_NO_MEMORY; + + im->key = *k; + status = im->key.unscaled->backend->create_glyph (im); + + if (status != CAIRO_STATUS_SUCCESS) { + free (im); + return status; } - - return glyph_surface.surface; + + _cairo_unscaled_font_reference (im->key.unscaled); + + im->key.base.memory = + sizeof (cairo_image_glyph_cache_entry_t) + + (im->image ? + sizeof (cairo_image_surface_t) + + 28 * sizeof (int) /* rough guess at size of pixman image structure */ + + (im->image->height * im->image->stride) : 0); + + *return_value = im; + + return CAIRO_STATUS_SUCCESS; } -/* public font interface follows */ -void -cairo_font_reference (cairo_font_t *font) +static void +_image_glyph_cache_destroy_entry (void *cache, + void *value) { - font->refcount++; + cairo_image_glyph_cache_entry_t *im; + + im = (cairo_image_glyph_cache_entry_t *) value; + _cairo_unscaled_font_destroy (im->key.unscaled); + cairo_surface_destroy (&(im->image->base)); + free (im); } -void -cairo_font_destroy (cairo_font_t *font) +static void +_image_glyph_cache_destroy_cache (void *cache) { - if (--(font->refcount) > 0) - return; + free (cache); +} - _cairo_glyph_cache_destroy (font->glyph_cache); +const cairo_cache_backend_t cairo_image_cache_backend = { + _cairo_glyph_cache_hash, + _cairo_glyph_cache_keys_equal, + _image_glyph_cache_create_entry, + _image_glyph_cache_destroy_entry, + _image_glyph_cache_destroy_cache +}; - if (font->backend->destroy) - font->backend->destroy (font); -} void -cairo_font_set_transform (cairo_font_t *font, - cairo_matrix_t *matrix) +_cairo_lock_global_image_glyph_cache() { - cairo_matrix_copy (&(font->matrix), matrix); + /* FIXME: implement locking. */ } void -cairo_font_current_transform (cairo_font_t *font, - cairo_matrix_t *matrix) +_cairo_unlock_global_image_glyph_cache() { - cairo_matrix_copy (matrix, &(font->matrix)); + /* FIXME: implement locking. */ +} + +static cairo_cache_t * +_global_image_glyph_cache = NULL; + +cairo_cache_t * +_cairo_get_global_image_glyph_cache () +{ + if (_global_image_glyph_cache == NULL) { + _global_image_glyph_cache = malloc (sizeof (cairo_cache_t)); + + if (_global_image_glyph_cache == NULL) + goto FAIL; + + if (_cairo_cache_init (_global_image_glyph_cache, + &cairo_image_cache_backend, + CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT)) + goto FAIL; + } + + return _global_image_glyph_cache; + + FAIL: + if (_global_image_glyph_cache) + free (_global_image_glyph_cache); + _global_image_glyph_cache = NULL; + return NULL; } diff --git a/src/cairo_ft_font.c b/src/cairo_ft_font.c index 5b0a7f64..74061350 100644 --- a/src/cairo_ft_font.c +++ b/src/cairo_ft_font.c @@ -31,116 +31,294 @@ #include FT_OUTLINE_H #include FT_IMAGE_H -typedef struct { - cairo_font_t base; +#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0)) +#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0) +#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) +#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) - FT_Library ft_library; - int owns_ft_library; +/* + * First we make a private, sharable implementation object which can be + * stored both in a private cache and in public font objects (including + * those connected to fonts we don't own) + */ + +typedef struct { + int refcount; FT_Face face; int owns_face; - FcPattern *pattern; -} cairo_ft_font_t; +} ft_font_val_t; -#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0)) -#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0) -#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) -#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) +static ft_font_val_t * +_create_from_face (FT_Face face, int owns_face) +{ + ft_font_val_t *tmp = malloc (sizeof(ft_font_val_t)); + if (tmp) { + tmp->refcount = 1; + tmp->face = face; + tmp->owns_face = owns_face; + FT_Set_Char_Size (face, + DOUBLE_TO_26_6 (1.0), + DOUBLE_TO_26_6 (1.0), + 0, 0); + } + return tmp; +} -/* implement the platform-specific interface */ +static void +_reference_font_val (ft_font_val_t *f) +{ + f->refcount++; +} -cairo_font_t * -cairo_ft_font_create (FT_Library ft_library, FcPattern *pattern) +static void +_destroy_font_val (ft_font_val_t *f) { - cairo_ft_font_t *f = NULL; + if (--(f->refcount) > 0) + return; + + if (f->owns_face) + FT_Done_Face (f->face); + + free (f); +} + +static ft_font_val_t * +_create_from_library_and_pattern (FT_Library ft_library, FcPattern *pattern) +{ + ft_font_val_t *f = NULL; char *filename = NULL; - FT_Face face = NULL; int owns_face = 0; + FT_Face face = NULL; FcPattern *resolved = NULL; FcResult result = FcResultMatch; - + + if (pattern == NULL) + goto FAIL; + FcConfigSubstitute (0, pattern, FcMatchPattern); FcDefaultSubstitute (pattern); resolved = FcFontMatch (0, pattern, &result); + if (!resolved) + goto FAIL; + if (result != FcResultMatch) - { - if (resolved) - FcPatternDestroy (resolved); - return NULL; - } + goto FREE_RESOLVED; /* If the pattern has an FT_Face object, use that. */ if (FcPatternGetFTFace (resolved, FC_FT_FACE, 0, &face) != FcResultMatch || face == NULL) { - /* otherwise it had better have a filename */ - int open_res = 0; - owns_face = 1; result = FcPatternGetString (resolved, FC_FILE, 0, (FcChar8 **)(&filename)); if (result == FcResultMatch) - open_res = FT_New_Face (ft_library, filename, 0, &face); + if (FT_New_Face (ft_library, filename, 0, &face)) + goto FREE_RESOLVED; if (face == NULL) - return NULL; + goto FREE_RESOLVED; + + owns_face = 1; } - f = (cairo_ft_font_t *) cairo_ft_font_create_for_ft_face (face); - if (f != NULL) - f->pattern = FcPatternDuplicate (resolved); + f = _create_from_face (face, owns_face); + + FcPatternDestroy (resolved); + return f; + + FREE_RESOLVED: + if (resolved) + FcPatternDestroy (resolved); + + FAIL: + return NULL; +} - f->ft_library = ft_library; - f->owns_ft_library = 0; - f->owns_face = owns_face; +/* + * We then make the user-exposed structure out of one of these impls, such + * that it is reasonably cheap to copy and/or destroy. Unfortunately this + * duplicates a certain amount of the caching machinery in the font cache, + * but that's unavoidable as we also provide an FcPattern resolution API, + * which is not part of cairo's generic font finding system. + */ - FcPatternDestroy (resolved); - return (cairo_font_t *) f; +typedef struct { + cairo_unscaled_font_t base; + FcPattern *pattern; + ft_font_val_t *val; +} cairo_ft_font_t; + +/* + * We then make a key and entry type which are compatible with the generic + * cache system. This cache serves to share single ft_font_val_t instances + * between fonts (or between font lifecycles). + */ + +typedef struct { + cairo_cache_entry_base_t base; + FcPattern *pattern; +} cairo_ft_cache_key_t; + +typedef struct { + cairo_ft_cache_key_t key; + ft_font_val_t *val; +} cairo_ft_cache_entry_t; + +/* + * Then we create a cache which maps FcPattern keys to the refcounted + * ft_font_val_t values. + */ + +typedef struct { + cairo_cache_t base; + FT_Library lib; +} ft_cache_t; + + +static unsigned long +_ft_font_cache_hash (void *cache, void *key) +{ + cairo_ft_cache_key_t *in; + in = (cairo_ft_cache_key_t *) key; + return FcPatternHash (in->pattern); } -FT_Face -cairo_ft_font_face (cairo_font_t *abstract_font) +static int +_ft_font_cache_keys_equal (void *cache, + void *k1, + void *k2) { - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + cairo_ft_cache_key_t *a; + cairo_ft_cache_key_t *b; + a = (cairo_ft_cache_key_t *) k1; + b = (cairo_ft_cache_key_t *) k2; + + return FcPatternEqual (a->pattern, b->pattern); +} - if (font == NULL) - return NULL; - return font->face; +static cairo_status_t +_ft_font_cache_create_entry (void *cache, + void *key, + void **return_entry) +{ + ft_cache_t *ftcache = (ft_cache_t *) cache; + cairo_ft_cache_key_t *k = (cairo_ft_cache_key_t *) key; + cairo_ft_cache_entry_t *entry; + + entry = malloc (sizeof (cairo_ft_cache_entry_t)); + if (entry == NULL) + return CAIRO_STATUS_NO_MEMORY; + + entry->key.pattern = FcPatternDuplicate (k->pattern); + if (!entry->key.pattern) { + free (entry); + return CAIRO_STATUS_NO_MEMORY; + } + + entry->val = _create_from_library_and_pattern (ftcache->lib, entry->key.pattern); + entry->key.base.memory = 1; + + *return_entry = entry; + + return CAIRO_STATUS_SUCCESS; } -FcPattern * -cairo_ft_font_pattern (cairo_font_t *abstract_font) +static void +_ft_font_cache_destroy_entry (void *cache, + void *entry) +{ + cairo_ft_cache_entry_t *e = (cairo_ft_cache_entry_t *) entry; + FcPatternDestroy (e->key.pattern); + _destroy_font_val (e->val); + free (e); +} + +static void +_ft_font_cache_destroy_cache (void *cache) { - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + ft_cache_t *fc = (ft_cache_t *) cache; + FT_Done_FreeType (fc->lib); + free (fc); +} - if (font == NULL) - return NULL; +const struct cairo_cache_backend _ft_font_cache_backend = { + _ft_font_cache_hash, + _ft_font_cache_keys_equal, + _ft_font_cache_create_entry, + _ft_font_cache_destroy_entry, + _ft_font_cache_destroy_cache +}; - return font->pattern; + +static ft_cache_t *_global_ft_cache = NULL; + +static void +_lock_global_ft_cache (void) +{ + /* FIXME: Perform locking here. */ +} + +static void +_unlock_global_ft_cache (void) +{ + /* FIXME: Perform locking here. */ +} + +static cairo_cache_t * +_get_global_ft_cache (void) +{ + if (_global_ft_cache == NULL) + { + _global_ft_cache = malloc (sizeof(ft_cache_t)); + if (!_global_ft_cache) + goto FAIL; + + if (_cairo_cache_init (&_global_ft_cache->base, + &_ft_font_cache_backend, + CAIRO_FT_CACHE_NUM_FONTS_DEFAULT)) + goto FAIL; + + if (FT_Init_FreeType (&_global_ft_cache->lib)) + goto FAIL; + } + return &_global_ft_cache->base; + + FAIL: + if (_global_ft_cache) + free (_global_ft_cache); + _global_ft_cache = NULL; + return NULL; } /* implement the backend interface */ -static cairo_font_t * +const struct cairo_font_backend cairo_ft_font_backend; + +static cairo_unscaled_font_t * _cairo_ft_font_create (const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { - cairo_ft_font_t *ft_font = NULL; - cairo_font_t *font = NULL; - FcPattern * pat = NULL; + cairo_status_t status; + cairo_ft_font_t *font = NULL; int fcslant; int fcweight; - FT_Library ft_library; - FT_Error error; + cairo_cache_t *cache; + cairo_ft_cache_entry_t *entry; + cairo_ft_cache_key_t key; - pat = FcPatternCreate (); - if (pat == NULL) - return NULL; + key.pattern = FcPatternCreate (); + if (key.pattern == NULL) + goto FAIL; + + font = malloc (sizeof (cairo_ft_font_t)); + if (font == NULL) + goto FREE_PATTERN; switch (weight) { @@ -167,52 +345,44 @@ _cairo_ft_font_create (const char *family, break; } - FcPatternAddString (pat, FC_FAMILY, family); - FcPatternAddInteger (pat, FC_SLANT, fcslant); - FcPatternAddInteger (pat, FC_WEIGHT, fcweight); + FcPatternAddString (key.pattern, FC_FAMILY, family); + FcPatternAddInteger (key.pattern, FC_SLANT, fcslant); + FcPatternAddInteger (key.pattern, FC_WEIGHT, fcweight); - error = FT_Init_FreeType (&ft_library); - if (error) { - FcPatternDestroy (pat); - return NULL; - } + if (_cairo_unscaled_font_init (&font->base, &cairo_ft_font_backend)) + goto FREE_PATTERN; - font = cairo_ft_font_create (ft_library, pat); - if (font == NULL) - return NULL; + _lock_global_ft_cache (); + cache = _get_global_ft_cache (); + if (cache == NULL) { + _unlock_global_ft_cache (); + goto FREE_PATTERN; + } - ft_font = (cairo_ft_font_t *) font; + status = _cairo_cache_lookup (cache, &key, (void **) &entry); + _unlock_global_ft_cache (); - ft_font->owns_ft_library = 1; + if (status) + goto FREE_PATTERN; - FT_Set_Char_Size (ft_font->face, - DOUBLE_TO_26_6 (1.0), - DOUBLE_TO_26_6 (1.0), - 0, 0); - - FcPatternDestroy (pat); - return font; -} + font->pattern = FcPatternDuplicate (entry->key.pattern); + if (font->pattern == NULL) + goto FREE_PATTERN; -static cairo_font_t * -_cairo_ft_font_copy (void *abstract_font) -{ - cairo_ft_font_t * font_new = NULL; - cairo_ft_font_t * font = abstract_font; - - if (font->base.backend != &cairo_ft_font_backend) - return NULL; + font->val = entry->val; + _reference_font_val (font->val); + + return &font->base; - font_new = (cairo_ft_font_t *) cairo_ft_font_create_for_ft_face (font->face); - if (font_new == NULL) - return NULL; + FREE_PATTERN: + FcPatternDestroy (key.pattern); - if (font_new != NULL && font->pattern != NULL) - font_new->pattern = FcPatternDuplicate (font->pattern); + FAIL: + return NULL; - return (cairo_font_t *) font_new; } + static void _cairo_ft_font_destroy (void *abstract_font) { @@ -220,15 +390,11 @@ _cairo_ft_font_destroy (void *abstract_font) if (font == NULL) return; - - if (font->face != NULL && font->owns_face) - FT_Done_Face (font->face); if (font->pattern != NULL) FcPatternDestroy (font->pattern); - if (font->ft_library && font->owns_ft_library) - FT_Done_FreeType (font->ft_library); + _destroy_font_val (font->val); free (font); } @@ -268,7 +434,7 @@ _utf8_to_ucs4 (char const *utf8, } static void -_install_font_matrix(cairo_matrix_t *matrix, FT_Face face) +_install_font_scale (cairo_font_scale_t *sc, FT_Face face) { cairo_matrix_t normalized; double scale_x, scale_y; @@ -276,16 +442,20 @@ _install_font_matrix(cairo_matrix_t *matrix, FT_Face face) FT_Matrix mat; /* The font matrix has x and y "scale" components which we extract and - * use as pixel scale values. These influence the way freetype chooses - * hints, as well as selecting different bitmaps in hand-rendered - * fonts. We also copy the normalized matrix to freetype's - * transformation. + * use as character scale values. These influence the way freetype + * chooses hints, as well as selecting different bitmaps in + * hand-rendered fonts. We also copy the normalized matrix to + * freetype's transformation. */ - _cairo_matrix_compute_scale_factors (matrix, &scale_x, &scale_y); - - cairo_matrix_copy (&normalized, matrix); + cairo_matrix_set_affine (&normalized, + sc->matrix[0][0], + sc->matrix[0][1], + sc->matrix[1][0], + sc->matrix[1][1], + 0, 0); + _cairo_matrix_compute_scale_factors (&normalized, &scale_x, &scale_y); cairo_matrix_scale (&normalized, 1.0 / scale_x, 1.0 / scale_y); cairo_matrix_get_affine (&normalized, &xx /* 00 */ , &yx /* 01 */, @@ -298,21 +468,28 @@ _install_font_matrix(cairo_matrix_t *matrix, FT_Face face) mat.yy = DOUBLE_TO_16_16(yy); FT_Set_Transform(face, &mat, NULL); - FT_Set_Char_Size(face, - DOUBLE_TO_26_6(scale_x), - DOUBLE_TO_26_6(scale_y), - 0, 0); + + FT_Set_Pixel_Sizes(face, + (FT_UInt) scale_x, + (FT_UInt) scale_y); } -static int -_utf8_to_glyphs (cairo_ft_font_t *font, - const unsigned char *utf8, - double x0, - double y0, - cairo_glyph_t **glyphs, - size_t *nglyphs) +static cairo_status_t +_cairo_ft_font_text_to_glyphs (void *abstract_font, + cairo_font_scale_t *sc, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *nglyphs) { - FT_Face face = font->face; + cairo_ft_font_t *font = abstract_font; + FT_Face face = font->val->face; + cairo_glyph_cache_key_t key; + cairo_image_glyph_cache_entry_t *val; + cairo_cache_t *cache; + + key.unscaled = &font->base; + key.scale = *sc; + double x = 0., y = 0.; size_t i; FT_ULong *ucs4 = NULL; @@ -320,56 +497,70 @@ _utf8_to_glyphs (cairo_ft_font_t *font, _utf8_to_ucs4 (utf8, &ucs4, nglyphs); if (ucs4 == NULL) - return 0; + return CAIRO_STATUS_NO_MEMORY; *glyphs = (cairo_glyph_t *) malloc ((*nglyphs) * (sizeof (cairo_glyph_t))); if (*glyphs == NULL) { free (ucs4); - return 0; + return CAIRO_STATUS_NO_MEMORY; } - _install_font_matrix (&font->base.matrix, face); + _cairo_lock_global_image_glyph_cache (); + cache = _cairo_get_global_image_glyph_cache (); + if (cache == NULL) { + _cairo_unlock_global_image_glyph_cache (); + return CAIRO_STATUS_NO_MEMORY; + } for (i = 0; i < *nglyphs; i++) { (*glyphs)[i].index = FT_Get_Char_Index (face, ucs4[i]); - (*glyphs)[i].x = x0 + x; - (*glyphs)[i].y = y0 + y; - - FT_Load_Glyph (face, (*glyphs)[i].index, FT_LOAD_DEFAULT); + (*glyphs)[i].x = x; + (*glyphs)[i].y = y; - x += DOUBLE_FROM_26_6 (face->glyph->advance.x); - y -= DOUBLE_FROM_26_6 (face->glyph->advance.y); + val = NULL; + key.index = (*glyphs)[i].index; + + if (_cairo_cache_lookup (cache, &key, (void **) &val) + != CAIRO_STATUS_SUCCESS || val == NULL) + continue; + + x += val->extents.x_advance; + y -= val->extents.y_advance; } + _cairo_unlock_global_image_glyph_cache (); free (ucs4); - return 1; + return CAIRO_STATUS_SUCCESS; } + static cairo_status_t _cairo_ft_font_font_extents (void *abstract_font, + cairo_font_scale_t *sc, cairo_font_extents_t *extents) { cairo_ft_font_t *font = abstract_font; - FT_Face face = font->face; - double scale_x, scale_y; + FT_Face face = font->val->face; + FT_Size_Metrics *metrics = &face->size->metrics; - double upm = face->units_per_EM; + _install_font_scale (sc, face); - _cairo_matrix_compute_scale_factors (&font->base.matrix, &scale_x, &scale_y); + extents->ascent = DOUBLE_FROM_26_6(metrics->ascender); + extents->descent = DOUBLE_FROM_26_6(metrics->descender); + extents->height = DOUBLE_FROM_26_6(metrics->height); + extents->max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance); - extents->ascent = face->ascender / upm * scale_y; - extents->descent = face->descender / upm * scale_y; - extents->height = face->height / upm * scale_y; - extents->max_x_advance = face->max_advance_width / upm * scale_x; - extents->max_y_advance = face->max_advance_height / upm * scale_y; + /* FIXME: this doesn't do vertical layout atm. */ + extents->max_y_advance = 0.0; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_ft_font_glyph_extents (void *abstract_font, + cairo_font_scale_t *sc, cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents) @@ -379,10 +570,10 @@ _cairo_ft_font_glyph_extents (void *abstract_font, cairo_point_double_t origin; cairo_point_double_t glyph_min, glyph_max; cairo_point_double_t total_min, total_max; - FT_Error error; - FT_Face face = font->face; - FT_GlyphSlot glyph = face->glyph; - FT_Glyph_Metrics *metrics = &glyph->metrics; + + cairo_image_glyph_cache_entry_t *img = NULL; + cairo_cache_t *cache; + cairo_glyph_cache_key_t key; if (num_glyphs == 0) { @@ -399,24 +590,33 @@ _cairo_ft_font_glyph_extents (void *abstract_font, origin.x = glyphs[0].x; origin.y = glyphs[0].y; - _install_font_matrix (&font->base.matrix, face); + _cairo_lock_global_image_glyph_cache (); + cache = _cairo_get_global_image_glyph_cache (); + if (cache == NULL) { + _cairo_unlock_global_image_glyph_cache (); + return CAIRO_STATUS_NO_MEMORY; + } + + key.unscaled = &font->base; + key.scale = *sc; for (i = 0; i < num_glyphs; i++) { - error = FT_Load_Glyph (face, glyphs[i].index, FT_LOAD_DEFAULT); - /* XXX: What to do in this error case? */ - if (error) + img = NULL; + key.index = glyphs[i].index; + if (_cairo_cache_lookup (cache, &key, (void **) &img) + != CAIRO_STATUS_SUCCESS || img == NULL) continue; - + /* XXX: Need to add code here to check the font's FcPattern for FC_VERTICAL_LAYOUT and if set get vertBearingX/Y instead. This will require that cairo_ft_font_create_for_ft_face accept an FcPattern. */ - glyph_min.x = glyphs[i].x + DOUBLE_FROM_26_6 (metrics->horiBearingX); - glyph_min.y = glyphs[i].y - DOUBLE_FROM_26_6 (metrics->horiBearingY); - glyph_max.x = glyph_min.x + DOUBLE_FROM_26_6 (metrics->width); - glyph_max.y = glyph_min.y + DOUBLE_FROM_26_6 (metrics->height); + glyph_min.x = glyphs[i].x + img->extents.x_bearing; + glyph_min.y = glyphs[i].y - img->extents.y_bearing; + glyph_max.x = glyph_min.x + img->extents.width; + glyph_max.y = glyph_min.y + img->extents.height; if (i==0) { total_min = glyph_min; @@ -433,12 +633,13 @@ _cairo_ft_font_glyph_extents (void *abstract_font, total_max.y = glyph_max.y; } } + _cairo_unlock_global_image_glyph_cache (); extents->x_bearing = total_min.x - origin.x; extents->y_bearing = total_min.y - origin.y; extents->width = total_max.x - total_min.x; extents->height = total_max.y - total_min.y; - extents->x_advance = glyphs[i-1].x + DOUBLE_FROM_26_6 (metrics->horiAdvance) - origin.x; + extents->x_advance = glyphs[i-1].x + (img == NULL ? 0 : img->extents.x_advance) - origin.x; extents->y_advance = glyphs[i-1].y + 0 - origin.y; return CAIRO_STATUS_SUCCESS; @@ -446,34 +647,16 @@ _cairo_ft_font_glyph_extents (void *abstract_font, static cairo_status_t -_cairo_ft_font_text_extents (void *abstract_font, - const unsigned char *utf8, - cairo_text_extents_t *extents) -{ - cairo_ft_font_t *font = abstract_font; - cairo_glyph_t *glyphs; - size_t nglyphs; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (_utf8_to_glyphs (font, utf8, 0, 0, &glyphs, &nglyphs)) - { - status = _cairo_ft_font_glyph_extents (font, glyphs, nglyphs, - extents); - free (glyphs); - } - return status; -} - -static cairo_status_t -_cairo_ft_font_glyph_bbox (void *abstract_font, - cairo_surface_t *surface, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) +_cairo_ft_font_glyph_bbox (void *abstract_font, + cairo_font_scale_t *sc, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) { + cairo_image_glyph_cache_entry_t *img; + cairo_cache_t *cache; + cairo_glyph_cache_key_t key; cairo_ft_font_t *font = abstract_font; - cairo_surface_t *mask = NULL; - cairo_glyph_size_t size; cairo_fixed_t x1, y1, x2, y2; int i; @@ -481,22 +664,33 @@ _cairo_ft_font_glyph_bbox (void *abstract_font, bbox->p1.x = bbox->p1.y = CAIRO_MAXSHORT << 16; bbox->p2.x = bbox->p2.y = CAIRO_MINSHORT << 16; - if (font == NULL - || surface == NULL - || glyphs == NULL) + _cairo_lock_global_image_glyph_cache (); + cache = _cairo_get_global_image_glyph_cache(); + + if (cache == NULL + || font == NULL + || glyphs == NULL) { + _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_NO_MEMORY; + } + + key.unscaled = &font->base; + key.scale = *sc; for (i = 0; i < num_glyphs; i++) { - mask = _cairo_font_lookup_glyph (&font->base, surface, - &glyphs[i], &size); - if (mask == NULL) + + img = NULL; + key.index = glyphs[i].index; + + if (_cairo_cache_lookup (cache, &key, (void **) &img) + != CAIRO_STATUS_SUCCESS || img == NULL) continue; - x1 = _cairo_fixed_from_double (glyphs[i].x + size.x); - y1 = _cairo_fixed_from_double (glyphs[i].y - size.y); - x2 = x1 + _cairo_fixed_from_double (size.width); - y2 = y1 + _cairo_fixed_from_double (size.height); + x1 = _cairo_fixed_from_double (glyphs[i].x + img->size.x); + y1 = _cairo_fixed_from_double (glyphs[i].y - img->size.y); + x2 = x1 + _cairo_fixed_from_double (img->size.width); + y2 = y1 + _cairo_fixed_from_double (img->size.height); if (x1 < bbox->p1.x) bbox->p1.x = x1; @@ -509,117 +703,82 @@ _cairo_ft_font_glyph_bbox (void *abstract_font, if (y2 > bbox->p2.y) bbox->p2.y = y2; - - if (mask) - cairo_surface_destroy (mask); } + _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_ft_font_text_bbox (void *abstract_font, - cairo_surface_t *surface, - double x0, - double y0, - const unsigned char *utf8, - cairo_box_t *bbox) -{ - cairo_ft_font_t *font = abstract_font; - cairo_glyph_t *glyphs; - size_t num_glyphs; - - if (_utf8_to_glyphs (font, utf8, x0, y0, &glyphs, &num_glyphs)) - { - cairo_status_t res; - res = _cairo_ft_font_glyph_bbox (font, surface, - glyphs, num_glyphs, bbox); - free (glyphs); - return res; - } - else - return CAIRO_STATUS_NO_MEMORY; -} static cairo_status_t -_cairo_ft_font_show_glyphs (void *abstract_font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - const cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_ft_font_show_glyphs (void *abstract_font, + cairo_font_scale_t *sc, + cairo_operator_t operator, + cairo_surface_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + int num_glyphs) { + cairo_image_glyph_cache_entry_t *img; + cairo_cache_t *cache; + cairo_glyph_cache_key_t key; cairo_ft_font_t *font = abstract_font; cairo_status_t status; - cairo_surface_t *mask = NULL; - cairo_glyph_size_t size; double x, y; int i; - if (font == NULL + _cairo_lock_global_image_glyph_cache (); + cache = _cairo_get_global_image_glyph_cache(); + + if (cache == NULL + || font == NULL || source == NULL || surface == NULL - || glyphs == NULL) + || glyphs == NULL) { + _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_NO_MEMORY; + } + + key.unscaled = &font->base; + key.scale = *sc; for (i = 0; i < num_glyphs; i++) { - mask = _cairo_font_lookup_glyph (&font->base, surface, - &glyphs[i], &size); - if (mask == NULL) + img = NULL; + key.index = glyphs[i].index; + + if (_cairo_cache_lookup (cache, &key, (void **) &img) + != CAIRO_STATUS_SUCCESS + || img == NULL + || img->image == NULL) continue; x = glyphs[i].x; y = glyphs[i].y; - status = _cairo_surface_composite (operator, source, mask, surface, - source_x + x + size.x, - source_y + y - size.y, + status = _cairo_surface_composite (operator, source, + &(img->image->base), + surface, + source_x + x + img->size.x, + source_y + y - img->size.y, 0, 0, - x + size.x, - y - size.y, - (double) size.width, - (double) size.height); - - cairo_surface_destroy (mask); + x + img->size.x, + y - img->size.y, + (double) img->size.width, + (double) img->size.height); - if (status) + if (status) { + _cairo_unlock_global_image_glyph_cache (); return status; + } } + _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_ft_font_show_text (void *abstract_font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - double x0, - double y0, - const unsigned char *utf8) -{ - cairo_ft_font_t *font = abstract_font; - cairo_glyph_t *glyphs; - size_t num_glyphs; - - if (_utf8_to_glyphs (font, utf8, x0, y0, &glyphs, &num_glyphs)) - { - cairo_status_t res; - res = _cairo_ft_font_show_glyphs (font, operator, - source, surface, - source_x, source_y, - glyphs, num_glyphs); - free (glyphs); - return res; - } - else - return CAIRO_STATUS_NO_MEMORY; -} static int _move_to (FT_Vector *to, void *closure) @@ -699,10 +858,11 @@ _cubic_to (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *closur } static cairo_status_t -_cairo_ft_font_glyph_path (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) +_cairo_ft_font_glyph_path (void *abstract_font, + cairo_font_scale_t *sc, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path) { int i; cairo_ft_font_t *font = abstract_font; @@ -717,8 +877,9 @@ _cairo_ft_font_glyph_path (void *abstract_font, 0, /* delta */ }; - glyph = font->face->glyph; - _install_font_matrix (&font->base.matrix, font->face); + glyph = font->val->face->glyph; + + _install_font_scale (sc, font->val->face); for (i = 0; i < num_glyphs; i++) { @@ -727,7 +888,7 @@ _cairo_ft_font_glyph_path (void *abstract_font, 0, DOUBLE_TO_16_16 (-1.0), }; - error = FT_Load_Glyph (font->face, glyphs[i].index, FT_LOAD_DEFAULT); + error = FT_Load_Glyph (font->val->face, glyphs[i].index, FT_LOAD_DEFAULT); /* XXX: What to do in this error case? */ if (error) continue; @@ -747,130 +908,218 @@ _cairo_ft_font_glyph_path (void *abstract_font, return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_ft_font_text_path (void *abstract_font, - double x, - double y, - const unsigned char *utf8, - cairo_path_t *path) -{ - cairo_ft_font_t *font = abstract_font; - cairo_glyph_t *glyphs; - size_t nglyphs; - - if (_utf8_to_glyphs (font, utf8, x, y, &glyphs, &nglyphs)) - { - cairo_status_t res; - res = _cairo_ft_font_glyph_path (font, glyphs, nglyphs, path); - free (glyphs); - return res; - } - else - return CAIRO_STATUS_NO_MEMORY; -} - -cairo_font_t * -cairo_ft_font_create_for_ft_face (FT_Face face) -{ - cairo_ft_font_t *f = NULL; - - f = malloc (sizeof (cairo_ft_font_t)); - if (f == NULL) - return NULL; - memset (f, 0, sizeof (cairo_ft_font_t)); - - _cairo_font_init (&f->base, - &cairo_ft_font_backend); - - f->ft_library = NULL; - f->owns_ft_library = 0; - - f->face = face; - f->owns_face = 0; - return (cairo_font_t *) f; -} - -static cairo_surface_t * -_cairo_ft_font_create_glyph (void *abstract_font, - const cairo_glyph_t *glyph, - cairo_glyph_size_t *return_size) +static cairo_status_t +_cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) { - cairo_ft_font_t *font = abstract_font; - cairo_image_surface_t *image; + cairo_ft_font_t *font = (cairo_ft_font_t *)val->key.unscaled; FT_GlyphSlot glyphslot; unsigned int width, height, stride; FT_Outline *outline; FT_BBox cbox; FT_Bitmap bitmap; - - glyphslot = font->face->glyph; - _install_font_matrix (&font->base.matrix, font->face); + FT_Glyph_Metrics *metrics; - FT_Load_Glyph (font->face, glyph->index, FT_LOAD_DEFAULT); + glyphslot = font->val->face->glyph; + metrics = &glyphslot->metrics; - outline = &glyphslot->outline; + _install_font_scale (&val->key.scale, font->val->face); + + if (FT_Load_Glyph (font->val->face, val->key.index, FT_LOAD_DEFAULT) != 0) + return CAIRO_STATUS_NO_MEMORY; + + val->extents.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX); + val->extents.y_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingY); + val->extents.width = DOUBLE_FROM_26_6 (metrics->width); + val->extents.height = DOUBLE_FROM_26_6 (metrics->height); + val->extents.x_advance = DOUBLE_FROM_26_6 (font->val->face->glyph->advance.x); + val->extents.y_advance = DOUBLE_FROM_26_6 (font->val->face->glyph->advance.y); + outline = &glyphslot->outline; + FT_Outline_Get_CBox (outline, &cbox); cbox.xMin &= -64; cbox.yMin &= -64; cbox.xMax = (cbox.xMax + 63) & -64; cbox.yMax = (cbox.yMax + 63) & -64; - + width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6); height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6); stride = (width + 3) & -4; - bitmap.pixel_mode = ft_pixel_mode_grays; - bitmap.num_grays = 256; - bitmap.width = width; - bitmap.rows = height; - bitmap.pitch = stride; - if (width * height == 0) - return NULL; - - bitmap.buffer = malloc (stride * height); - if (bitmap.buffer == NULL) - return NULL; - - memset (bitmap.buffer, 0x0, stride * height); + val->image = NULL; + else + { - FT_Outline_Translate (outline, -cbox.xMin, -cbox.yMin); - FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap); - - image = (cairo_image_surface_t *) + bitmap.pixel_mode = ft_pixel_mode_grays; + bitmap.num_grays = 256; + bitmap.width = width; + bitmap.rows = height; + bitmap.pitch = stride; + bitmap.buffer = calloc (1, stride * height); + + if (bitmap.buffer == NULL) { + return CAIRO_STATUS_NO_MEMORY; + }; + + FT_Outline_Translate (outline, -cbox.xMin, -cbox.yMin); + + if (FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap) != 0) { + free (bitmap.buffer); + return CAIRO_STATUS_NO_MEMORY; + } + + val->image = (cairo_image_surface_t *) cairo_image_surface_create_for_data ((char *) bitmap.buffer, CAIRO_FORMAT_A8, width, height, stride); - if (image == NULL) { - free (bitmap.buffer); - return NULL; + if (val->image == NULL) { + free (bitmap.buffer); + return CAIRO_STATUS_NO_MEMORY; + } + + _cairo_image_surface_assume_ownership_of_data (val->image); } - - _cairo_image_surface_assume_ownership_of_data (image); - - return_size->width = (unsigned short) width; - return_size->height = (unsigned short) height; - return_size->x = (short) (cbox.xMin >> 6); - return_size->y = (short) (cbox.yMax >> 6); - return &image->base; + val->size.width = (unsigned short) width; + val->size.height = (unsigned short) height; + val->size.x = (short) (cbox.xMin >> 6); + val->size.y = (short) (cbox.yMax >> 6); + + return CAIRO_STATUS_SUCCESS; } const struct cairo_font_backend cairo_ft_font_backend = { _cairo_ft_font_create, - _cairo_ft_font_copy, _cairo_ft_font_destroy, _cairo_ft_font_font_extents, - _cairo_ft_font_text_extents, + _cairo_ft_font_text_to_glyphs, _cairo_ft_font_glyph_extents, - _cairo_ft_font_text_bbox, _cairo_ft_font_glyph_bbox, - _cairo_ft_font_show_text, _cairo_ft_font_show_glyphs, - _cairo_ft_font_text_path, _cairo_ft_font_glyph_path, _cairo_ft_font_create_glyph }; + + +/* implement the platform-specific interface */ + +cairo_font_t * +cairo_ft_font_create (FT_Library ft_library, FcPattern *pattern) +{ + cairo_font_scale_t scale; + cairo_font_t *scaled; + cairo_ft_font_t *f = NULL; + ft_font_val_t *v = NULL; + FcPattern *dup; + + scale.matrix[0][0] = 1.; + scale.matrix[0][1] = 0.; + scale.matrix[1][0] = 0.; + scale.matrix[1][1] = 1.; + + scaled = malloc (sizeof (cairo_font_t)); + if (scaled == NULL) + goto FAIL; + + dup = FcPatternDuplicate(pattern); + if (dup == NULL) + goto FREE_SCALED; + + v = _create_from_library_and_pattern (ft_library, pattern); + if (v == NULL) + goto FREE_PATTERN; + + f = malloc (sizeof(cairo_ft_font_t)); + if (f == NULL) + goto FREE_VAL; + + if (_cairo_unscaled_font_init (&f->base, &cairo_ft_font_backend)) + goto FREE_VAL; + + f->pattern = dup; + f->val = v; + + _cairo_font_init (scaled, &scale, &f->base); + + return scaled; + + FREE_VAL: + _destroy_font_val (v); + + FREE_PATTERN: + FcPatternDestroy (dup); + + FREE_SCALED: + free (scaled); + + FAIL: + return NULL; +} + +cairo_font_t * +cairo_ft_font_create_for_ft_face (FT_Face face) +{ + cairo_font_scale_t scale; + cairo_font_t *scaled; + cairo_ft_font_t *f = NULL; + ft_font_val_t *v = NULL; + + scale.matrix[0][0] = 1.; + scale.matrix[0][1] = 0.; + scale.matrix[1][0] = 0.; + scale.matrix[1][1] = 1.; + + scaled = malloc (sizeof (cairo_font_t)); + if (scaled == NULL) + goto FAIL; + + v = _create_from_face (face, 0); + if (v == NULL) + goto FREE_SCALED; + + f = malloc (sizeof(cairo_ft_font_t)); + if (f == NULL) + goto FREE_VAL; + + _cairo_unscaled_font_init (&f->base, &cairo_ft_font_backend); + f->pattern = NULL; + f->val = v; + + _cairo_font_init (scaled, &scale, &f->base); + + return scaled; + + FREE_VAL: + _destroy_font_val (v); + + FREE_SCALED: + free (scaled); + + FAIL: + return NULL; +} + +FT_Face +cairo_ft_font_face (cairo_font_t *abstract_font) +{ + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font->unscaled; + + if (font == NULL || font->val == NULL) + return NULL; + + return font->val->face; +} + +FcPattern * +cairo_ft_font_pattern (cairo_font_t *abstract_font) +{ + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font->unscaled; + + if (font == NULL) + return NULL; + + return font->pattern; +} diff --git a/src/cairo_glitz_surface.c b/src/cairo_glitz_surface.c index 8d18280b..d351108d 100644 --- a/src/cairo_glitz_surface.c +++ b/src/cairo_glitz_surface.c @@ -900,7 +900,8 @@ static const struct cairo_surface_backend cairo_glitz_surface_backend = { _cairo_glitz_surface_copy_page, _cairo_glitz_surface_show_page, _cairo_glitz_surface_set_clip_region, - _cairo_glitz_surface_create_pattern + _cairo_glitz_surface_create_pattern, + NULL /* show_glyphs */ }; cairo_surface_t * diff --git a/src/cairo_gstate.c b/src/cairo_gstate.c index c89d6376..fabb53ef 100644 --- a/src/cairo_gstate.c +++ b/src/cairo_gstate.c @@ -77,9 +77,9 @@ _cairo_gstate_init (cairo_gstate_t *gstate) gstate->num_dashes = 0; gstate->dash_offset = 0.0; - gstate->font = _cairo_font_create (CAIRO_FONT_FAMILY_DEFAULT, - CAIRO_FONT_SLANT_DEFAULT, - CAIRO_FONT_WEIGHT_DEFAULT); + gstate->font = _cairo_unscaled_font_create (CAIRO_FONT_FAMILY_DEFAULT, + CAIRO_FONT_SLANT_DEFAULT, + CAIRO_FONT_WEIGHT_DEFAULT); gstate->surface = NULL; @@ -119,11 +119,8 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) } if (other->font) { - gstate->font = _cairo_font_copy (other->font); - if (!gstate->font) { - status = CAIRO_STATUS_NO_MEMORY; - goto CLEANUP_DASHES; - } + gstate->font = other->font; + _cairo_unscaled_font_reference (gstate->font); } if (other->clip.region) @@ -149,9 +146,10 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) CLEANUP_PATH: _cairo_path_fini (&gstate->path); + CLEANUP_FONT: - cairo_font_destroy (gstate->font); - CLEANUP_DASHES: + _cairo_unscaled_font_destroy (gstate->font); + free (gstate->dash); gstate->dash = NULL; @@ -161,7 +159,7 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) void _cairo_gstate_fini (cairo_gstate_t *gstate) { - cairo_font_destroy (gstate->font); + _cairo_unscaled_font_destroy (gstate->font); if (gstate->surface) cairo_surface_destroy (gstate->surface); @@ -177,6 +175,8 @@ _cairo_gstate_fini (cairo_gstate_t *gstate) cairo_pattern_destroy (gstate->pattern); + _cairo_matrix_fini (&gstate->font_matrix); + _cairo_matrix_fini (&gstate->ctm); _cairo_matrix_fini (&gstate->ctm_inverse); @@ -627,6 +627,8 @@ _cairo_gstate_default_matrix (cairo_gstate_t *gstate) if (scale == 0) scale = 1; + cairo_matrix_set_identity (&gstate->font_matrix); + cairo_matrix_set_identity (&gstate->ctm); cairo_matrix_scale (&gstate->ctm, scale, scale); cairo_matrix_copy (&gstate->ctm_inverse, &gstate->ctm); @@ -1676,15 +1678,6 @@ extract_transformed_rectangle(cairo_matrix_t *mat, double a, b, c, d, tx, ty; cairo_status_t st; - /* XXX: Something in the rectangle-based clipping support is - * broken. See cairo_snippets/xxx_clip_rectangle which - * demonstrates no clipping at all. - * - * For now, I'm am disabling this optimization completely until it - * can be fixed. - */ - return 0; - st = cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty); if (!(st == CAIRO_STATUS_SUCCESS && b == 0. && c == 0.)) return 0; @@ -1992,16 +1985,28 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, return CAIRO_STATUS_SUCCESS; } + cairo_status_t _cairo_gstate_select_font (cairo_gstate_t *gstate, const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { - if (gstate->font != NULL) - cairo_font_destroy (gstate->font); + cairo_unscaled_font_t *tmp; + + tmp = _cairo_unscaled_font_create (family, slant, weight); - gstate->font = _cairo_font_create (family, slant, weight); + if (tmp == NULL) + return CAIRO_STATUS_NO_MEMORY; + + if (gstate->font != tmp) + { + if (gstate->font != NULL) + _cairo_unscaled_font_destroy (gstate->font); + + cairo_matrix_set_identity (&gstate->font_matrix); + gstate->font = tmp; + } return CAIRO_STATUS_SUCCESS; } @@ -2010,212 +2015,294 @@ cairo_status_t _cairo_gstate_scale_font (cairo_gstate_t *gstate, double scale) { - return _cairo_font_scale (gstate->font, scale); + return cairo_matrix_scale (&gstate->font_matrix, scale, scale); } cairo_status_t _cairo_gstate_transform_font (cairo_gstate_t *gstate, cairo_matrix_t *matrix) { - return _cairo_font_transform (gstate->font, matrix); + cairo_matrix_t tmp; + double a, b, c, d, tx, ty; + cairo_matrix_get_affine (matrix, &a, &b, &c, &d, &tx, &ty); + cairo_matrix_set_affine (&tmp, a, b, c, d, 0, 0); + return cairo_matrix_multiply (&gstate->font_matrix, &gstate->font_matrix, &tmp); } + cairo_status_t _cairo_gstate_current_font (cairo_gstate_t *gstate, cairo_font_t **font) { - *font = gstate->font; + cairo_font_scale_t scale; + cairo_font_t *scaled; + double dummy; + + scaled = malloc (sizeof (cairo_font_t)); + if (scaled == NULL) + return CAIRO_STATUS_NO_MEMORY; + + cairo_matrix_get_affine (&gstate->font_matrix, + &scale.matrix[0][0], + &scale.matrix[0][1], + &scale.matrix[1][0], + &scale.matrix[1][1], + &dummy, &dummy); + + _cairo_font_init (scaled, &scale, gstate->font); + _cairo_unscaled_font_reference (gstate->font); + + *font = scaled; return CAIRO_STATUS_SUCCESS; } -cairo_status_t -_cairo_gstate_current_font_extents (cairo_gstate_t *gstate, - cairo_font_extents_t *extents) +void +_cairo_gstate_set_font_transform (cairo_gstate_t *gstate, + cairo_matrix_t *matrix) { - cairo_int_status_t status; - cairo_matrix_t saved_font_matrix; - - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); - - status = _cairo_font_font_extents (gstate->font, extents); - - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + cairo_matrix_copy (&gstate->font_matrix, matrix); +} - return status; +void +_cairo_gstate_current_font_transform (cairo_gstate_t *gstate, + cairo_matrix_t *matrix) +{ + cairo_matrix_copy (matrix, &gstate->font_matrix); } +/* + * Like everything else in this file, fonts involve Too Many Coordinate Spaces; + * it is easy to get confused about what's going on. + * + * The user's view + * --------------- + * + * Users ask for things in user space. When cairo starts, a user space unit + * is about 1/96 inch, which is similar to (but importantly different from) + * the normal "point" units most users think in terms of. When a user + * selects a font, its scale is set to "one user unit". The user can then + * independently scale the user coordinate system *or* the font matrix, in + * order to adjust the rendered size of the font. + * + * If the user asks for a permanent reference to "a font", they are given a + * handle to a structure holding a scale matrix and an unscaled font. This + * effectively decouples the font from further changes to user space. Even + * if the user then "sets" the current cairo_t font to the handle they were + * passed, further changes to the cairo_t CTM will not affect externally + * held references to the font. + * + * + * The font's view + * --------------- + * + * Fonts are designed and stored (in say .ttf files) in "font space", which + * describes an "EM Square" (a design tile) and has some abstract number + * such as 1000, 1024, or 2048 units per "EM". This is basically an + * uninteresting space for us, but we need to remember that it exists. + * + * Font resources (from libraries or operating systems) render themselves + * to a particular device. Since they do not want to make most programmers + * worry about the font design space, the scaling API is simplified to + * involve just telling the font the required pixel size of the EM square + * (that is, in device space). + * + * + * Cairo's gstate view + * ------------------- + * + * In addition to the CTM and CTM inverse, we keep a matrix in the gstate + * called the "font matrix" which describes the user's most recent + * font-scaling or font-transforming request. This is kept in terms of an + * abstract scale factor, composed with the CTM and used to set the font's + * pixel size. So if the user asks to "scale the font by 12", the matrix + * is: + * + * [ 12.0, 0.0, 0.0, 12.0, 0.0, 0.0 ] + * + * It is an affine matrix, like all cairo matrices, but its tx and ty + * components are always set to zero; we don't permit "nudging" fonts + * around. + * + * In order to perform any action on a font, we must build an object + * called a cairo_font_scale_t; this contains the central 2x2 matrix + * resulting from "font matrix * CTM". + * + * We pass this to the font when making requests of it, which causes it to + * reply for a particular [user request, device] combination, under the CTM + * (to accomodate the "zoom in" == "bigger fonts" issue above). + * + * The other terms in our communication with the font are therefore in + * device space. When we ask it to perform text->glyph conversion, it will + * produce a glyph string in device space. Glyph vectors we pass to it for + * measuring or rendering should be in device space. The metrics which we + * get back from the font will be in device space. The contents of the + * global glyph image cache will be in device space. + * + * + * Cairo's public view + * ------------------- + * + * Since the values entering and leaving via public API calls are in user + * space, the gstate functions typically need to multiply argumens by the + * CTM (for user-input glyph vectors), and return values by the CTM inverse + * (for font responses such as metrics or glyph vectors). + * + */ -cairo_status_t -_cairo_gstate_set_font (cairo_gstate_t *gstate, - cairo_font_t *font) +static void +_build_font_scale (cairo_gstate_t *gstate, + cairo_font_scale_t *sc) { - if (gstate->font != NULL) - cairo_font_destroy (gstate->font); - gstate->font = font; - cairo_font_reference (gstate->font); - return CAIRO_STATUS_SUCCESS; + cairo_matrix_t tmp; + double dummy; + cairo_matrix_multiply (&tmp, &gstate->font_matrix, &gstate->ctm); + cairo_matrix_get_affine (&tmp, + &sc->matrix[0][0], + &sc->matrix[0][1], + &sc->matrix[1][0], + &sc->matrix[1][1], + &dummy, &dummy); } cairo_status_t -_cairo_gstate_text_extents (cairo_gstate_t *gstate, - const unsigned char *utf8, - cairo_text_extents_t *extents) +_cairo_gstate_current_font_extents (cairo_gstate_t *gstate, + cairo_font_extents_t *extents) { - cairo_matrix_t saved_font_matrix; - cairo_status_t status; - double scale_x, scale_y; - - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - _cairo_matrix_compute_scale_factors (&gstate->ctm, &scale_x, &scale_y); - cairo_matrix_scale (&gstate->font->matrix, scale_x, scale_y); - - status = _cairo_font_text_extents (gstate->font, - utf8, extents); - - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + cairo_int_status_t status; + cairo_font_scale_t sc; + double dummy = 0.0; - extents->x_bearing /= scale_x; - extents->y_bearing /= scale_y; - extents->width /= scale_x; - extents->height /= scale_y; - extents->x_advance /= scale_x; - extents->y_advance /= scale_y; + _build_font_scale (gstate, &sc); - return status; -} + status = _cairo_unscaled_font_font_extents (gstate->font, &sc, extents); -cairo_status_t -_cairo_gstate_glyph_extents (cairo_gstate_t *gstate, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - cairo_status_t status; - cairo_matrix_t saved_font_matrix; - double scale_x, scale_y; + /* The font responded in device space; convert to user space. */ - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - _cairo_matrix_compute_scale_factors (&gstate->ctm, &scale_x, &scale_y); - cairo_matrix_scale (&gstate->font->matrix, scale_x, scale_y); + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &dummy, + &extents->ascent); - status = _cairo_font_glyph_extents (gstate->font, - glyphs, num_glyphs, - extents); + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &dummy, + &extents->descent); - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &dummy, + &extents->height); - extents->x_bearing /= scale_x; - extents->y_bearing /= scale_y; - extents->width /= scale_x; - extents->height /= scale_y; - extents->x_advance /= scale_x; - extents->y_advance /= scale_y; + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &extents->max_x_advance, + &extents->max_y_advance); return status; } cairo_status_t -_cairo_gstate_show_text (cairo_gstate_t *gstate, - const unsigned char *utf8) +_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *nglyphs) { cairo_status_t status; - cairo_point_t point; - double x, y; - cairo_matrix_t saved_font_matrix; - cairo_pattern_t pattern; - cairo_box_t bbox; + cairo_font_scale_t sc; + + cairo_point_t point; + double dev_x, dev_y; + int i; + + _build_font_scale (gstate, &sc); status = _cairo_path_current_point (&gstate->path, &point); if (status == CAIRO_STATUS_NO_CURRENT_POINT) { - x = 0; - y = 0; - cairo_matrix_transform_point (&gstate->ctm, &x, &y); + dev_x = 0.0; + dev_y = 0.0; } else { - x = _cairo_fixed_to_double (point.x); - y = _cairo_fixed_to_double (point.y); + dev_x = _cairo_fixed_to_double (point.x); + dev_y = _cairo_fixed_to_double (point.y); } - - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); - _cairo_pattern_init_copy (&pattern, gstate->pattern); - - status = _cairo_font_text_bbox (gstate->font, gstate->surface, - x, y, utf8, &bbox); - if (status) - return status; - - status = _cairo_gstate_create_pattern (gstate, &pattern, &bbox); - if (status) + status = _cairo_unscaled_font_text_to_glyphs (gstate->font, + &sc, utf8, glyphs, nglyphs); + + if (status || !glyphs || !nglyphs || !(*glyphs) || !(nglyphs)) return status; - - if (gstate->clip.surface) - { - cairo_surface_t *intermediate; - cairo_color_t empty_color; - _cairo_color_init (&empty_color); - _cairo_color_set_alpha (&empty_color, .0); - intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, - CAIRO_FORMAT_A8, - gstate->clip.width, - gstate->clip.height, - &empty_color); + /* The font responded in device space, starting from (0,0); add any + current point offset in device space, and convert to user space. */ - status = _cairo_font_show_text (gstate->font, - CAIRO_OPERATOR_ADD, pattern.source, - intermediate, - gstate->clip.x - pattern.source_offset.x, - gstate->clip.y - pattern.source_offset.y, - x - gstate->clip.x, - y - gstate->clip.y, utf8); + for (i = 0; i < *nglyphs; ++i) { + (*glyphs)[i].x += dev_x; + (*glyphs)[i].y += dev_y; + cairo_matrix_transform_point (&gstate->ctm_inverse, + &((*glyphs)[i].x), + &((*glyphs)[i].y)); + } + + return CAIRO_STATUS_SUCCESS; +} - if (status) - goto BAIL; - - status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - gstate->clip.surface, - NULL, - intermediate, - 0, 0, - 0, 0, - 0, 0, - gstate->clip.width, - gstate->clip.height); +cairo_status_t +_cairo_gstate_set_font (cairo_gstate_t *gstate, + cairo_font_t *font) +{ + if (gstate->font != NULL) + _cairo_unscaled_font_destroy (gstate->font); + gstate->font = font->unscaled; + _cairo_unscaled_font_reference (gstate->font); + cairo_matrix_set_affine (&gstate->font_matrix, + font->scale.matrix[0][0], + font->scale.matrix[0][1], + font->scale.matrix[1][0], + font->scale.matrix[1][1], + 0, 0); + return CAIRO_STATUS_SUCCESS; +} - if (status) - goto BAIL; +cairo_status_t +_cairo_gstate_glyph_extents (cairo_gstate_t *gstate, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + cairo_status_t status; + cairo_glyph_t *transformed_glyphs; + cairo_font_scale_t sc; + int i; - status = _cairo_surface_composite (gstate->operator, - pattern.source, - intermediate, - gstate->surface, - 0, 0, - 0, 0, - gstate->clip.x, - gstate->clip.y, - gstate->clip.width, - gstate->clip.height); + _build_font_scale (gstate, &sc); - BAIL: - cairo_surface_destroy (intermediate); - - } - else + transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); + if (transformed_glyphs == NULL) + return CAIRO_STATUS_NO_MEMORY; + + for (i = 0; i < num_glyphs; ++i) { - status = _cairo_font_show_text (gstate->font, - gstate->operator, pattern.source, - gstate->surface, - -pattern.source_offset.x, - -pattern.source_offset.y, - x, y, utf8); + transformed_glyphs[i] = glyphs[i]; + cairo_matrix_transform_point (&gstate->ctm, + &transformed_glyphs[i].x, + &transformed_glyphs[i].y); } - - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - _cairo_pattern_fini (&pattern); + status = _cairo_unscaled_font_glyph_extents (gstate->font, &sc, + transformed_glyphs, num_glyphs, + extents); + + /* The font responded in device space; convert to user space. */ + + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &extents->x_bearing, + &extents->y_bearing); + + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &extents->width, + &extents->height); + + cairo_matrix_transform_distance (&gstate->ctm_inverse, + &extents->x_advance, + &extents->y_advance); + + free (transformed_glyphs); return status; } @@ -2226,12 +2313,14 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, int num_glyphs) { cairo_status_t status; - cairo_matrix_t saved_font_matrix; int i; cairo_glyph_t *transformed_glyphs = NULL; + cairo_font_scale_t sc; cairo_pattern_t pattern; cairo_box_t bbox; + _build_font_scale (gstate, &sc); + transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); if (transformed_glyphs == NULL) return CAIRO_STATUS_NO_MEMORY; @@ -2240,16 +2329,14 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, { transformed_glyphs[i] = glyphs[i]; cairo_matrix_transform_point (&gstate->ctm, - &(transformed_glyphs[i].x), - &(transformed_glyphs[i].y)); + &transformed_glyphs[i].x, + &transformed_glyphs[i].y); } - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); - _cairo_pattern_init_copy (&pattern, gstate->pattern); - status = _cairo_font_glyph_bbox (gstate->font, gstate->surface, - transformed_glyphs, num_glyphs, &bbox); + status = _cairo_unscaled_font_glyph_bbox (gstate->font, &sc, + transformed_glyphs, num_glyphs, + &bbox); if (status) return status; @@ -2277,12 +2364,13 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, transformed_glyphs[i].y -= gstate->clip.y; } - status = _cairo_font_show_glyphs (gstate->font, - CAIRO_OPERATOR_ADD, - pattern.source, intermediate, - gstate->clip.x - pattern.source_offset.x, - gstate->clip.y - pattern.source_offset.y, - transformed_glyphs, num_glyphs); + status = _cairo_unscaled_font_show_glyphs (gstate->font, + &sc, + CAIRO_OPERATOR_ADD, + pattern.source, intermediate, + gstate->clip.x - pattern.source_offset.x, + gstate->clip.y - pattern.source_offset.y, + transformed_glyphs, num_glyphs); if (status) goto BAIL; @@ -2317,16 +2405,15 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, } else { - status = _cairo_font_show_glyphs (gstate->font, - gstate->operator, pattern.source, - gstate->surface, - -pattern.source_offset.x, - -pattern.source_offset.y, - transformed_glyphs, num_glyphs); + status = _cairo_unscaled_font_show_glyphs (gstate->font, + &sc, + gstate->operator, pattern.source, + gstate->surface, + -pattern.source_offset.x, + -pattern.source_offset.y, + transformed_glyphs, num_glyphs); } - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - _cairo_pattern_fini (&pattern); free (transformed_glyphs); @@ -2334,40 +2421,6 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, return status; } - -cairo_status_t -_cairo_gstate_text_path (cairo_gstate_t *gstate, - const unsigned char *utf8) -{ - cairo_status_t status; - cairo_matrix_t saved_font_matrix; - cairo_point_t point; - double x, y; - - status = _cairo_path_current_point (&gstate->path, &point); - if (status == CAIRO_STATUS_NO_CURRENT_POINT) { - x = 0; - y = 0; - cairo_matrix_transform_point (&gstate->ctm, &x, &y); - } else { - x = _cairo_fixed_to_double (point.x); - y = _cairo_fixed_to_double (point.y); - } - - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); - - status = _cairo_font_text_path (gstate->font, - x, y, - utf8, - &gstate->path); - - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - - return status; -} - - cairo_status_t _cairo_gstate_glyph_path (cairo_gstate_t *gstate, cairo_glyph_t *glyphs, @@ -2376,7 +2429,9 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, cairo_status_t status; int i; cairo_glyph_t *transformed_glyphs = NULL; - cairo_matrix_t saved_font_matrix; + cairo_font_scale_t sc; + + _build_font_scale (gstate, &sc); transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); if (transformed_glyphs == NULL) @@ -2390,14 +2445,9 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, &(transformed_glyphs[i].y)); } - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); - - status = _cairo_font_glyph_path (gstate->font, - transformed_glyphs, num_glyphs, - &gstate->path); - - cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + status = _cairo_unscaled_font_glyph_path (gstate->font, &sc, + transformed_glyphs, num_glyphs, + &gstate->path); free (transformed_glyphs); return status; diff --git a/src/cairo_image_surface.c b/src/cairo_image_surface.c index e2ee69c5..9e6abb2a 100644 --- a/src/cairo_image_surface.c +++ b/src/cairo_image_surface.c @@ -525,5 +525,6 @@ static const cairo_surface_backend_t cairo_image_surface_backend = { _cairo_image_surface_copy_page, _cairo_image_surface_show_page, _cairo_image_abstract_surface_set_clip_region, - _cairo_image_abstract_surface_create_pattern + _cairo_image_abstract_surface_create_pattern, + NULL /* show_glyphs */ }; diff --git a/src/cairo_png_surface.c b/src/cairo_png_surface.c index 1d1fa63e..cfd68c87 100644 --- a/src/cairo_png_surface.c +++ b/src/cairo_png_surface.c @@ -382,5 +382,6 @@ static const cairo_surface_backend_t cairo_png_surface_backend = { _cairo_png_surface_copy_page, _cairo_png_surface_show_page, _cairo_png_surface_set_clip_region, - _cairo_png_surface_create_pattern + _cairo_png_surface_create_pattern, + NULL /* show_glyphs */ }; diff --git a/src/cairo_ps_surface.c b/src/cairo_ps_surface.c index 4296d0d4..9b3096b0 100644 --- a/src/cairo_ps_surface.c +++ b/src/cairo_ps_surface.c @@ -436,5 +436,6 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = { _cairo_ps_surface_copy_page, _cairo_ps_surface_show_page, _cairo_ps_surface_set_clip_region, - _cairo_ps_surface_create_pattern + _cairo_ps_surface_create_pattern, + NULL /* show_glyphs */ }; diff --git a/src/cairo_xcb_surface.c b/src/cairo_xcb_surface.c index 36aa1573..bc37d204 100644 --- a/src/cairo_xcb_surface.c +++ b/src/cairo_xcb_surface.c @@ -745,7 +745,8 @@ static const struct cairo_surface_backend cairo_xcb_surface_backend = { _cairo_xcb_surface_copy_page, _cairo_xcb_surface_show_page, _cairo_xcb_surface_set_clip_region, - _cairo_xcb_surface_create_pattern + _cairo_xcb_surface_create_pattern, + NULL /* show_glyphs */ }; static void diff --git a/src/cairo_xlib_surface.c b/src/cairo_xlib_surface.c index 8513ae4e..fca7f33e 100644 --- a/src/cairo_xlib_surface.c +++ b/src/cairo_xlib_surface.c @@ -631,6 +631,8 @@ _cairo_xlib_surface_set_clip_region (void *abstract_surface, xr.height = surf->height; XUnionRectWithRegion (&xr, xregion, xregion); rects = malloc(sizeof(XRectangle)); + if (rects == NULL) + return CAIRO_STATUS_NO_MEMORY; rects[0] = xr; m = 1; @@ -641,6 +643,8 @@ _cairo_xlib_surface_set_clip_region (void *abstract_surface, if (n == 0) return CAIRO_STATUS_SUCCESS; rects = malloc(sizeof(XRectangle) * n); + if (rects == NULL) + return CAIRO_STATUS_NO_MEMORY; box = pixman_region_rects (region); xregion = XCreateRegion(); @@ -661,7 +665,7 @@ _cairo_xlib_surface_set_clip_region (void *abstract_surface, XSetClipRectangles(surf->dpy, surf->gc, 0, 0, rects, m, Unsorted); free(rects); if (surf->picture) - XRenderSetPictureClipRegion (surf->dpy, surf->picture, xregion); + XRenderSetPictureClipRegion (surf->dpy, surf->picture, xregion); XDestroyRegion(xregion); XSetGraphicsExposures(surf->dpy, surf->gc, gc_values.graphics_exposures); return CAIRO_STATUS_SUCCESS; @@ -675,6 +679,17 @@ _cairo_xlib_surface_create_pattern (void *abstract_surface, return CAIRO_INT_STATUS_UNSUPPORTED; } +static cairo_status_t +_cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + cairo_surface_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + int num_glyphs); + static const struct cairo_surface_backend cairo_xlib_surface_backend = { _cairo_xlib_surface_create_similar, _cairo_xlib_surface_destroy, @@ -690,7 +705,8 @@ static const struct cairo_surface_backend cairo_xlib_surface_backend = { _cairo_xlib_surface_copy_page, _cairo_xlib_surface_show_page, _cairo_xlib_surface_set_clip_region, - _cairo_xlib_surface_create_pattern + _cairo_xlib_surface_create_pattern, + _cairo_xlib_surface_show_glyphs }; cairo_surface_t * @@ -761,3 +777,512 @@ cairo_xlib_surface_create (Display *dpy, return (cairo_surface_t *) surface; } DEPRECATE (cairo_surface_create_for_drawable, cairo_xlib_surface_create); + + +/* RENDER glyphset cache code */ + +typedef struct glyphset_cache { + cairo_cache_t base; + struct glyphset_cache *next; + Display *display; + XRenderPictFormat *a8_pict_format; + GlyphSet glyphset; + Glyph counter; +} glyphset_cache_t; + +typedef struct { + cairo_glyph_cache_key_t key; + Glyph glyph; + XGlyphInfo info; +} glyphset_cache_entry_t; + +static Glyph +_next_xlib_glyph (glyphset_cache_t *cache) +{ + return ++(cache->counter); +} + + +static cairo_status_t +_xlib_glyphset_cache_create_entry (void *cache, + void *key, + void **return_entry) +{ + glyphset_cache_t *g = (glyphset_cache_t *) cache; + cairo_glyph_cache_key_t *k = (cairo_glyph_cache_key_t *)key; + glyphset_cache_entry_t *v; + + cairo_status_t status; + + cairo_cache_t *im_cache; + cairo_image_glyph_cache_entry_t *im; + + v = malloc (sizeof (glyphset_cache_entry_t)); + _cairo_lock_global_image_glyph_cache (); + im_cache = _cairo_get_global_image_glyph_cache (); + + if (g == NULL || v == NULL ||g == NULL || im_cache == NULL) { + _cairo_unlock_global_image_glyph_cache (); + return CAIRO_STATUS_NO_MEMORY; + } + + status = _cairo_cache_lookup (im_cache, key, (void **) (&im)); + if (status != CAIRO_STATUS_SUCCESS || im == NULL) { + _cairo_unlock_global_image_glyph_cache (); + return CAIRO_STATUS_NO_MEMORY; + } + + v->key = *k; + _cairo_unscaled_font_reference (v->key.unscaled); + + v->glyph = _next_xlib_glyph (g); + + v->info.width = im->image ? im->image->stride : im->size.width; + v->info.height = im->size.height; + v->info.x = - im->extents.x_bearing; + v->info.y = im->extents.y_bearing; + v->info.xOff = 0; + v->info.yOff = 0; + + XRenderAddGlyphs (g->display, g->glyphset, + &(v->glyph), &(v->info), 1, + im->image ? im->image->data : NULL, + im->image ? v->info.height * v->info.width : 0); + + v->key.base.memory = im->image ? im->image->width * im->image->stride : 0; + *return_entry = v; + _cairo_unlock_global_image_glyph_cache (); + return CAIRO_STATUS_SUCCESS; +} + +static void +_xlib_glyphset_cache_destroy_cache (void *cache) +{ + /* FIXME: will we ever release glyphset caches? */ +} + +static void +_xlib_glyphset_cache_destroy_entry (void *cache, void *entry) +{ + glyphset_cache_t *g; + glyphset_cache_entry_t *v; + + g = (glyphset_cache_t *) cache; + v = (glyphset_cache_entry_t *) entry; + + _cairo_unscaled_font_destroy (v->key.unscaled); + XRenderFreeGlyphs (g->display, g->glyphset, &(v->glyph), 1); + free (v); +} + +const cairo_cache_backend_t _xlib_glyphset_cache_backend = { + _cairo_glyph_cache_hash, + _cairo_glyph_cache_keys_equal, + _xlib_glyphset_cache_create_entry, + _xlib_glyphset_cache_destroy_entry, + _xlib_glyphset_cache_destroy_cache +}; + + +static glyphset_cache_t * +_xlib_glyphset_caches = NULL; + +static void +_lock_xlib_glyphset_caches (void) +{ + /* FIXME: implement locking */ +} + +static void +_unlock_xlib_glyphset_caches (void) +{ + /* FIXME: implement locking */ +} + +static glyphset_cache_t * +_get_glyphset_cache (Display *d) +{ + /* + * There should usually only be one, or a very small number, of + * displays. So we just do a linear scan. + */ + + glyphset_cache_t *g; + + for (g = _xlib_glyphset_caches; g != NULL; g = g->next) { + if (g->display == d) + return g; + } + + g = malloc (sizeof (glyphset_cache_t)); + if (g == NULL) + goto ERR; + + g->counter = 0; + g->display = d; + g->a8_pict_format = XRenderFindStandardFormat (d, PictStandardA8); + if (g->a8_pict_format == NULL) + goto ERR; + + if (_cairo_cache_init (&g->base, + &_xlib_glyphset_cache_backend, + CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT)) + goto FREE_GLYPHSET_CACHE; + + g->glyphset = XRenderCreateGlyphSet (d, g->a8_pict_format); + g->next = _xlib_glyphset_caches; + _xlib_glyphset_caches = g; + return g; + + FREE_GLYPHSET_CACHE: + free (g); + + ERR: + return NULL; +} + +#define N_STACK_BUF 1024 + +static cairo_status_t +_cairo_xlib_surface_show_glyphs32 (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + glyphset_cache_t *g, + cairo_glyph_cache_key_t *key, + cairo_xlib_surface_t *src, + cairo_xlib_surface_t *self, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + glyphset_cache_entry_t **entries, + int num_glyphs) +{ + XGlyphElt32 *elts = NULL; + XGlyphElt32 stack_elts [N_STACK_BUF]; + + unsigned int *chars = NULL; + unsigned int stack_chars [N_STACK_BUF]; + + int i; + int lastX = 0, lastY = 0; + + /* Acquire arrays of suitable sizes. */ + if (num_glyphs < N_STACK_BUF) { + elts = stack_elts; + chars = stack_chars; + + } else { + elts = malloc (num_glyphs * sizeof (XGlyphElt32)); + if (elts == NULL) + goto FAIL; + + chars = malloc (num_glyphs * sizeof (unsigned int)); + if (chars == NULL) + goto FREE_ELTS; + + } + + for (i = 0; i < num_glyphs; ++i) { + chars[i] = entries[i]->glyph; + elts[i].chars = &(chars[i]); + elts[i].nchars = 1; + elts[i].glyphset = g->glyphset; + elts[i].xOff = glyphs[i].x - lastX; + elts[i].yOff = glyphs[i].y - lastY; + lastX = glyphs[i].x; + lastY = glyphs[i].y; + } + + XRenderCompositeText32 (self->dpy, + _render_operator (operator), + src->picture, + self->picture, + g->a8_pict_format, + source_x, source_y, + 0, 0, + elts, num_glyphs); + + if (num_glyphs >= N_STACK_BUF) { + free (chars); + free (elts); + } + + return CAIRO_STATUS_SUCCESS; + + FREE_ELTS: + if (num_glyphs >= N_STACK_BUF) + free (elts); + + FAIL: + return CAIRO_STATUS_NO_MEMORY; +} + + +static cairo_status_t +_cairo_xlib_surface_show_glyphs16 (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + glyphset_cache_t *g, + cairo_glyph_cache_key_t *key, + cairo_xlib_surface_t *src, + cairo_xlib_surface_t *self, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + glyphset_cache_entry_t **entries, + int num_glyphs) +{ + XGlyphElt16 *elts = NULL; + XGlyphElt16 stack_elts [N_STACK_BUF]; + + unsigned short *chars = NULL; + unsigned short stack_chars [N_STACK_BUF]; + + int i; + int lastX = 0, lastY = 0; + + /* Acquire arrays of suitable sizes. */ + if (num_glyphs < N_STACK_BUF) { + elts = stack_elts; + chars = stack_chars; + + } else { + elts = malloc (num_glyphs * sizeof (XGlyphElt16)); + if (elts == NULL) + goto FAIL; + + chars = malloc (num_glyphs * sizeof (unsigned short)); + if (chars == NULL) + goto FREE_ELTS; + + } + + for (i = 0; i < num_glyphs; ++i) { + chars[i] = entries[i]->glyph; + elts[i].chars = &(chars[i]); + elts[i].nchars = 1; + elts[i].glyphset = g->glyphset; + elts[i].xOff = glyphs[i].x - lastX; + elts[i].yOff = glyphs[i].y - lastY; + lastX = glyphs[i].x; + lastY = glyphs[i].y; + } + + XRenderCompositeText16 (self->dpy, + _render_operator (operator), + src->picture, + self->picture, + g->a8_pict_format, + source_x, source_y, + 0, 0, + elts, num_glyphs); + + if (num_glyphs >= N_STACK_BUF) { + free (chars); + free (elts); + } + + return CAIRO_STATUS_SUCCESS; + + FREE_ELTS: + if (num_glyphs >= N_STACK_BUF) + free (elts); + + FAIL: + return CAIRO_STATUS_NO_MEMORY; +} + +static cairo_status_t +_cairo_xlib_surface_show_glyphs8 (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + glyphset_cache_t *g, + cairo_glyph_cache_key_t *key, + cairo_xlib_surface_t *src, + cairo_xlib_surface_t *self, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + glyphset_cache_entry_t **entries, + int num_glyphs) +{ + XGlyphElt8 *elts = NULL; + XGlyphElt8 stack_elts [N_STACK_BUF]; + + char *chars = NULL; + char stack_chars [N_STACK_BUF]; + + int i; + int lastX = 0, lastY = 0; + + /* Acquire arrays of suitable sizes. */ + if (num_glyphs < N_STACK_BUF) { + elts = stack_elts; + chars = stack_chars; + + } else { + elts = malloc (num_glyphs * sizeof (XGlyphElt8)); + if (elts == NULL) + goto FAIL; + + chars = malloc (num_glyphs * sizeof (char)); + if (chars == NULL) + goto FREE_ELTS; + + } + + for (i = 0; i < num_glyphs; ++i) { + chars[i] = entries[i]->glyph; + elts[i].chars = &(chars[i]); + elts[i].nchars = 1; + elts[i].glyphset = g->glyphset; + elts[i].xOff = glyphs[i].x - lastX; + elts[i].yOff = glyphs[i].y - lastY; + lastX = glyphs[i].x; + lastY = glyphs[i].y; + } + + XRenderCompositeText8 (self->dpy, + _render_operator (operator), + src->picture, + self->picture, + g->a8_pict_format, + source_x, source_y, + 0, 0, + elts, num_glyphs); + + if (num_glyphs >= N_STACK_BUF) { + free (chars); + free (elts); + } + + return CAIRO_STATUS_SUCCESS; + + FREE_ELTS: + if (num_glyphs >= N_STACK_BUF) + free (elts); + + FAIL: + return CAIRO_STATUS_NO_MEMORY; +} + + +static cairo_status_t +_cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + cairo_surface_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + unsigned int elt_size; + cairo_xlib_surface_t *self = (cairo_xlib_surface_t *) surface; + cairo_image_surface_t *tmp = NULL; + cairo_xlib_surface_t *src = NULL; + glyphset_cache_t *g; + cairo_status_t status; + cairo_glyph_cache_key_t key; + glyphset_cache_entry_t **entries; + glyphset_cache_entry_t *stack_entries [N_STACK_BUF]; + int i; + + /* Acquire an entry array of suitable size. */ + if (num_glyphs < N_STACK_BUF) { + entries = stack_entries; + + } else { + entries = malloc (num_glyphs * sizeof (glyphset_cache_entry_t *)); + if (entries == NULL) + goto FAIL; + } + + /* prep the source surface. */ + if (source->backend == surface->backend) { + src = (cairo_xlib_surface_t *) source; + + } else { + tmp = _cairo_surface_get_image (source); + if (tmp == NULL) + goto FREE_ENTRIES; + + src = (cairo_xlib_surface_t *) + _cairo_surface_create_similar_scratch (surface, self->format, 1, + tmp->width, tmp->height); + + if (src == NULL) + goto FREE_TMP; + + if (_cairo_surface_set_image (&(src->base), tmp) != CAIRO_STATUS_SUCCESS) + goto FREE_SRC; + } + + _lock_xlib_glyphset_caches (); + g = _get_glyphset_cache (self->dpy); + if (g == NULL) + goto UNLOCK; + + /* Work out the index size to use. */ + elt_size = 8; + key.scale = *scale; + key.unscaled = font; + + for (i = 0; i < num_glyphs; ++i) { + key.index = glyphs[i].index; + status = _cairo_cache_lookup (&g->base, &key, (void **) (&entries[i])); + if (status != CAIRO_STATUS_SUCCESS || entries[i] == NULL) + goto UNLOCK; + + if (elt_size == 8 && entries[i]->glyph > 0xff) + elt_size = 16; + if (elt_size == 16 && entries[i]->glyph > 0xffff) { + elt_size = 32; + break; + } + } + + /* Call the appropriate sub-function. */ + + if (elt_size == 8) + status = _cairo_xlib_surface_show_glyphs8 (font, scale, operator, g, &key, src, self, + source_x, source_y, + glyphs, entries, num_glyphs); + else if (elt_size == 16) + status = _cairo_xlib_surface_show_glyphs16 (font, scale, operator, g, &key, src, self, + source_x, source_y, + glyphs, entries, num_glyphs); + else + status = _cairo_xlib_surface_show_glyphs32 (font, scale, operator, g, &key, src, self, + source_x, source_y, + glyphs, entries, num_glyphs); + + _unlock_xlib_glyphset_caches (); + + if (tmp != NULL) { + cairo_surface_destroy (&(src->base)); + cairo_surface_destroy (&(tmp->base)); + } + + if (num_glyphs >= N_STACK_BUF) + free (entries); + + return status; + + UNLOCK: + _unlock_xlib_glyphset_caches (); + + FREE_SRC: + cairo_surface_destroy (&(src->base)); + + FREE_TMP: + cairo_surface_destroy (&(tmp->base)); + + FREE_ENTRIES: + if (num_glyphs >= N_STACK_BUF) + free (entries); + + FAIL: + return CAIRO_STATUS_NO_MEMORY; +} diff --git a/src/cairoint.h b/src/cairoint.h index 6242a3fc..728a5d17 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -49,7 +49,6 @@ #include "config.h" #endif -#include <assert.h> #include <stdlib.h> #include <string.h> #include <math.h> @@ -253,52 +252,204 @@ typedef struct cairo_pen { } cairo_pen_t; typedef struct cairo_color cairo_color_t; +typedef struct cairo_image_surface cairo_image_surface_t; + +/* cairo_cache.c structures and functions */ + +typedef struct cairo_cache_backend { + + unsigned long (*hash) (void *cache, + void *key); + + int (*keys_equal) (void *cache, + void *k1, + void *k2); + + cairo_status_t (*create_entry) (void *cache, + void *key, + void **entry_return); + + void (*destroy_entry) (void *cache, + void *entry); + + void (*destroy_cache) (void *cache); + +} cairo_cache_backend_t; + + +/* + * The cairo_cache system makes the following assumptions about + * entries in its cache: + * + * - a pointer to an entry can be cast to a cairo_cache_entry_base_t. + * - a pointer to an entry can also be cast to the "key type". + * + * The practical effect of this is that your entries must be laid + * out this way: + * + * struct my_entry { + * cairo_cache_entry_base_t; + * my_key_value_1; + * my_key_value_2; + * ... + * my_value; + * }; + */ + +typedef struct { + unsigned long memory; + unsigned long hashcode; +} cairo_cache_entry_base_t; + +typedef struct { + unsigned long high_water_mark; + unsigned long size; + unsigned long rehash; +} cairo_cache_arrangement_t; + +#undef CAIRO_MEASURE_CACHE_PERFORMANCE + +typedef struct { + unsigned long refcount; + const cairo_cache_backend_t *backend; + cairo_cache_arrangement_t *arrangement; + cairo_cache_entry_base_t **entries; + + unsigned long max_memory; + unsigned long used_memory; + unsigned long live_entries; + +#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE + unsigned long hits; + unsigned long misses; + unsigned long probes; +#endif +} cairo_cache_t; + +extern cairo_status_t __internal_linkage +_cairo_cache_init (cairo_cache_t *cache, + const cairo_cache_backend_t *backend, + unsigned long max_memory); + +extern void __internal_linkage +_cairo_cache_reference (cairo_cache_t *cache); + +extern void __internal_linkage +_cairo_cache_destroy (cairo_cache_t *cache); + +extern cairo_status_t __internal_linkage +_cairo_cache_lookup (cairo_cache_t *cache, + void *key, + void **entry_return); + +extern unsigned long __internal_linkage +_cairo_hash_string (const char *c); + +#define CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT 0x100000 +#define CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT 0x100000 +#define CAIRO_FONT_CACHE_NUM_FONTS_DEFAULT 20 +#define CAIRO_FT_CACHE_NUM_FONTS_DEFAULT 20 + +typedef struct { + double matrix[2][2]; +} cairo_font_scale_t; + +struct cairo_font_backend; + +typedef struct { + int refcount; + const struct cairo_font_backend *backend; +} cairo_unscaled_font_t; + +/* + * A cairo_font contains a pointer to a cairo_sizeless_font_t and a scale + * matrix. These are the things the user holds references to. + */ + +struct cairo_font { + int refcount; + cairo_font_scale_t scale; + cairo_unscaled_font_t *unscaled; +}; + + +/* cairo_font.c is responsible for two global caches: + * + * - font entries: [[[base], name, weight, slant], cairo_unscaled_font_t ] + * - glyph entries: [[[base], cairo_font_t, index], image, size, extents ] + * + * Surfaces may build their own glyph caches if they have surface-specific + * glyph resources to maintain; those caches can feed off of the global + * caches if need be (eg. cairo_xlib_surface.c does this). + */ + +typedef struct { + cairo_cache_entry_base_t base; + cairo_unscaled_font_t *unscaled; + cairo_font_scale_t scale; + unsigned long index; +} cairo_glyph_cache_key_t; + +typedef struct { + cairo_glyph_cache_key_t key; + cairo_image_surface_t *image; + cairo_glyph_size_t size; + cairo_text_extents_t extents; +} cairo_image_glyph_cache_entry_t; + +extern void __internal_linkage +_cairo_lock_global_image_glyph_cache (void); + +extern void __internal_linkage +_cairo_unlock_global_image_glyph_cache (void); + +extern cairo_cache_t * __internal_linkage +_cairo_get_global_image_glyph_cache (void); + +/* Some glyph cache functions you can reuse. */ + +extern unsigned long __internal_linkage +_cairo_glyph_cache_hash (void *cache, void *key); + +extern int __internal_linkage +_cairo_glyph_cache_keys_equal (void *cache, + void *k1, + void *k2); + + +/* the font backend interface */ typedef struct cairo_font_backend { - cairo_font_t *(*create) (const char *family, + cairo_unscaled_font_t *(*create) (const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight); - cairo_font_t *(*copy) (void *font); - void (*destroy) (void *font); cairo_status_t (*font_extents) (void *font, + cairo_font_scale_t *scale, cairo_font_extents_t *extents); - cairo_status_t (*text_extents) (void *font, + cairo_status_t (*text_to_glyphs) (void *font, + cairo_font_scale_t *scale, const unsigned char *utf8, - cairo_text_extents_t *extents); - + cairo_glyph_t **glyphs, + int *num_glyphs); + cairo_status_t (*glyph_extents) (void *font, + cairo_font_scale_t *scale, cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents); - cairo_status_t (*text_bbox) (void *font, - cairo_surface_t *surface, - double x, - double y, - const unsigned char *utf8, - cairo_box_t *bbox); - cairo_status_t (*glyph_bbox) (void *font, - cairo_surface_t *surface, + cairo_font_scale_t *scale, const cairo_glyph_t *glyphs, int num_glyphs, cairo_box_t *bbox); - cairo_status_t (*show_text) (void *font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - double x, - double y, - const unsigned char *utf8); - cairo_status_t (*show_glyphs) (void *font, + cairo_font_scale_t *scale, cairo_operator_t operator, cairo_surface_t *source, cairo_surface_t *surface, @@ -307,25 +458,19 @@ typedef struct cairo_font_backend { const cairo_glyph_t *glyphs, int num_glyphs); - cairo_status_t (*text_path) (void *font, - double x, - double y, - const unsigned char *utf8, - cairo_path_t *path); - cairo_status_t (*glyph_path) (void *font, + cairo_font_scale_t *scale, cairo_glyph_t *glyphs, int num_glyphs, cairo_path_t *path); - cairo_surface_t *(*create_glyph) (void *font, - const cairo_glyph_t *glyph, - cairo_glyph_size_t *return_size); + + cairo_status_t (*create_glyph) (cairo_image_glyph_cache_entry_t *entry); + } cairo_font_backend_t; /* concrete font backends */ extern const struct cairo_font_backend cairo_ft_font_backend; -typedef struct cairo_image_surface cairo_image_surface_t; typedef struct cairo_surface_backend { cairo_surface_t * @@ -410,6 +555,23 @@ typedef struct cairo_surface_backend { (*create_pattern) (void *surface, cairo_pattern_t *pattern, cairo_box_t *extents); + + /* + * This is an optional entry to let the surface manage its own glyph + * resources. If null, the font will be asked to render against this + * surface, using image surfaces as glyphs. + */ + cairo_status_t + (*show_glyphs) (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + cairo_surface_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + int num_glyphs); + } cairo_surface_backend_t; struct cairo_matrix { @@ -547,40 +709,6 @@ typedef struct cairo_traps { /* XXX: Platform-specific. Other platforms may want a different default */ #define CAIRO_FONT_BACKEND_DEFAULT &cairo_ft_font_backend -#define CAIRO_FONT_CACHE_SIZE_DEFAULT 256 - -typedef struct { - unsigned long index; - double matrix[2][2]; - - unsigned int time; - - cairo_surface_t *surface; - cairo_glyph_size_t size; -} cairo_glyph_surface_t; - -typedef struct cairo_glyph_surface_node { - struct cairo_glyph_surface_node *next; - struct cairo_glyph_surface_node *prev; - - cairo_glyph_surface_t s; -} cairo_glyph_surface_node_t; - -typedef struct { - cairo_glyph_surface_node_t *first; - cairo_glyph_surface_node_t *last; - unsigned int n_nodes; - - unsigned int ref_count; - unsigned int cache_size; -} cairo_glyph_cache_t; - -struct cairo_font { - int refcount; - cairo_matrix_t matrix; - cairo_glyph_cache_t *glyph_cache; - const struct cairo_font_backend *backend; -}; #define CAIRO_GSTATE_OPERATOR_DEFAULT CAIRO_OPERATOR_OVER #define CAIRO_GSTATE_TOLERANCE_DEFAULT 0.1 @@ -618,7 +746,7 @@ typedef struct cairo_gstate { int num_dashes; double dash_offset; - cairo_font_t *font; + cairo_unscaled_font_t *font; cairo_surface_t *surface; @@ -628,6 +756,9 @@ typedef struct cairo_gstate { cairo_clip_rec_t clip; double pixels_per_inch; + + cairo_matrix_t font_matrix; + cairo_matrix_t ctm; cairo_matrix_t ctm_inverse; @@ -935,6 +1066,14 @@ extern cairo_status_t __internal_linkage _cairo_gstate_current_font (cairo_gstate_t *gstate, cairo_font_t **font); +extern void __internal_linkage +_cairo_gstate_set_font_transform (cairo_gstate_t *gstate, + cairo_matrix_t *matrix); + +extern void __internal_linkage +_cairo_gstate_current_font_transform (cairo_gstate_t *gstate, + cairo_matrix_t *matrix); + extern cairo_status_t __internal_linkage _cairo_gstate_current_font_extents (cairo_gstate_t *gstate, cairo_font_extents_t *extents); @@ -943,11 +1082,11 @@ extern cairo_status_t __internal_linkage _cairo_gstate_set_font (cairo_gstate_t *gstate, cairo_font_t *font); - extern cairo_status_t __internal_linkage -_cairo_gstate_text_extents (cairo_gstate_t *gstate, - const unsigned char *utf8, - cairo_text_extents_t *extents); +_cairo_gstate_text_to_glyphs (cairo_gstate_t *font, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs); extern cairo_status_t __internal_linkage _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, @@ -956,19 +1095,11 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, cairo_text_extents_t *extents); extern cairo_status_t __internal_linkage -_cairo_gstate_show_text (cairo_gstate_t *gstate, - const unsigned char *utf8); - -extern cairo_status_t __internal_linkage _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, cairo_glyph_t *glyphs, int num_glyphs); extern cairo_status_t __internal_linkage -_cairo_gstate_text_path (cairo_gstate_t *gstate, - const unsigned char *utf8); - -extern cairo_status_t __internal_linkage _cairo_gstate_glyph_path (cairo_gstate_t *gstate, cairo_glyph_t *glyphs, int num_glyphs); @@ -992,95 +1123,69 @@ _cairo_color_set_alpha (cairo_color_t *color, double alpha); /* cairo_font.c */ -extern cairo_font_t * __internal_linkage -_cairo_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight); - -extern cairo_status_t __internal_linkage -_cairo_font_init (cairo_font_t *font, - const struct cairo_font_backend *backend); - -extern cairo_font_t * __internal_linkage -_cairo_font_copy (cairo_font_t *font); - -extern cairo_status_t __internal_linkage -_cairo_font_scale (cairo_font_t *font, double scale); +extern cairo_unscaled_font_t * __internal_linkage +_cairo_unscaled_font_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight); -extern cairo_status_t __internal_linkage -_cairo_font_transform (cairo_font_t *font, cairo_matrix_t *matrix); +extern void __internal_linkage +_cairo_font_init (cairo_font_t *scaled, + cairo_font_scale_t *scale, + cairo_unscaled_font_t *unscaled); extern cairo_status_t __internal_linkage -_cairo_font_font_extents (cairo_font_t *font, - cairo_font_extents_t *extents); +_cairo_unscaled_font_init (cairo_unscaled_font_t *font, + const struct cairo_font_backend *backend); -extern cairo_status_t __internal_linkage -_cairo_font_text_extents (cairo_font_t *font, - const unsigned char *utf8, - cairo_text_extents_t *extents); +extern void __internal_linkage +_cairo_unscaled_font_reference (cairo_unscaled_font_t *font); -extern cairo_status_t __internal_linkage -_cairo_font_glyph_extents (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents); +extern void __internal_linkage +_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font); extern cairo_status_t __internal_linkage -_cairo_font_text_bbox (cairo_font_t *font, - cairo_surface_t *surface, - double x, - double y, - const unsigned char *utf8, - cairo_box_t *bbox); +_cairo_unscaled_font_font_extents (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_font_extents_t *extents); extern cairo_status_t __internal_linkage -_cairo_font_glyph_bbox (cairo_font_t *font, - cairo_surface_t *surface, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox); +_cairo_unscaled_font_text_to_glyphs (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs); extern cairo_status_t __internal_linkage -_cairo_font_show_text (cairo_font_t *font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - double x, - double y, - const unsigned char *utf8); - +_cairo_unscaled_font_glyph_extents (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); extern cairo_status_t __internal_linkage -_cairo_font_show_glyphs (cairo_font_t *font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - cairo_glyph_t *glyphs, - int num_glyphs); - +_cairo_unscaled_font_glyph_bbox (cairo_unscaled_font_t *font, + cairo_font_scale_t *size, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox); extern cairo_status_t __internal_linkage -_cairo_font_text_path (cairo_font_t *font, - double x, - double y, - const unsigned char *utf8, - cairo_path_t *path); +_cairo_unscaled_font_show_glyphs (cairo_unscaled_font_t *font, + cairo_font_scale_t *size, + cairo_operator_t operator, + cairo_surface_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + cairo_glyph_t *glyphs, + int num_glyphs); extern cairo_status_t __internal_linkage -_cairo_font_glyph_path (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path); - -extern cairo_surface_t *__internal_linkage -_cairo_font_lookup_glyph (cairo_font_t *font, - cairo_surface_t *surface, - const cairo_glyph_t *glyph, - cairo_glyph_size_t *return_size); +_cairo_unscaled_font_glyph_path (cairo_unscaled_font_t *font, + cairo_font_scale_t *size, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path); /* cairo_hull.c */ extern cairo_status_t |