diff --git a/ChangeLog b/ChangeLog index 0191739bb..a0cb430bc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,98 @@ +2005-08-31 Keith Packard + + Reviewed by: otaylor, cworth + * src/Makefile.am: + Split out scaled font code to cairo-scaled-font.c + + * src/cairo-cache-private.h: + * src/cairo-cache.c: (_cairo_cache_init), (_cairo_cache_fini), + (_cairo_cache_create), (_cairo_cache_destroy), + (_cairo_cache_preserve), (_cairo_cache_release), + (_cairo_cache_lookup), (_cairo_cache_remove_random), + (_cairo_cache_insert), (_cairo_cache_remove), + (_cairo_cache_foreach): + Replace cairo cache implementation (this code from cworth) + + * src/cairo-debug.c: (cairo_debug_reset_static_data): + * src/cairo-font.c: (_cairo_font_reset_static_data): + No more global glyph cache to clean up + + * src/cairo-ft-font.c: (_get_bitmap_surface), + (_render_glyph_outline), (_render_glyph_bitmap), + (_transform_glyph_bitmap), (_get_pattern_ft_options), + (_cairo_ft_scaled_font_create), (_cairo_ft_scaled_font_create_toy), + (_decompose_glyph_outline), (_cairo_ft_scaled_glyph_init), + (_cairo_ft_ucs4_to_index), (_cairo_ft_show_glyphs), + (_cairo_ft_font_face_scaled_font_create), + (_cairo_ft_font_face_create), + (cairo_ft_font_face_create_for_pattern), + (cairo_ft_font_face_create_for_ft_face): + Store glyphs in new per-scaled font caches which + hold user-space metrics and device space bounding boxes + + * src/cairo-gstate.c: (_cairo_gstate_text_to_glyphs), + (_cairo_gstate_show_glyphs_draw_func), (_cairo_gstate_show_glyphs): + Refactor glyph drawing APIs so that the surface API is + invoked directly from the gstate code. + + * src/cairo-path.c: (_cairo_path_fixed_create), + (_cairo_path_fixed_destroy): + Add path creation/destruction routines (to hold glyph paths) + + * src/cairo-scaled-font.c: (_cairo_scaled_glyph_keys_equal), + (_cairo_scaled_glyph_fini), (_cairo_scaled_glyph_destroy), + (_cairo_scaled_font_set_error), (cairo_scaled_font_status), + (_cairo_scaled_font_map_lock), (_cairo_scaled_font_map_unlock), + (_cairo_scaled_font_map_destroy), (_hash_bytes_fnv), + (_cairo_scaled_font_init_key), (_cairo_scaled_font_keys_equal), + (_cairo_scaled_font_init), (_cairo_scaled_font_set_metrics), + (_cairo_scaled_font_fini), (cairo_scaled_font_create), + (cairo_scaled_font_reference), (cairo_scaled_font_destroy), + (cairo_scaled_font_extents), (cairo_scaled_font_glyph_extents), + (_cairo_scaled_font_text_to_glyphs), + (_cairo_scaled_font_glyph_device_extents), + (_cairo_scaled_font_show_glyphs), (_scaled_glyph_path_move_to), + (_scaled_glyph_path_line_to), (_scaled_glyph_path_curve_to), + (_scaled_glyph_path_close_path), (_cairo_scaled_font_glyph_path), + (_cairo_scaled_glyph_set_metrics), + (_cairo_scaled_glyph_set_surface), (_cairo_scaled_glyph_set_path), + (_cairo_scaled_glyph_lookup): + New implementation of scaled fonts which uses per-scaled_font + caches for glyphs and keeps user-space metrics, device-space bboxes + along with glyph images and/or glyph paths. + + * src/cairo-xlib-surface.c: + (_cairo_xlib_surface_create_similar_with_format), + (_cairo_xlib_surface_create_similar), (_cairo_xlib_surface_finish), + (_cairo_xlib_surface_clone_similar), + (_cairo_xlib_surface_font_init), + (_cairo_xlib_surface_scaled_font_fini), + (_cairo_xlib_surface_scaled_glyph_fini), + (_cairo_xlib_surface_add_glyph), + (_cairo_xlib_surface_show_glyphs8), + (_cairo_xlib_surface_show_glyphs16), + (_cairo_xlib_surface_show_glyphs32), + (_cairo_xlib_surface_show_glyphs): + Adapt to new scaled font API changes. + + * src/cairoint.h: + New cache and scaled_font APIs + + * test/clip-operator-ref.png: + * test/operator-clear-ref.png: + * test/operator-source-ref.png: + * test/text-antialias-gray-ref.png: + * test/text-antialias-gray.c: + * test/text-antialias-none-ref.png: + * test/text-antialias-none.c: + * test/text-antialias-subpixel-ref.png: + * test/text-antialias-subpixel.c: + * test/unbounded-operator-ref.png: + Repond to bug fix in metrics computation for glyphs + where y values were rounded up instead of down + because of a sign difference between cairo and FreeType. + + 2005-08-31 Carl Worth * configure.in: Increment CAIRO_VERSION to 1.1.1 after making diff --git a/src/Makefile.am b/src/Makefile.am index 7a2cd6ba4..a2c344149 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -121,6 +121,7 @@ libcairo_la_SOURCES = \ cairo-pen.c \ cairo-polygon.c \ cairo-region.c \ + cairo-scaled-font.c \ cairo-slope.c \ cairo-spline.c \ cairo-surface.c \ diff --git a/src/cairo-cache-private.h b/src/cairo-cache-private.h new file mode 100644 index 000000000..a6eeba29c --- /dev/null +++ b/src/cairo-cache-private.h @@ -0,0 +1,131 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Red Hat, Inc. + * Copyright © 2005 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 + * Carl Worth + */ + +#ifndef CAIRO_CACHE_PRIVATE_H +#define CAIRO_CACHE_PRIVATE_H + +typedef struct _cairo_cache cairo_cache_t; + +/** + * cairo_cache_entry_t: + * + * A #cairo_cache_entry_t contains both a key and a value for + * cairo_cache_t. User-derived types for cairo_cache_entry_t must + * be type-compatible with this structure (eg. they must have an + * unsigned long as the first parameter. The easiest way to get this + * is to use: + * + * typedef _my_entry { + * cairo_cache_entry_t base; + * ... Remainder of key and value fields here .. + * } my_entry_t; + * + * which then allows a pointer to my_entry_t to be passed to any of + * the cairo_cache functions as follows without requiring a cast: + * + * _cairo_cache_insert (cache, &my_entry->base, size); + * + * IMPORTANT: The caller is responsible for initializing + * my_entry->base.hash with a hash code derived from the key. The + * essential property of the hash code is that keys_equal must never + * return TRUE for two keys that have different hashes. The best hash + * code will reduce the frequency of two keys with the same code for + * which keys_equal returns FALSE. + * + * The user must also initialize my_entry->base.size to indicate + * the size of the current entry. What units to use for size is + * entirely up to the caller, (though the same units must be used for + * the max_size parameter passed to _cairo_cache_create). If all + * entries are close to the same size, the simplest thing to do is to + * just use units of "entries", (eg. set size==1 in all entries and + * set max_size to the number of entries which you want to be saved + * in the cache). + * + * Which parts of the entry make up the "key" and which part make up + * the value are entirely up to the caller, (as determined by the + * computation going into base.hash as well as the keys_equal + * function). A few of the cairo_cache functions accept an entry which + * will be used exclusively as a "key", (indicated by a parameter name + * of key). In these cases, the value-related fields of the entry need + * not be initialized if so desired. + **/ +typedef struct _cairo_cache_entry { + unsigned long hash; + unsigned long size; +} cairo_cache_entry_t; + +typedef cairo_bool_t +(*cairo_cache_keys_equal_func_t) (void *key_a, void *key_b); + +typedef void +(*cairo_cache_callback_func_t) (void *entry, + void *closure); + +cairo_private cairo_cache_t * +_cairo_cache_create (cairo_cache_keys_equal_func_t keys_equal, + cairo_destroy_func_t entry_destroy, + unsigned long max_size); + +cairo_private void +_cairo_cache_destroy (cairo_cache_t *cache); + +cairo_private void +_cairo_cache_preserve (cairo_cache_t *cache); + +cairo_private void +_cairo_cache_release (cairo_cache_t *cache); + +cairo_private cairo_bool_t +_cairo_cache_lookup (cairo_cache_t *cache, + cairo_cache_entry_t *key, + cairo_cache_entry_t **entry_return); + +cairo_private cairo_status_t +_cairo_cache_insert (cairo_cache_t *cache, + cairo_cache_entry_t *entry); + +cairo_private void +_cairo_cache_remove (cairo_cache_t *cache, + cairo_cache_entry_t *key); + +cairo_private void +_cairo_cache_foreach (cairo_cache_t *cache, + cairo_cache_callback_func_t cache_callback, + void *closure); + +#endif diff --git a/src/cairo-cache.c b/src/cairo-cache.c index b43bccabf..25a35bf8e 100644 --- a/src/cairo-cache.c +++ b/src/cairo-cache.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * - * This file is Copyright © 2004 Red Hat, Inc. + * Copyright © 2004 Red Hat, Inc. + * Copyright © 2005 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 @@ -30,481 +31,298 @@ * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): - * Keith Packard + * Keith Packard * Graydon Hoare + * Carl Worth */ #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. - */ +struct _cairo_cache { + cairo_hash_table_t *hash_table; -static const 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 } + cairo_destroy_func_t entry_destroy; + + unsigned long max_size; + unsigned long size; + + cairo_bool_t preserve_entries; }; -#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. - * If max_memory is set, entries are not expired, and must be explicitely - * removed. - * - * 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 NDEBUG -#define _cache_sane_state(c) -#else -static void -_cache_sane_state (cairo_cache_t *cache) +static cairo_status_t +_cairo_cache_init (cairo_cache_t *cache, + cairo_cache_keys_equal_func_t keys_equal, + cairo_destroy_func_t entry_destroy, + unsigned long max_size) { - assert (cache != NULL); - assert (cache->entries != NULL); - assert (cache->backend != NULL); - assert (cache->arrangement != NULL); - /* Cannot check this, a single object may larger */ - /* assert (cache->used_memory <= cache->max_memory); */ - assert (cache->live_entries <= cache->arrangement->size); + cache->hash_table = _cairo_hash_table_create (keys_equal); + if (cache->hash_table == NULL) + return CAIRO_STATUS_NO_MEMORY; + + cache->entry_destroy = entry_destroy; + + cache->max_size = max_size; + cache->size = 0; + + cache->preserve_entries = FALSE; + + return CAIRO_STATUS_SUCCESS; } -#endif static void -_entry_destroy (cairo_cache_t *cache, unsigned long i) +_cairo_cache_fini (cairo_cache_t *cache) { - _cache_sane_state (cache); + cairo_cache_entry_t *entry; - 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); + /* We have to manually remove all entries from the cache ourselves + * rather than relying on _cairo_hash_table_destroy to do that + * since otherwise the cache->entry_destroy callback would not get + * called on each entry. */ - 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) - { - /* Found an empty spot, there can't be a match */ - break; - } - else if (*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; + while (1) { + entry = _cairo_hash_table_random_entry (cache->hash_table, NULL); + if (entry == NULL) + break; + _cairo_cache_remove (cache, entry); } - /* - * 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; + _cairo_hash_table_destroy (cache->hash_table); + cache->size = 0; } -static cairo_cache_entry_base_t ** -_find_available_entry_for (cairo_cache_t *cache, - void *key) +/** + * _cairo_cache_create: + * @keys_equal: a function to return TRUE if two keys are equal + * @entry_destroy: destroy notifier for cache entries + * @max_size: the maximum size for this cache + * + * Creates a new cache using the keys_equal() function to determine + * the equality of entries. + * + * Data is provided to the cache in the form of user-derived version + * of cairo_cache_entry_t. A cache entry must be able to hold hash + * code, a size, and the key/value pair being stored in the + * cache. Sometimes only the key will be necessary, (as in + * _cairo_cache_remove()), and in these cases the value portion of the + * entry need not be initialized. + * + * The units for max_size can be chosen by the caller, but should be + * consistent with the units of the size field of cache entries. When + * adding an entry with _cairo_cache_insert if the total size of + * entries in the cache would exceed max_size then entries will be + * removed at random until the new entry would fit or the cache is + * empty. Then the new entry is inserted. + * + * There are cases in which the automatic removal of entries is + * undesired. If the cache entries have reference counts, then it is a + * simple matter to use the reference counts to ensure that entries + * continue to live even after being ejected from the cache. However, + * in some cases the memory overhead of adding a reference count to + * the entry would be objectionable. In such cases, the + * _cairo_cache_preserve() and _cairo_cache_release() calls can be + * used to establish a window during which no automatic removal of + * entries will occur. + * + * Return value: + **/ +cairo_cache_t * +_cairo_cache_create (cairo_cache_keys_equal_func_t keys_equal, + cairo_destroy_func_t entry_destroy, + unsigned long max_size) { - return _cache_lookup (cache, key, NULL); -} + cairo_status_t status; + cairo_cache_t *cache; -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); -} + cache = malloc (sizeof (cairo_cache_t)); + if (cache == NULL) + return NULL; -static const 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 - -/* Find a random in the cache matching the given predicate. We use the - * same algorithm as the probing algorithm to walk over the entries in - * the hash table in a pseudo-random order. Walking linearly would - * favor entries following gaps in the hash table. We could also - * call rand() repeatedly, which works well for almost-full tables, - * but degrades when the table is almost empty, or predicate - * returns false for most entries. - */ -static cairo_cache_entry_base_t ** -_random_entry (cairo_cache_t *cache, - int (*predicate)(void*)) -{ - cairo_cache_entry_base_t **probe; - unsigned long hash; - unsigned long table_size, i, idx, step; - - _cache_sane_state (cache); - - table_size = cache->arrangement->size; - hash = rand (); - idx = hash % table_size; - step = 0; - - for (i = 0; i < table_size; ++i) - { - assert(idx < table_size); - probe = cache->entries + idx; - - if (LIVE_ENTRY_P(cache, idx) - && (!predicate || predicate (*probe))) - return probe; - - if (step == 0) { - step = hash % cache->arrangement->rehash; - if (step == 0) - step = 1; - } - - idx += step; - if (idx >= table_size) - idx -= table_size; + status = _cairo_cache_init (cache, keys_equal, entry_destroy, max_size); + if (status) { + free (cache); + return NULL; } - return NULL; -} - -/* 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->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 (cache->arrangement->size, - sizeof(cairo_cache_entry_base_t *)); - - if (cache->entries == NULL) - return CAIRO_STATUS_NO_MEMORY; - } - _cache_sane_state (cache); - return CAIRO_STATUS_SUCCESS; + return cache; } +/** + * _cairo_cache_destroy: + * @cache: a cache to destroy + * + * Immediately destroys the given cache, freeing all resources + * associated with it. As part of this process, the entry_destroy() + * function, (as passed to _cairo_cache_create), will be called for + * each entry in the cache. + **/ void _cairo_cache_destroy (cairo_cache_t *cache) { - unsigned long i; - if (cache == NULL) - return; - - _cache_sane_state (cache); + _cairo_cache_fini (cache); - for (i = 0; i < cache->arrangement->size; ++i) - _entry_destroy (cache, i); - - free (cache->entries); - cache->entries = NULL; - cache->backend->destroy_cache (cache); + free (cache); } +/** + * _cairo_cache_preserve: + * @cache: a cache with some precious entries in it (or about to be + * added) + * + * Disable the automatic ejection of entries from the cache. Future + * calls to _cairo_cache_insert will add new entries to the cache + * regardless of how large the cache grows. See + * _cairo_cache_release(). + **/ void -_cairo_cache_shrink_to (cairo_cache_t *cache, - unsigned long max_memory) +_cairo_cache_preserve (cairo_cache_t *cache) { - unsigned long idx; - /* Make some entries die if we're under memory pressure. */ - while (cache->live_entries > 0 && cache->used_memory > max_memory) { - idx = _random_entry (cache, NULL) - cache->entries; - assert (idx < cache->arrangement->size); - _entry_destroy (cache, idx); - } + cache->preserve_entries = TRUE; } -cairo_status_t -_cairo_cache_lookup (cairo_cache_t *cache, - void *key, - void **entry_return, - int *created_entry) +/** + * _cairo_cache_release: + * @cache: a cache, just after the entries in it have become less + * previous + * + * Cancel the effects of _cairo_cache_preserve(). That is, allow the + * cache to resume ejecting entries when it is larger than max_size as + * passed to cairo_cache_create. If the cache is already larger than + * max_size, no entries will be immediately removed, but the cache + * will be brought down to size at the time of the next call to + * _cairo_cache_insert. + **/ +void +_cairo_cache_release (cairo_cache_t *cache) { - - 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; - if (created_entry) - *created_entry = 0; - return status; - } - -#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE - cache->misses++; -#endif - - /* Build the new entry. */ - status = cache->backend->create_entry (cache, key, - (void **)&new_entry); - if (status != CAIRO_STATUS_SUCCESS) - return status; - - /* Store the hash value in case the backend forgot. */ - new_entry->hashcode = cache->backend->hash (cache, key); - - if (cache->live_entries && cache->max_memory) - _cairo_cache_shrink_to (cache, cache->max_memory); - - /* Can't assert this; new_entry->memory may be larger than max_memory */ - /* 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); - 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); - - *entry_return = new_entry; - if (created_entry) - *created_entry = 1; - - return status; + cache->preserve_entries = FALSE; } -cairo_status_t -_cairo_cache_remove (cairo_cache_t *cache, - void *key) +/** + * _cairo_cache_lookup: + * @cache: a cache + * @key: the key of interest + * @entry_return: pointer for return value + * + * Performs a lookup in @cache looking for an entry which has a key + * that matches @key, (as determined by the keys_equal() function + * passed to _cairo_cache_create). + * + * Return value: TRUE if there is an entry in the cache that matches + * @key, (which will now be in *entry_return). FALSE otherwise, (in + * which case *entry_return will be NULL). + **/ +cairo_private cairo_bool_t +_cairo_cache_lookup (cairo_cache_t *cache, + cairo_cache_entry_t *key, + cairo_cache_entry_t **entry_return) { - cairo_cache_entry_base_t **slot; + return _cairo_hash_table_lookup (cache->hash_table, + (cairo_hash_entry_t *) key, + (cairo_hash_entry_t **) entry_return); +} - _cache_sane_state (cache); +/** + * _cairo_cache_remove_random: + * @cache: a cache + * + * Remove a random entry from the cache. + * + * Return value: CAIRO_STATUS_SUCCESS if an entry was successfully + * removed. CAIRO_INT_STATUS_CACHE_EMPTY if there are no entries that + * can be removed. + **/ +static cairo_int_status_t +_cairo_cache_remove_random (cairo_cache_t *cache) +{ + cairo_cache_entry_t *entry; - /* See if we have an entry in the table already. */ - slot = _find_exact_live_entry_for (cache, key); - if (slot != NULL) - _entry_destroy (cache, slot - cache->entries); + entry = _cairo_hash_table_random_entry (cache->hash_table, NULL); + if (entry == NULL) + return CAIRO_INT_STATUS_CACHE_EMPTY; + + _cairo_cache_remove (cache, entry); return CAIRO_STATUS_SUCCESS; } -void * -_cairo_cache_random_entry (cairo_cache_t *cache, - int (*predicate)(void*)) +/** + * _cairo_cache_insert: + * @cache: a cache + * @entry: an entry to be inserted + * + * Insert @entry into the cache. If an entry exists in the cache with + * a matching key, then the old entry will be removed first, (and the + * entry_destroy() callback will be called on it). + * + * Return value: CAIRO_STATUS_SUCCESS if successful or + * CAIRO_STATUS_NO_MEMORY if insufficient memory is available. + **/ +cairo_private cairo_status_t +_cairo_cache_insert (cairo_cache_t *cache, + cairo_cache_entry_t *entry) { - cairo_cache_entry_base_t **slot = _random_entry (cache, predicate); + cairo_status_t status; - return slot ? *slot : NULL; + if (! cache->preserve_entries) { + while (cache->size + entry->size > cache->max_size) { + status = _cairo_cache_remove_random (cache); + if (status) { + if (status == CAIRO_INT_STATUS_CACHE_EMPTY) + break; + return status; + } + } + } + + status = _cairo_hash_table_insert (cache->hash_table, + (cairo_hash_entry_t *) entry); + if (status) + return status; + + cache->size += entry->size; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_cache_remove: + * @cache: a cache + * @entry: key of entry to be removed + * + * Remove an entry from the cache which has a key that matches @key, + * if any (as determined by the keys_equal() function passed to + * _cairo_cache_create). + **/ +cairo_private void +_cairo_cache_remove (cairo_cache_t *cache, + cairo_cache_entry_t *entry) +{ + cache->size -= entry->size; + + _cairo_hash_table_remove (cache->hash_table, + (cairo_hash_entry_t *) entry); + + if (cache->entry_destroy) + cache->entry_destroy (entry); +} + +/** + * _cairo_cache_foreach: + * @cache: a cache + * @cache_callback: function to be called for each entry + * @closure: additional argument to be passed to @cache_callback + * + * Call @cache_callback for each entry in the cache, in a + * non-specified order. + **/ +void +_cairo_cache_foreach (cairo_cache_t *cache, + cairo_cache_callback_func_t cache_callback, + void *closure) +{ + _cairo_hash_table_foreach (cache->hash_table, + cache_callback, + closure); } unsigned long diff --git a/src/cairo-debug.c b/src/cairo-debug.c index 31eefc5ae..6d32b418b 100644 --- a/src/cairo-debug.c +++ b/src/cairo-debug.c @@ -60,7 +60,6 @@ void cairo_debug_reset_static_data (void) { #if CAIRO_HAS_XLIB_SURFACE - _cairo_xlib_surface_reset_static_data (); _cairo_xlib_screen_reset_static_data (); #endif diff --git a/src/cairo-font.c b/src/cairo-font.c index b228f2850..b0fab1b4a 100644 --- a/src/cairo-font.c +++ b/src/cairo-font.c @@ -413,561 +413,6 @@ static const cairo_font_face_backend_t _cairo_toy_font_face_backend = { _cairo_toy_font_face_scaled_font_create }; -/* cairo_scaled_font_t */ - -static const cairo_scaled_font_t _cairo_scaled_font_nil = { - { 0 }, /* hash_entry */ - CAIRO_STATUS_NO_MEMORY, /* status */ - -1, /* ref_count */ - NULL, /* font_face */ - { 1., 0., 0., 1., 0, 0}, /* font_matrix */ - { 1., 0., 0., 1., 0, 0}, /* ctm */ - { 1., 0., 0., 1., 0, 0}, /* scale */ - { CAIRO_ANTIALIAS_DEFAULT, /* options */ - CAIRO_SUBPIXEL_ORDER_DEFAULT, - CAIRO_HINT_STYLE_DEFAULT, - CAIRO_HINT_METRICS_DEFAULT} , - CAIRO_SCALED_FONT_BACKEND_DEFAULT, -}; - -/** - * _cairo_scaled_font_set_error: - * @scaled_font: a scaled_font - * @status: a status value indicating an error, (eg. not - * CAIRO_STATUS_SUCCESS) - * - * Sets scaled_font->status to @status and calls _cairo_error; - * - * All assignments of an error status to scaled_font->status should happen - * through _cairo_scaled_font_set_error() or else _cairo_error() should be - * called immediately after the assignment. - * - * The purpose of this function is to allow the user to set a - * breakpoint in _cairo_error() to generate a stack trace for when the - * user causes cairo to detect an error. - **/ -void -_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, - cairo_status_t status) -{ - /* Don't overwrite an existing error. This preserves the first - * error, which is the most significant. It also avoids attempting - * to write to read-only data (eg. from a nil scaled_font). */ - if (scaled_font->status == CAIRO_STATUS_SUCCESS) - scaled_font->status = status; - - _cairo_error (status); -} - -/** - * cairo_scaled_font_status: - * @scaled_font: a #cairo_scaled_font_t - * - * Checks whether an error has previously occurred for this - * scaled_font. - * - * Return value: %CAIRO_STATUS_SUCCESS or another error such as - * %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_status_t -cairo_scaled_font_status (cairo_scaled_font_t *scaled_font) -{ - return scaled_font->status; -} - -/* Here we keep a unique mapping from - * cairo_font_face_t/matrix/ctm/options => cairo_scaled_font_t. - * - * Here are the things that we want to map: - * - * a) All otherwise referenced cairo_scaled_font_t's - * b) Some number of not otherwise referenced cairo_scaled_font_t's - * - * The implementation uses a hash table which covers (a) - * completely. Then, for (b) we have an array of otherwise - * unreferenced fonts (holdovers) which are expired in - * least-recently-used order. - * - * The cairo_scaled_font_create code gets to treat this like a regular - * hash table. All of the magic for the little holdover cache is in - * cairo_scaled_font_reference and cairo_scaled_font_destroy. - */ - -/* This defines the size of the holdover array ... that is, the number - * of scaled fonts we keep around even when not otherwise referenced - */ -#define CAIRO_SCALED_FONT_MAX_HOLDOVERS 24 - -typedef struct _cairo_scaled_font_map { - cairo_hash_table_t *hash_table; - cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS]; - int num_holdovers; -} cairo_scaled_font_map_t; - -static cairo_scaled_font_map_t *cairo_scaled_font_map = NULL; - -CAIRO_MUTEX_DECLARE (cairo_scaled_font_map_mutex); - -static int -_cairo_scaled_font_keys_equal (void *abstract_key_a, void *abstract_key_b); - -static cairo_scaled_font_map_t * -_cairo_scaled_font_map_lock (void) -{ - CAIRO_MUTEX_LOCK (cairo_scaled_font_map_mutex); - - if (cairo_scaled_font_map == NULL) - { - cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t)); - if (cairo_scaled_font_map == NULL) - goto CLEANUP_MUTEX_LOCK; - - cairo_scaled_font_map->hash_table = - _cairo_hash_table_create (_cairo_scaled_font_keys_equal); - - if (cairo_scaled_font_map->hash_table == NULL) - goto CLEANUP_SCALED_FONT_MAP; - - cairo_scaled_font_map->num_holdovers = 0; - } - - return cairo_scaled_font_map; - - CLEANUP_SCALED_FONT_MAP: - free (cairo_scaled_font_map); - CLEANUP_MUTEX_LOCK: - CAIRO_MUTEX_UNLOCK (cairo_scaled_font_map_mutex); - return NULL; -} - -static void -_cairo_scaled_font_map_unlock (void) -{ - CAIRO_MUTEX_UNLOCK (cairo_scaled_font_map_mutex); -} - -static void -_cairo_scaled_font_map_destroy (void) -{ - int i; - cairo_scaled_font_map_t *font_map = cairo_scaled_font_map; - cairo_scaled_font_t *scaled_font; - - if (font_map == NULL) - return; - - CAIRO_MUTEX_UNLOCK (cairo_scaled_font_map_mutex); - - for (i = 0; i < font_map->num_holdovers; i++) { - scaled_font = font_map->holdovers[i]; - /* We should only get here through the reset_static_data path - * and there had better not be any active references at that - * point. */ - assert (scaled_font->ref_count == 0); - _cairo_hash_table_remove (font_map->hash_table, - &scaled_font->hash_entry); - _cairo_scaled_font_fini (scaled_font); - free (scaled_font); - } - - _cairo_hash_table_destroy (font_map->hash_table); - - free (cairo_scaled_font_map); - cairo_scaled_font_map = NULL; -} - -/* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/) - * - * Not necessarily better than a lot of other hashes, but should be OK, and - * well tested with binary data. - */ - -#define FNV_32_PRIME ((uint32_t)0x01000193) -#define FNV1_32_INIT ((uint32_t)0x811c9dc5) - -static uint32_t -_hash_bytes_fnv (unsigned char *buffer, - int len, - uint32_t hval) -{ - while (len--) { - hval *= FNV_32_PRIME; - hval ^= *buffer++; - } - - return hval; -} - -static void -_cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font, - cairo_font_face_t *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options) -{ - uint32_t hash = FNV1_32_INIT; - - scaled_font->status = CAIRO_STATUS_SUCCESS; - scaled_font->font_face = font_face; - scaled_font->font_matrix = *font_matrix; - scaled_font->ctm = *ctm; - scaled_font->options = *options; - - /* We do a bytewise hash on the font matrices, ignoring the - * translation values. */ - hash = _hash_bytes_fnv ((unsigned char *)(&scaled_font->font_matrix.xx), - sizeof(double) * 4, - hash); - hash = _hash_bytes_fnv ((unsigned char *)(&scaled_font->ctm.xx), - sizeof(double) * 4, - hash); - - hash ^= (unsigned long) scaled_font->font_face; - - hash ^= cairo_font_options_hash (&scaled_font->options); - - scaled_font->hash_entry.hash = hash; -} - -static cairo_bool_t -_cairo_scaled_font_keys_equal (void *abstract_key_a, void *abstract_key_b) -{ - cairo_scaled_font_t *key_a = abstract_key_a; - cairo_scaled_font_t *key_b = abstract_key_b; - - return (key_a->font_face == key_b->font_face && - memcmp ((unsigned char *)(&key_a->font_matrix.xx), - (unsigned char *)(&key_b->font_matrix.xx), - sizeof(double) * 4) == 0 && - memcmp ((unsigned char *)(&key_a->ctm.xx), - (unsigned char *)(&key_b->ctm.xx), - sizeof(double) * 4) == 0 && - cairo_font_options_equal (&key_a->options, &key_b->options)); -} - -void -_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, - cairo_font_face_t *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - const cairo_scaled_font_backend_t *backend) -{ - scaled_font->ref_count = 1; - - _cairo_scaled_font_init_key (scaled_font, font_face, - font_matrix, ctm, options); - - cairo_font_face_reference (font_face); - - cairo_matrix_multiply (&scaled_font->scale, - &scaled_font->font_matrix, - &scaled_font->ctm); - - scaled_font->backend = backend; -} - -void -_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - if (scaled_font->font_face) - cairo_font_face_destroy (scaled_font->font_face); - - scaled_font->backend->fini (scaled_font); -} - -/** - * cairo_scaled_font_create: - * @font_face: a #cairo_font_face_t - * @font_matrix: font space to user space transformation matrix for the - * font. In the simplest case of a N point font, this matrix is - * just a scale by N, but it can also be used to shear the font - * or stretch it unequally along the two axes. See - * cairo_set_font_matrix(). - * @ctm: user to device transformation matrix with which the font will - * be used. - * @options: options to use when getting metrics for the font and - * rendering with it. - * - * Creates a #cairo_scaled_font_t object from a font face and matrices that - * describe the size of the font and the environment in which it will - * be used. - * - * Return value: a newly created #cairo_scaled_font_t. Destroy with - * cairo_scaled_font_destroy() - **/ -cairo_scaled_font_t * -cairo_scaled_font_create (cairo_font_face_t *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options) -{ - cairo_status_t status; - cairo_scaled_font_map_t *font_map; - cairo_scaled_font_t key, *scaled_font = NULL; - - font_map = _cairo_scaled_font_map_lock (); - if (font_map == NULL) - goto UNWIND; - - _cairo_scaled_font_init_key (&key, font_face, - font_matrix, ctm, options); - - /* Return existing scaled_font if it exists in the hash table. */ - if (_cairo_hash_table_lookup (font_map->hash_table, &key.hash_entry, - (cairo_hash_entry_t**) &scaled_font)) - { - _cairo_scaled_font_map_unlock (); - return cairo_scaled_font_reference (scaled_font); - } - - /* Otherwise create it and insert it into the hash table. */ - status = font_face->backend->scaled_font_create (font_face, font_matrix, - ctm, options, &scaled_font); - if (status) - goto UNWIND_FONT_MAP_LOCK; - - status = _cairo_hash_table_insert (font_map->hash_table, - &scaled_font->hash_entry); - if (status) - goto UNWIND_SCALED_FONT_CREATE; - - _cairo_scaled_font_map_unlock (); - - return scaled_font; - -UNWIND_SCALED_FONT_CREATE: - /* We can't call _cairo_scaled_font_destroy here since it expects - * that the font has already been successfully inserted into the - * hash table. */ - _cairo_scaled_font_fini (scaled_font); - free (scaled_font); -UNWIND_FONT_MAP_LOCK: - _cairo_scaled_font_map_unlock (); -UNWIND: - return NULL; -} - -/** - * cairo_scaled_font_reference: - * @scaled_font: a #cairo_scaled_font_t, (may be NULL in which case - * this function does nothing) - * - * Increases the reference count on @scaled_font by one. This prevents - * @scaled_font from being destroyed until a matching call to - * cairo_scaled_font_destroy() is made. - * - * Returns: the referenced #cairo_scaled_font_t - **/ -cairo_scaled_font_t * -cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font) -{ - if (scaled_font == NULL) - return NULL; - - if (scaled_font->ref_count == (unsigned int)-1) - return scaled_font; - - /* We would normally assert (scaled_font->ref_count > 0) here, but - * we are using ref_count == 0 as a legitimate case for the - * holdovers array. See below. */ - - /* If the original reference count is 0, then this font must have - * been found in font_map->holdovers, (which means this caching is - * actually working). So now we remove it from the holdovers - * array. */ - if (scaled_font->ref_count == 0) { - cairo_scaled_font_map_t *font_map; - int i; - - font_map = _cairo_scaled_font_map_lock (); - { - for (i = 0; i < font_map->num_holdovers; i++) - if (font_map->holdovers[i] == scaled_font) - break; - assert (i < font_map->num_holdovers); - - font_map->num_holdovers--; - memmove (&font_map->holdovers[i], - &font_map->holdovers[i+1], - (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*)); - } - _cairo_scaled_font_map_unlock (); - } - - scaled_font->ref_count++; - - return scaled_font; -} - -/** - * cairo_scaled_font_destroy: - * @scaled_font: a #cairo_scaled_font_t - * - * Decreases the reference count on @font by one. If the result - * is zero, then @font and all associated resources are freed. - * See cairo_scaled_font_reference(). - **/ -void -cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font) -{ - cairo_scaled_font_map_t *font_map; - - if (scaled_font == NULL) - return; - - if (scaled_font->ref_count == (unsigned int)-1) - return; - - assert (scaled_font->ref_count > 0); - - if (--(scaled_font->ref_count) > 0) - return; - - font_map = _cairo_scaled_font_map_lock (); - assert (font_map != NULL); - { - /* Rather than immediately destroying this object, we put it into - * the font_map->holdovers array in case it will get used again - * soon. To make room for it, we do actually destroy the - * least-recently-used holdover. - */ - if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) { - cairo_scaled_font_t *lru; - - lru = font_map->holdovers[0]; - assert (lru->ref_count == 0); - - _cairo_hash_table_remove (font_map->hash_table, &lru->hash_entry); - - _cairo_scaled_font_fini (lru); - free (lru); - - font_map->num_holdovers--; - memmove (&font_map->holdovers[0], - &font_map->holdovers[1], - font_map->num_holdovers * sizeof (cairo_scaled_font_t*)); - } - - font_map->holdovers[font_map->num_holdovers] = scaled_font; - font_map->num_holdovers++; - } - _cairo_scaled_font_map_unlock (); -} - -cairo_status_t -_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, - const char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs) -{ - if (scaled_font->status) - return scaled_font->status; - - return scaled_font->backend->text_to_glyphs (scaled_font, utf8, glyphs, num_glyphs); -} - -cairo_status_t -_cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - if (scaled_font->status) - return scaled_font->status; - - return scaled_font->backend->glyph_extents (scaled_font, glyphs, num_glyphs, extents); -} - - -cairo_status_t -_cairo_scaled_font_glyph_bbox (cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) -{ - if (scaled_font->status) - return scaled_font->status; - - return scaled_font->backend->glyph_bbox (scaled_font, glyphs, num_glyphs, bbox); -} - -cairo_status_t -_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_status_t status; - - /* These operators aren't interpreted the same way by the backends; - * they are implemented in terms of other operators in cairo-gstate.c - */ - assert (operator != CAIRO_OPERATOR_SOURCE && operator != CAIRO_OPERATOR_CLEAR); - - if (scaled_font->status) - return scaled_font->status; - - status = _cairo_surface_show_glyphs (scaled_font, operator, pattern, - surface, - source_x, source_y, - dest_x, dest_y, - width, height, - glyphs, num_glyphs); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - /* Surface display routine either does not exist or failed. */ - return scaled_font->backend->show_glyphs (scaled_font, operator, pattern, - surface, - source_x, source_y, - dest_x, dest_y, - width, height, - glyphs, num_glyphs); -} - -cairo_status_t -_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_fixed_t *path) -{ - if (scaled_font->status) - return scaled_font->status; - - return scaled_font->backend->glyph_path (scaled_font, glyphs, num_glyphs, path); -} - -cairo_status_t -_cairo_scaled_font_get_glyph_cache_key (cairo_scaled_font_t *scaled_font, - cairo_glyph_cache_key_t *key) -{ - if (scaled_font->status) - return scaled_font->status; - - scaled_font->backend->get_glyph_cache_key (scaled_font, key); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font, - cairo_font_extents_t *extents) -{ - if (scaled_font->status) - return scaled_font->status; - - return scaled_font->backend->font_extents (scaled_font, extents); -} - void _cairo_unscaled_font_init (cairo_unscaled_font_t *unscaled_font, const cairo_unscaled_font_backend_t *backend) @@ -1001,303 +446,11 @@ _cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font) free (unscaled_font); } -/* Public font API follows. */ - -/** - * cairo_scaled_font_extents: - * @scaled_font: a #cairo_scaled_font_t - * @extents: a #cairo_font_extents_t which to store the retrieved extents. - * - * Gets the metrics for a #cairo_scaled_font_t. - **/ -void -cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, - cairo_font_extents_t *extents) -{ - cairo_int_status_t status; - double font_scale_x, font_scale_y; - - if (scaled_font->status) - return; - - status = _cairo_scaled_font_font_extents (scaled_font, extents); - if (status) { - _cairo_scaled_font_set_error (scaled_font, status); - return; - } - - _cairo_matrix_compute_scale_factors (&scaled_font->font_matrix, - &font_scale_x, &font_scale_y, - /* XXX */ 1); - - /* - * The font responded in unscaled units, scale by the font - * matrix scale factors to get to user space - */ - - extents->ascent *= font_scale_y; - extents->descent *= font_scale_y; - extents->height *= font_scale_y; - extents->max_x_advance *= font_scale_x; - extents->max_y_advance *= font_scale_y; -} - -/** - * cairo_font_glyph_extents: - * @scaled_font: a #cairo_scaled_font_t - * @glyphs: an array of glyph IDs with X and Y offsets. - * @num_glyphs: the number of glyphs in the @glyphs array - * @extents: a #cairo_text_extents_t which to store the retrieved extents. - * - * cairo_font_glyph_extents() gets the overall metrics for a string of - * glyphs. The X and Y offsets in @glyphs are taken from an origin of 0,0. - **/ -void -cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_glyph_t origin_glyph; - cairo_text_extents_t origin_extents; - int i; - double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0; - double x_pos = 0.0, y_pos = 0.0; - int set = 0; - - if (scaled_font->status) - return; - - if (!num_glyphs) - { - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; - - return; - } - - for (i = 0; i < num_glyphs; i++) - { - double x, y; - double wm, hm; - - origin_glyph = glyphs[i]; - origin_glyph.x = 0.0; - origin_glyph.y = 0.0; - status = _cairo_scaled_font_glyph_extents (scaled_font, - &origin_glyph, 1, - &origin_extents); - - /* - * Transform font space metrics into user space metrics - * by running the corners through the font matrix and - * expanding the bounding box as necessary - */ - x = origin_extents.x_bearing; - y = origin_extents.y_bearing; - cairo_matrix_transform_point (&scaled_font->font_matrix, - &x, &y); - - for (hm = 0.0; hm <= 1.0; hm += 1.0) - for (wm = 0.0; wm <= 1.0; wm += 1.0) - { - x = origin_extents.x_bearing + origin_extents.width * wm; - y = origin_extents.y_bearing + origin_extents.height * hm; - cairo_matrix_transform_point (&scaled_font->font_matrix, - &x, &y); - x += glyphs[i].x; - y += glyphs[i].y; - if (!set) - { - min_x = max_x = x; - min_y = max_y = y; - set = 1; - } - else - { - if (x < min_x) min_x = x; - if (x > max_x) max_x = x; - if (y < min_y) min_y = y; - if (y > max_y) max_y = y; - } - } - - x = origin_extents.x_advance; - y = origin_extents.y_advance; - cairo_matrix_transform_point (&scaled_font->font_matrix, - &x, &y); - x_pos = glyphs[i].x + x; - y_pos = glyphs[i].y + y; - } - - extents->x_bearing = min_x - glyphs[0].x; - extents->y_bearing = min_y - glyphs[0].y; - extents->width = max_x - min_x; - extents->height = max_y - min_y; - extents->x_advance = x_pos - glyphs[0].x; - extents->y_advance = y_pos - glyphs[0].y; -} - -/* 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.xx) - ^ ((unsigned long) in->scale.yx) - ^ ((unsigned long) in->scale.xy) - ^ ((unsigned long) in->scale.yy) - ^ (in->flags * 1451) /* 1451 is just an abitrary prime */ - ^ in->index; -} - -int -_cairo_glyph_cache_keys_equal (void *cache, - void *k1, - void *k2) -{ - 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->flags == b->flags) - && (a->scale.xx == b->scale.xx) - && (a->scale.yx == b->scale.yx) - && (a->scale.xy == b->scale.xy) - && (a->scale.yy == b->scale.yy); -} - - -static cairo_status_t -_image_glyph_cache_create_entry (void *cache, - void *key, - void **return_value) -{ - cairo_glyph_cache_key_t *k = (cairo_glyph_cache_key_t *) key; - cairo_image_glyph_cache_entry_t *im; - cairo_status_t status; - - 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->key.unscaled, - im); - - if (status != CAIRO_STATUS_SUCCESS) { - free (im); - return status; - } - - _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; -} - - -static void -_image_glyph_cache_destroy_entry (void *cache, - void *value) -{ - 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); -} - -static void -_image_glyph_cache_destroy_cache (void *cache) -{ - free (cache); -} - -static 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 -}; - -CAIRO_MUTEX_DECLARE(_global_image_glyph_cache_mutex); - -static cairo_cache_t * -_global_image_glyph_cache = NULL; - -void -_cairo_lock_global_image_glyph_cache (void) -{ - CAIRO_MUTEX_LOCK (_global_image_glyph_cache_mutex); -} - -void -_cairo_unlock_global_image_glyph_cache (void) -{ - if (_global_image_glyph_cache) { - _cairo_cache_shrink_to (_global_image_glyph_cache, - CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT); - } - CAIRO_MUTEX_UNLOCK (_global_image_glyph_cache_mutex); -} - -cairo_cache_t * -_cairo_get_global_image_glyph_cache (void) -{ - 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, - 0)) - 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; -} - void _cairo_font_reset_static_data (void) { _cairo_scaled_font_map_destroy (); - _cairo_lock_global_image_glyph_cache(); - _cairo_cache_destroy (_global_image_glyph_cache); - _global_image_glyph_cache = NULL; - _cairo_unlock_global_image_glyph_cache(); - CAIRO_MUTEX_LOCK (cairo_toy_font_face_hash_table_mutex); _cairo_hash_table_destroy (cairo_toy_font_face_hash_table); cairo_toy_font_face_hash_table = NULL; diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index bcc66580d..45f1ed07b 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -57,20 +57,6 @@ #define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) #define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) -/* We pack some of our own information into the bits unused - * by FreeType's load flags. If FreeType ever uses up all - * the load flag bits, we'll have to do something else. - * (probably just store what we care about in load_flags - * then convert into FreeType terms. - */ -#define PRIVATE_FLAG_HINT_METRICS (0x01 << 24) -#define PRIVATE_FLAG_EMBOLDEN (0x02 << 24) -#define PRIVATE_FLAGS_MASK (0xff << 24) - - /* This is the max number of FT_face objects we keep open at once - */ - #define MAX_OPEN_FACES 10 - /* This is the max number of FT_face objects we keep open at once */ #define MAX_OPEN_FACES 10 @@ -123,10 +109,20 @@ _cairo_ft_unscaled_font_keys_equal (void *key_a, static void _cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled); +typedef enum _cairo_ft_extra_flags { + CAIRO_FT_OPTIONS_HINT_METRICS = (1 << 0), + CAIRO_FT_OPTIONS_EMBOLDEN = (1 << 1) +} cairo_ft_extra_flags_t; + +typedef struct _cairo_ft_options { + int load_flags; /* flags for FT_Load_Glyph */ + cairo_ft_extra_flags_t extra_flags; /* other flags that affect results */ +} cairo_ft_options_t; + struct _cairo_ft_font_face { cairo_font_face_t base; cairo_ft_unscaled_font_t *unscaled; - int load_flags; + cairo_ft_options_t ft_options; cairo_ft_font_face_t *next; }; @@ -706,10 +702,10 @@ _native_byte_order_lsb (void) /* Fills in val->image with an image surface created from @bitmap */ static cairo_status_t -_get_bitmap_surface (cairo_image_glyph_cache_entry_t *val, - FT_Bitmap *bitmap, - cairo_bool_t own_buffer, - int rgba) +_get_bitmap_surface (FT_Bitmap *bitmap, + cairo_bool_t own_buffer, + cairo_font_options_t *font_options, + cairo_image_surface_t **surface) { int width, height, stride; unsigned char *data; @@ -723,7 +719,7 @@ _get_bitmap_surface (cairo_image_glyph_cache_entry_t *val, if (own_buffer && bitmap->buffer) free (bitmap->buffer); - val->image = NULL; + *surface = NULL; } else { switch (bitmap->pixel_mode) { case FT_PIXEL_MODE_MONO: @@ -771,10 +767,13 @@ _get_bitmap_surface (cairo_image_glyph_cache_entry_t *val, break; case FT_PIXEL_MODE_LCD: - case FT_PIXEL_MODE_LCD_V: + case FT_PIXEL_MODE_LCD_V: case FT_PIXEL_MODE_GRAY: - if (rgba == FC_RGBA_NONE || rgba == FC_RGBA_UNKNOWN) - { + switch (font_options->antialias) { + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_NONE: + default: stride = bitmap->pitch; if (own_buffer) { data = bitmap->buffer; @@ -785,7 +784,8 @@ _get_bitmap_surface (cairo_image_glyph_cache_entry_t *val, memcpy (data, bitmap->buffer, stride * height); } format = CAIRO_FORMAT_A8; - } else { + break; + case CAIRO_ANTIALIAS_SUBPIXEL: { int x, y; unsigned char *in_line, *out_line, *in; unsigned int *out; @@ -798,20 +798,20 @@ _get_bitmap_surface (cairo_image_glyph_cache_entry_t *val, int vmul = 1; int hmul = 1; - switch (rgba) { - case FC_RGBA_RGB: - case FC_RGBA_BGR: + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: default: width /= 3; hmul = 3; break; - case FC_RGBA_VRGB: - case FC_RGBA_VBGR: + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: vmul = 3; height /= 3; break; } - subpixel = TRUE; /* * Filter the glyph to soften the color fringes */ @@ -821,18 +821,19 @@ _get_bitmap_surface (cairo_image_glyph_cache_entry_t *val, data_rgba = calloc (1, stride_rgba * height); os = 1; - switch (rgba) { - case FC_RGBA_VRGB: + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_VRGB: os = stride; - case FC_RGBA_RGB: + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: default: rf = 0; gf = 1; bf = 2; break; - case FC_RGBA_VBGR: + case CAIRO_SUBPIXEL_ORDER_VBGR: os = stride; - case FC_RGBA_BGR: + case CAIRO_SUBPIXEL_ORDER_BGR: bf = 0; gf = 1; rf = 2; @@ -873,6 +874,9 @@ _get_bitmap_surface (cairo_image_glyph_cache_entry_t *val, data = data_rgba; stride = stride_rgba; format = CAIRO_FORMAT_ARGB32; + subpixel = TRUE; + break; + } } break; case FT_PIXEL_MODE_GRAY2: @@ -882,24 +886,21 @@ _get_bitmap_surface (cairo_image_glyph_cache_entry_t *val, return CAIRO_STATUS_NO_MEMORY; } - val->image = (cairo_image_surface_t *) + *surface = (cairo_image_surface_t *) cairo_image_surface_create_for_data (data, format, width, height, stride); - if (val->image->base.status) { + if ((*surface)->base.status) { free (data); return CAIRO_STATUS_NO_MEMORY; } if (subpixel) - pixman_image_set_component_alpha (val->image->pixman_image, TRUE); + pixman_image_set_component_alpha ((*surface)->pixman_image, TRUE); - _cairo_image_surface_assume_ownership_of_data (val->image); + _cairo_image_surface_assume_ownership_of_data ((*surface)); } - val->size.width = width; - val->size.height = height; - return CAIRO_STATUS_SUCCESS; } @@ -919,10 +920,10 @@ _get_bitmap_surface (cairo_image_glyph_cache_entry_t *val, * this version of the code path entirely. */ static cairo_status_t -_render_glyph_outline (FT_Face face, - cairo_image_glyph_cache_entry_t *val) +_render_glyph_outline (FT_Face face, + cairo_font_options_t *font_options, + cairo_image_surface_t **surface) { - int rgba = FC_RGBA_UNKNOWN; FT_GlyphSlot glyphslot = face->glyph; FT_Outline *outline = &glyphslot->outline; FT_Bitmap bitmap; @@ -931,7 +932,6 @@ _render_glyph_outline (FT_Face face, int hmul = 1; int vmul = 1; unsigned int width, height, stride; - cairo_format_t format; cairo_bool_t subpixel = FALSE; cairo_status_t status; @@ -947,63 +947,66 @@ _render_glyph_outline (FT_Face face, stride = (width * hmul + 3) & ~3; if (width * height == 0) { + cairo_format_t format; /* Looks like fb handles zero-sized images just fine */ - if ((val->key.flags & FT_LOAD_MONOCHROME) != 0) - format = CAIRO_FORMAT_A8; - else if (FT_LOAD_TARGET_MODE (val->key.flags) == FT_RENDER_MODE_LCD || - FT_LOAD_TARGET_MODE (val->key.flags) == FT_RENDER_MODE_LCD_V) + switch (font_options->antialias) { + case CAIRO_ANTIALIAS_NONE: + format = CAIRO_FORMAT_A1; + break; + case CAIRO_ANTIALIAS_SUBPIXEL: format= CAIRO_FORMAT_ARGB32; - else + break; + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: + default: format = CAIRO_FORMAT_A8; + break; + } - val->image = (cairo_image_surface_t *) + (*surface) = (cairo_image_surface_t *) cairo_image_surface_create_for_data (NULL, format, 0, 0, 0); - if (val->image->base.status) + if ((*surface)->base.status) return CAIRO_STATUS_NO_MEMORY; } else { matrix.xx = matrix.yy = 0x10000L; matrix.xy = matrix.yx = 0; - if ((val->key.flags & FT_LOAD_MONOCHROME) != 0) { + switch (font_options->antialias) { + case CAIRO_ANTIALIAS_NONE: bitmap.pixel_mode = FT_PIXEL_MODE_MONO; bitmap.num_grays = 1; stride = ((width + 31) & -32) >> 3; - } else { - /* XXX not a complete set of flags. This code - * will go away when cworth rewrites the glyph - * cache code */ - if (FT_LOAD_TARGET_MODE (val->key.flags) == FT_RENDER_MODE_LCD) - rgba = FC_RGBA_RGB; - else if (FT_LOAD_TARGET_MODE (val->key.flags) == FT_RENDER_MODE_LCD_V) - rgba = FC_RGBA_VBGR; - - switch (rgba) { - case FC_RGBA_RGB: - case FC_RGBA_BGR: + break; + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: + bitmap.pixel_mode = FT_PIXEL_MODE_GRAY; + bitmap.num_grays = 256; + stride = (width + 3) & -4; + break; + case CAIRO_ANTIALIAS_SUBPIXEL: + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: + default: matrix.xx *= 3; hmul = 3; subpixel = TRUE; break; - case FC_RGBA_VRGB: - case FC_RGBA_VBGR: + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: matrix.yy *= 3; vmul = 3; subpixel = TRUE; break; } - if (subpixel) - format = CAIRO_FORMAT_ARGB32; - else - format = CAIRO_FORMAT_A8; - - if (subpixel) - FT_Outline_Transform (outline, &matrix); + FT_Outline_Transform (outline, &matrix); bitmap.pixel_mode = FT_PIXEL_MODE_GRAY; bitmap.num_grays = 256; stride = (width * hmul + 3) & -4; } + bitmap.pitch = stride; bitmap.width = width * hmul; bitmap.rows = height * vmul; @@ -1020,7 +1023,7 @@ _render_glyph_outline (FT_Face face, return CAIRO_STATUS_NO_MEMORY; } - status = _get_bitmap_surface (val, &bitmap, TRUE, rgba); + status = _get_bitmap_surface (&bitmap, TRUE, font_options, surface); if (status) return status; } @@ -1030,8 +1033,8 @@ _render_glyph_outline (FT_Face face, * Y coordinate of the control box needs to be negated. */ - val->size.x = (short) (cbox.xMin >> 6); - val->size.y = - (short) (cbox.yMax >> 6); + (*surface)->base.device_x_offset = floor ((double) cbox.xMin / 64.0); + (*surface)->base.device_y_offset = floor (-(double) cbox.yMax / 64.0); return CAIRO_STATUS_SUCCESS; } @@ -1052,8 +1055,9 @@ _render_glyph_outline (FT_Face face, * this version of the code path entirely. */ static cairo_status_t -_render_glyph_bitmap (FT_Face face, - cairo_image_glyph_cache_entry_t *val) +_render_glyph_bitmap (FT_Face face, + cairo_font_options_t *font_options, + cairo_image_surface_t **surface) { FT_GlyphSlot glyphslot = face->glyph; cairo_status_t status = CAIRO_STATUS_SUCCESS; @@ -1070,14 +1074,21 @@ _render_glyph_bitmap (FT_Face face, if (error) return CAIRO_STATUS_NO_MEMORY; - _get_bitmap_surface (val, &glyphslot->bitmap, FALSE, FC_RGBA_NONE); - - val->size.x = glyphslot->bitmap_left; - val->size.y = - glyphslot->bitmap_top; + _get_bitmap_surface (&glyphslot->bitmap, FALSE, font_options, surface); + /* + * Note: the font's coordinate system is upside down from ours, so the + * Y coordinate of the control box needs to be negated. + */ + + (*surface)->base.device_x_offset = glyphslot->bitmap_left; + (*surface)->base.device_y_offset = -glyphslot->bitmap_top; + return status; } +#if 0 +/* XXX */ static cairo_status_t _transform_glyph_bitmap (cairo_image_glyph_cache_entry_t *val) { @@ -1166,7 +1177,7 @@ _transform_glyph_bitmap (cairo_image_glyph_cache_entry_t *val) /* Draw the original bitmap transformed into the new bitmap */ - _cairo_pattern_init_for_surface (&pattern, &val->image->base); + _cairo_pattern_init_for_surface (&pattern, &(*surface)->base); cairo_pattern_set_matrix (&pattern.base, &transformed_to_original); _cairo_surface_composite (CAIRO_OPERATOR_OVER, @@ -1185,8 +1196,8 @@ _transform_glyph_bitmap (cairo_image_glyph_cache_entry_t *val) cairo_matrix_transform_point (&original_to_transformed, &origin_x, &origin_y); - old_image = val->image; - val->image = (cairo_image_surface_t *)image; + old_image = (*surface); + (*surface) = (cairo_image_surface_t *)image; cairo_surface_destroy (&old_image->base); val->size.width = width; @@ -1196,120 +1207,13 @@ _transform_glyph_bitmap (cairo_image_glyph_cache_entry_t *val) return status; } - -static cairo_status_t -_cairo_ft_unscaled_font_create_glyph (void *abstract_font, - cairo_image_glyph_cache_entry_t *val) -{ - cairo_ft_unscaled_font_t *unscaled = abstract_font; - FT_GlyphSlot glyphslot; - FT_Face face; - FT_Glyph_Metrics *metrics; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - double x_factor, y_factor; - - face = _cairo_ft_unscaled_font_lock_face (unscaled); - if (!face) - return CAIRO_STATUS_NO_MEMORY; - - glyphslot = face->glyph; - metrics = &glyphslot->metrics; - - _cairo_ft_unscaled_font_set_scale (unscaled, &val->key.scale); - - if (FT_Load_Glyph (face, val->key.index, val->key.flags & ~PRIVATE_FLAGS_MASK) != 0) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL; - } - -#if HAVE_FT_GLYPHSLOT_EMBOLDEN - if (val->key.flags & PRIVATE_FLAG_EMBOLDEN && - (face->style_flags & FT_STYLE_FLAG_BOLD) == 0) { - FT_GlyphSlot_Embolden (glyphslot); - } #endif - - if (unscaled->x_scale == 0) - x_factor = 0; - else - x_factor = 1 / unscaled->x_scale; - - if (unscaled->y_scale == 0) - y_factor = 0; - else - y_factor = 1 / unscaled->y_scale; - - /* - * Note: the font's coordinate system is upside down from ours, so the - * Y coordinates of the bearing and advance need to be negated. - * - * Scale metrics back to glyph space from the scaled glyph space returned - * by FreeType - * - * If we want hinted metrics but aren't asking for hinted glyphs from - * FreeType, then we need to do the metric hinting ourselves. - */ - - if ((val->key.flags & PRIVATE_FLAG_HINT_METRICS) && - (val->key.flags & FT_LOAD_NO_HINTING)) { - FT_Pos x1, x2; - FT_Pos y1, y2; - FT_Pos advance; - - x1 = (metrics->horiBearingX) & -64; - x2 = (metrics->horiBearingX + metrics->width + 63) & -64; - y1 = (metrics->horiBearingY) & -64; - y2 = (metrics->horiBearingY + metrics->height + 63) & -64; - - advance = ((metrics->horiAdvance + 32) & -64); - - val->extents.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; - val->extents.y_bearing = -DOUBLE_FROM_26_6 (y1) * y_factor; - - val->extents.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; - val->extents.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; - - /* - * use untransformed advance values - * XXX uses horizontal advance only at present; should provide FT_LOAD_VERTICAL_LAYOUT - */ - val->extents.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor; - val->extents.y_advance = 0; - } else { - val->extents.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor; - val->extents.y_bearing = -DOUBLE_FROM_26_6 (metrics->horiBearingY) * y_factor; - - val->extents.width = DOUBLE_FROM_26_6 (metrics->width) * x_factor; - val->extents.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor; - - val->extents.x_advance = DOUBLE_FROM_26_6 (face->glyph->metrics.horiAdvance) * x_factor; - val->extents.y_advance = 0 * y_factor; - } - - if (glyphslot->format == FT_GLYPH_FORMAT_OUTLINE) - status = _render_glyph_outline (face, val); - else - status = _render_glyph_bitmap (face, val); - - if (val->image && - unscaled->have_shape && - (unscaled->face->face_flags & FT_FACE_FLAG_SCALABLE) == 0) - status = _transform_glyph_bitmap (val); - - FAIL: - if (status && val->image) { - cairo_surface_destroy (&val->image->base); - val->image = NULL; - } - - _cairo_ft_unscaled_font_unlock_face (unscaled); - - return status; -} static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend = { _cairo_ft_unscaled_font_destroy, +#if 0 _cairo_ft_unscaled_font_create_glyph +#endif }; /* cairo_ft_scaled_font_t */ @@ -1317,7 +1221,7 @@ static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend = { typedef struct _cairo_ft_scaled_font { cairo_scaled_font_t base; cairo_ft_unscaled_font_t *unscaled; - int load_flags; + cairo_ft_options_t ft_options; } cairo_ft_scaled_font_t; const cairo_scaled_font_backend_t cairo_ft_scaled_font_backend; @@ -1325,26 +1229,29 @@ const cairo_scaled_font_backend_t cairo_ft_scaled_font_backend; /* The load flags passed to FT_Load_Glyph control aspects like hinting and * antialiasing. Here we compute them from the fields of a FcPattern. */ -static int -_get_pattern_load_flags (FcPattern *pattern) +static cairo_ft_options_t +_get_pattern_ft_options (FcPattern *pattern) { FcBool antialias, vertical_layout, hinting, autohint; + cairo_ft_options_t ft_options; int rgba; #ifdef FC_HINT_STYLE int hintstyle; #endif - int load_flags = 0; int target_flags = 0; + ft_options.load_flags = 0; + ft_options.extra_flags = 0; + /* disable antialiasing if requested */ if (FcPatternGetBool (pattern, FC_ANTIALIAS, 0, &antialias) != FcResultMatch) antialias = FcTrue; if (antialias) - load_flags |= FT_LOAD_NO_BITMAP; + ft_options.load_flags |= FT_LOAD_NO_BITMAP; else - load_flags |= FT_LOAD_MONOCHROME; + ft_options.load_flags |= FT_LOAD_MONOCHROME; /* disable hinting if requested */ if (FcPatternGetBool (pattern, @@ -1356,7 +1263,7 @@ _get_pattern_load_flags (FcPattern *pattern) hintstyle = FC_HINT_FULL; if (!hinting || hintstyle == FC_HINT_NONE) - load_flags |= FT_LOAD_NO_HINTING; + ft_options.load_flags |= FT_LOAD_NO_HINTING; if (antialias) { switch (hintstyle) { @@ -1397,7 +1304,7 @@ _get_pattern_load_flags (FcPattern *pattern) break; } - load_flags |= target_flags; + ft_options.load_flags |= target_flags; /* force autohinting if requested */ if (FcPatternGetBool (pattern, @@ -1405,14 +1312,14 @@ _get_pattern_load_flags (FcPattern *pattern) autohint = FcFalse; if (autohint) - load_flags |= FT_LOAD_FORCE_AUTOHINT; + ft_options.load_flags |= FT_LOAD_FORCE_AUTOHINT; if (FcPatternGetBool (pattern, FC_VERTICAL_LAYOUT, 0, &vertical_layout) != FcResultMatch) vertical_layout = FcFalse; if (vertical_layout) - load_flags |= FT_LOAD_VERTICAL_LAYOUT; + ft_options.load_flags |= FT_LOAD_VERTICAL_LAYOUT; #ifdef FC_EMBOLDEN { @@ -1423,11 +1330,11 @@ _get_pattern_load_flags (FcPattern *pattern) embolden = FcFalse; if (embolden) - load_flags |= PRIVATE_FLAG_EMBOLDEN; + ft_options.extra_flags |= CAIRO_FT_OPTIONS_EMBOLDEN; } #endif - return load_flags; + return ft_options; } static int @@ -1486,27 +1393,75 @@ _cairo_ft_scaled_font_create (cairo_ft_unscaled_font_t *unscaled, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, - int load_flags) + cairo_ft_options_t ft_options) { cairo_ft_scaled_font_t *scaled_font = NULL; + FT_Face face; + FT_Size_Metrics *metrics; - scaled_font = malloc (sizeof(cairo_ft_scaled_font_t)); - if (scaled_font == NULL) + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) return NULL; + + scaled_font = malloc (sizeof(cairo_ft_scaled_font_t)); + if (scaled_font == NULL) { + _cairo_ft_unscaled_font_unlock_face (unscaled); + return NULL; + } + + _cairo_unscaled_font_reference (&unscaled->base); + scaled_font->unscaled = unscaled; + + if (options->hint_metrics != CAIRO_HINT_METRICS_OFF) + ft_options.extra_flags |= CAIRO_FT_OPTIONS_HINT_METRICS; + + scaled_font->ft_options = ft_options; _cairo_scaled_font_init (&scaled_font->base, font_face, font_matrix, ctm, options, &cairo_ft_scaled_font_backend); - _cairo_unscaled_font_reference (&unscaled->base); - scaled_font->unscaled = unscaled; + _cairo_ft_unscaled_font_set_scale (unscaled, + &scaled_font->base.scale); - if (options->hint_metrics != CAIRO_HINT_METRICS_OFF) - load_flags |= PRIVATE_FLAG_HINT_METRICS; + metrics = &face->size->metrics; - scaled_font->load_flags = load_flags; + /* + * Get to unscaled metrics so that the upper level can get back to + * user space + */ + if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { + double x_factor, y_factor; + if (unscaled->x_scale == 0) + x_factor = 0; + else + x_factor = 1 / unscaled->x_scale; + + if (unscaled->y_scale == 0) + y_factor = 0; + else + y_factor = 1 / unscaled->y_scale; + + scaled_font->base.extents.ascent = DOUBLE_FROM_26_6(metrics->ascender) * y_factor; + scaled_font->base.extents.descent = DOUBLE_FROM_26_6(- metrics->descender) * y_factor; + scaled_font->base.extents.height = DOUBLE_FROM_26_6(metrics->height) * y_factor; + scaled_font->base.extents.max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) * x_factor; + } else { + double scale = face->units_per_EM; + + scaled_font->base.extents.ascent = face->ascender / scale; + scaled_font->base.extents.descent = - face->descender / scale; + scaled_font->base.extents.height = face->height / scale; + scaled_font->base.extents.max_x_advance = face->max_advance_width / scale; + } + + + /* FIXME: this doesn't do vertical layout atm. */ + scaled_font->base.extents.max_y_advance = 0.0; + + return &scaled_font->base; } @@ -1520,7 +1475,7 @@ static cairo_status_t _cairo_ft_scaled_font_create_toy (cairo_toy_font_face_t *toy_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, - const cairo_font_options_t *options, + const cairo_font_options_t *font_options, cairo_scaled_font_t **font) { FcPattern *pattern, *resolved; @@ -1531,7 +1486,7 @@ _cairo_ft_scaled_font_create_toy (cairo_toy_font_face_t *toy_face, int fcweight; cairo_matrix_t scale; cairo_ft_font_transform_t sf; - int load_flags; + cairo_ft_options_t ft_options; unsigned char *family = (unsigned char*) toy_face->family; pattern = FcPatternCreate (); @@ -1576,7 +1531,7 @@ _cairo_ft_scaled_font_create_toy (cairo_toy_font_face_t *toy_face, FcPatternAddInteger (pattern, FC_PIXEL_SIZE, sf.y_scale); FcConfigSubstitute (NULL, pattern, FcMatchPattern); - cairo_ft_font_options_substitute (options, pattern); + cairo_ft_font_options_substitute (font_options, pattern); FcDefaultSubstitute (pattern); resolved = FcFontMatch (NULL, pattern, &result); @@ -1587,12 +1542,12 @@ _cairo_ft_scaled_font_create_toy (cairo_toy_font_face_t *toy_face, if (!unscaled) goto FREE_RESOLVED; - load_flags = _get_pattern_load_flags (resolved); + ft_options = _get_pattern_ft_options (resolved); new_font = _cairo_ft_scaled_font_create (unscaled, &toy_face->base, font_matrix, ctm, - options, load_flags); + font_options, ft_options); _cairo_unscaled_font_destroy (&unscaled->base); @@ -1621,452 +1576,6 @@ _cairo_ft_scaled_font_fini (void *abstract_font) _cairo_unscaled_font_destroy (&scaled_font->unscaled->base); } -static void -_cairo_ft_scaled_font_get_glyph_cache_key (void *abstract_font, - cairo_glyph_cache_key_t *key) -{ - cairo_ft_scaled_font_t *scaled_font = abstract_font; - - key->unscaled = &scaled_font->unscaled->base; - key->scale = scaled_font->base.scale; - key->flags = scaled_font->load_flags; -} - -static cairo_status_t -_cairo_ft_scaled_font_text_to_glyphs (void *abstract_font, - const char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs) -{ - double x = 0., y = 0.; - size_t i; - uint32_t *ucs4 = NULL; - cairo_ft_scaled_font_t *scaled_font = abstract_font; - FT_Face face; - cairo_glyph_cache_key_t key; - cairo_image_glyph_cache_entry_t *val; - cairo_cache_t *cache = NULL; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - _cairo_ft_scaled_font_get_glyph_cache_key (scaled_font, &key); - - status = _cairo_utf8_to_ucs4 ((unsigned char*)utf8, -1, &ucs4, num_glyphs); - if (status) - return status; - - face = cairo_ft_scaled_font_lock_face (&scaled_font->base); - if (!face) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL1; - } - - _cairo_lock_global_image_glyph_cache (); - cache = _cairo_get_global_image_glyph_cache (); - if (cache == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL2; - } - - *glyphs = (cairo_glyph_t *) malloc ((*num_glyphs) * (sizeof (cairo_glyph_t))); - if (*glyphs == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto FAIL2; - } - - for (i = 0; i < *num_glyphs; i++) - { - (*glyphs)[i].index = FT_Get_Char_Index (face, ucs4[i]); - (*glyphs)[i].x = x; - (*glyphs)[i].y = y; - - val = NULL; - key.index = (*glyphs)[i].index; - - if (_cairo_cache_lookup (cache, &key, (void **) &val, NULL) - != CAIRO_STATUS_SUCCESS || val == NULL) - continue; - - x += val->extents.x_advance; - y += val->extents.y_advance; - } - - FAIL2: - if (cache) - _cairo_unlock_global_image_glyph_cache (); - - cairo_ft_scaled_font_unlock_face (&scaled_font->base); - - FAIL1: - free (ucs4); - - return status; -} - - -static cairo_status_t -_cairo_ft_scaled_font_font_extents (void *abstract_font, - cairo_font_extents_t *extents) -{ - cairo_ft_scaled_font_t *scaled_font = abstract_font; - FT_Face face; - FT_Size_Metrics *metrics; - - face = _cairo_ft_unscaled_font_lock_face (scaled_font->unscaled); - if (!face) - return CAIRO_STATUS_NO_MEMORY; - - metrics = &face->size->metrics; - - _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, - &scaled_font->base.scale); - - /* - * Get to unscaled metrics so that the upper level can get back to - * user space - */ - if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { - double x_factor, y_factor; - - if (scaled_font->unscaled->x_scale == 0) - x_factor = 0; - else - x_factor = 1 / scaled_font->unscaled->x_scale; - - if (scaled_font->unscaled->y_scale == 0) - y_factor = 0; - else - y_factor = 1 / scaled_font->unscaled->y_scale; - - extents->ascent = DOUBLE_FROM_26_6(metrics->ascender) * y_factor; - extents->descent = DOUBLE_FROM_26_6(- metrics->descender) * y_factor; - extents->height = DOUBLE_FROM_26_6(metrics->height) * y_factor; - extents->max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) * x_factor; - } else { - double scale = face->units_per_EM; - - extents->ascent = face->ascender / scale; - extents->descent = - face->descender / scale; - extents->height = face->height / scale; - extents->max_x_advance = face->max_advance_width / scale; - } - - /* FIXME: this doesn't do vertical layout atm. */ - extents->max_y_advance = 0.0; - - _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_ft_scaled_font_glyph_extents (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - int i; - cairo_ft_scaled_font_t *scaled_font = abstract_font; - cairo_point_double_t origin; - cairo_point_double_t glyph_min, glyph_max; - /* Initialize just to squelch anti-helpful compiler warning. */ - cairo_point_double_t total_min = { 0, 0}, total_max = {0,0}; - - cairo_image_glyph_cache_entry_t *img = NULL; - cairo_cache_t *cache; - cairo_glyph_cache_key_t key; - - if (num_glyphs == 0) - { - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; - - return CAIRO_STATUS_SUCCESS; - } - - origin.x = glyphs[0].x; - origin.y = glyphs[0].y; - - _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; - } - - _cairo_ft_scaled_font_get_glyph_cache_key (scaled_font, &key); - - for (i = 0; i < num_glyphs; i++) - { - img = NULL; - key.index = glyphs[i].index; - if (_cairo_cache_lookup (cache, &key, (void **) &img, NULL) - != 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_face_create_for_ft_face accept an - FcPattern. */ - 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; - total_max = glyph_max; - } else { - if (glyph_min.x < total_min.x) - total_min.x = glyph_min.x; - if (glyph_min.y < total_min.y) - total_min.y = glyph_min.y; - - if (glyph_max.x > total_max.x) - total_max.x = glyph_max.x; - if (glyph_max.y > total_max.y) - 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 + (img == NULL ? 0 : img->extents.x_advance) - origin.x; - extents->y_advance = glyphs[i-1].y + (img == NULL ? 0 : img->extents.y_advance) - origin.y; - - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_status_t -_cairo_ft_scaled_font_glyph_bbox (void *abstract_font, - 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_scaled_font_t *scaled_font = abstract_font; - - cairo_fixed_t x1, y1, x2, y2; - int i; - - bbox->p1.x = bbox->p1.y = CAIRO_MAXSHORT << 16; - bbox->p2.x = bbox->p2.y = CAIRO_MINSHORT << 16; - - _cairo_lock_global_image_glyph_cache (); - cache = _cairo_get_global_image_glyph_cache(); - - if (cache == NULL - || scaled_font == NULL - || glyphs == NULL) { - _cairo_unlock_global_image_glyph_cache (); - return CAIRO_STATUS_NO_MEMORY; - } - - _cairo_ft_scaled_font_get_glyph_cache_key (scaled_font, &key); - - for (i = 0; i < num_glyphs; i++) - { - - img = NULL; - key.index = glyphs[i].index; - - if (_cairo_cache_lookup (cache, &key, (void **) &img, NULL) - != CAIRO_STATUS_SUCCESS || img == NULL) - continue; - - 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; - - if (y1 < bbox->p1.y) - bbox->p1.y = y1; - - if (x2 > bbox->p2.x) - bbox->p2.x = x2; - - if (y2 > bbox->p2.y) - bbox->p2.y = y2; - } - _cairo_unlock_global_image_glyph_cache (); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_format_t -_select_text_mask_format (cairo_bool_t have_a1_glyphs, - cairo_bool_t have_a8_glyphs, - cairo_bool_t have_argb32_glyphs) -{ - if (have_a8_glyphs) - return CAIRO_FORMAT_A8; - - if (have_a1_glyphs && have_argb32_glyphs) - return CAIRO_FORMAT_A8; - - if (have_a1_glyphs) - return CAIRO_FORMAT_A1; - - if (have_argb32_glyphs) - return CAIRO_FORMAT_ARGB32; - - /* when there are no glyphs to draw, just pick something */ - return CAIRO_FORMAT_A8; -} - -static cairo_status_t -_cairo_ft_scaled_font_show_glyphs (void *abstract_font, - cairo_operator_t operator, - cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - const cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_image_glyph_cache_entry_t **entries; - cairo_cache_t *cache; - cairo_glyph_cache_key_t key; - cairo_ft_scaled_font_t *scaled_font = abstract_font; - cairo_surface_pattern_t glyph_pattern; - cairo_surface_t *mask; - cairo_surface_pattern_t mask_pattern; - cairo_format_t mask_format = CAIRO_FORMAT_A1; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_bool_t have_a1_glyphs, have_a8_glyphs, have_argb32_glyphs; - int x, y; - int i; - - _cairo_lock_global_image_glyph_cache (); - cache = _cairo_get_global_image_glyph_cache(); - - if (cache == NULL - || scaled_font == NULL - || pattern == NULL - || surface == NULL - || glyphs == NULL) { - _cairo_unlock_global_image_glyph_cache (); - return CAIRO_STATUS_NO_MEMORY; - } - - key.unscaled = &scaled_font->unscaled->base; - key.scale = scaled_font->base.scale; - key.flags = scaled_font->load_flags; - - entries = malloc (num_glyphs * sizeof (cairo_image_glyph_cache_entry_t)); - if (!entries) - goto CLEANUP_CACHE; - - have_a1_glyphs = FALSE; - have_a8_glyphs = FALSE; - have_argb32_glyphs = FALSE; - - for (i = 0; i < num_glyphs; i++) - { - entries[i] = NULL; - key.index = glyphs[i].index; - - if (_cairo_cache_lookup (cache, &key, (void **) &entries[i], NULL) != CAIRO_STATUS_SUCCESS) - continue; - - switch (entries[i]->image->format) { - case CAIRO_FORMAT_A1: - have_a1_glyphs = TRUE; - break; - case CAIRO_FORMAT_A8: - have_a8_glyphs = TRUE; - break; - case CAIRO_FORMAT_ARGB32: - have_argb32_glyphs = TRUE; - break; - default: - break; - } - } - - mask_format = _select_text_mask_format (have_a1_glyphs, have_a8_glyphs, have_argb32_glyphs); - - mask = cairo_image_surface_create (mask_format, width, height); - if (!mask) - goto CLEANUP_ENTRIES; - - status = _cairo_surface_fill_rectangle (mask, CAIRO_OPERATOR_CLEAR, - CAIRO_COLOR_TRANSPARENT, - 0, 0, width, height); - if (status) - goto CLEANUP_MASK; - - for (i = 0; i < num_glyphs; i++) - { - if (entries[i] == NULL - || entries[i]->image == NULL) - continue; - - x = (int) floor (glyphs[i].x + 0.5); - y = (int) floor (glyphs[i].y + 0.5); - - _cairo_pattern_init_for_surface (&glyph_pattern, &(entries[i]->image->base)); - - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, &glyph_pattern.base, - NULL, - mask, - 0, 0, - 0, 0, - x + entries[i]->size.x - dest_x, - y + entries[i]->size.y - dest_y, - entries[i]->size.width, - entries[i]->size.height); - - _cairo_pattern_fini (&glyph_pattern.base); - - if (status) - goto CLEANUP_MASK; - } - - if (mask_format == CAIRO_FORMAT_ARGB32) - pixman_image_set_component_alpha (((cairo_image_surface_t *)mask)->pixman_image, TRUE); - - _cairo_pattern_init_for_surface (&mask_pattern, mask); - - status = _cairo_surface_composite (operator, pattern, &mask_pattern.base, - surface, - source_x, source_y, - 0, 0, - dest_x, dest_y, - width, height); - - _cairo_pattern_fini (&mask_pattern.base); - - CLEANUP_MASK: - cairo_surface_destroy (mask); - - CLEANUP_ENTRIES: - free (entries); - - CLEANUP_CACHE: - _cairo_unlock_global_image_glyph_cache (); - - return status; -} - - static int _move_to (FT_Vector *to, void *closure) { @@ -2156,17 +1665,11 @@ _cubic_to (FT_Vector *control1, FT_Vector *control2, } static cairo_status_t -_cairo_ft_scaled_font_glyph_path (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_fixed_t *path) +_decompose_glyph_outline (FT_Face face, + cairo_font_options_t *options, + cairo_path_fixed_t **pathp) { - int i; - cairo_ft_scaled_font_t *scaled_font = abstract_font; - FT_GlyphSlot glyph; - FT_Face face; - FT_Error error; - FT_Outline_Funcs outline_funcs = { + static const FT_Outline_Funcs outline_funcs = { _move_to, _line_to, _conic_to, @@ -2174,62 +1677,238 @@ _cairo_ft_scaled_font_glyph_path (void *abstract_font, 0, /* shift */ 0, /* delta */ }; + static const FT_Matrix invert_y = { + DOUBLE_TO_16_16 (1.0), 0, + 0, DOUBLE_TO_16_16 (-1.0), + }; - face = cairo_ft_scaled_font_lock_face (abstract_font); - if (!face) + FT_GlyphSlot glyph; + cairo_path_fixed_t *path; + + path = _cairo_path_fixed_create (); + if (!path) return CAIRO_STATUS_NO_MEMORY; glyph = face->glyph; - for (i = 0; i < num_glyphs; i++) - { - FT_Matrix invert_y = { - DOUBLE_TO_16_16 (1.0), 0, - 0, DOUBLE_TO_16_16 (-1.0), - }; - - error = FT_Load_Glyph (scaled_font->unscaled->face, glyphs[i].index, - (scaled_font->load_flags & ~PRIVATE_FLAGS_MASK) | FT_LOAD_NO_BITMAP); - /* XXX: What to do in this error case? */ - if (error) - continue; - /* XXX: Do we want to support bitmap fonts here? */ - if (glyph->format == ft_glyph_format_bitmap) - continue; - -#if HAVE_FT_GLYPHSLOT_EMBOLDEN - /* - * embolden glyhps if required - */ - if (scaled_font->load_flags & PRIVATE_FLAG_EMBOLDEN && - (face->style_flags & FT_STYLE_FLAG_BOLD) == 0) - FT_GlyphSlot_Embolden (glyph); -#endif - - /* Font glyphs have an inverted Y axis compared to cairo. */ - FT_Outline_Transform (&glyph->outline, &invert_y); - FT_Outline_Translate (&glyph->outline, - DOUBLE_TO_26_6(glyphs[i].x), - DOUBLE_TO_26_6(glyphs[i].y)); - FT_Outline_Decompose (&glyph->outline, &outline_funcs, path); - } + /* Font glyphs have an inverted Y axis compared to cairo. */ + FT_Outline_Transform (&glyph->outline, &invert_y); + FT_Outline_Decompose (&glyph->outline, &outline_funcs, path); + _cairo_path_fixed_close_path (path); + *pathp = path; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ft_scaled_glyph_init (void *abstract_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) +{ + cairo_text_extents_t fs_metrics; + cairo_ft_scaled_font_t *scaled_font = abstract_font; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + FT_GlyphSlot glyph; + FT_Face face; + FT_Error error; + int load_flags = scaled_font->ft_options.load_flags; + FT_Glyph_Metrics *metrics; + double x_factor, y_factor; + + face = cairo_ft_scaled_font_lock_face (abstract_font); + if (!face) + return CAIRO_STATUS_NO_MEMORY; + + if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 && + (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) == 0) + load_flags |= FT_LOAD_NO_BITMAP; + + error = FT_Load_Glyph (scaled_font->unscaled->face, + _cairo_scaled_glyph_index(scaled_glyph), + load_flags); + + if (error) { + cairo_ft_scaled_font_unlock_face (abstract_font); + return CAIRO_STATUS_NO_MEMORY; + } + + glyph = face->glyph; + +#if HAVE_FT_GLYPHSLOT_EMBOLDEN + /* + * embolden glyphs if requested + */ + if (scaled_font->ft_options.extra_flags & CAIRO_FT_OPTIONS_EMBOLDEN) + FT_GlyphSlot_Embolden (glyph); +#endif + + if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) { + /* + * Compute font-space metrics + */ + metrics = &glyph->metrics; + + if (unscaled->x_scale == 0) + x_factor = 0; + else + x_factor = 1 / unscaled->x_scale; + + if (unscaled->y_scale == 0) + y_factor = 0; + else + y_factor = 1 / unscaled->y_scale; + + /* + * Note: the font's coordinate system is upside down from ours, so the + * Y coordinates of the bearing and advance need to be negated. + * + * Scale metrics back to glyph space from the scaled glyph space returned + * by FreeType + * + * If we want hinted metrics but aren't asking for hinted glyphs from + * FreeType, then we need to do the metric hinting ourselves. + */ + + if ((scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) && + (load_flags & FT_LOAD_NO_HINTING)) + { + FT_Pos x1, x2; + FT_Pos y1, y2; + FT_Pos advance; + + x1 = (metrics->horiBearingX) & -64; + x2 = (metrics->horiBearingX + metrics->width + 63) & -64; + y1 = (-metrics->horiBearingY) & -64; + y2 = (-metrics->horiBearingY + metrics->height + 63) & -64; + + advance = ((metrics->horiAdvance + 32) & -64); + + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; + + fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; + fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; + + /* + * use untransformed advance values + * XXX uses horizontal advance only at present; should provide FT_LOAD_VERTICAL_LAYOUT + */ + fs_metrics.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor; + fs_metrics.y_advance = 0; + } else { + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor; + + fs_metrics.width = DOUBLE_FROM_26_6 (metrics->width) * x_factor; + fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor; + + fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor; + fs_metrics.y_advance = 0 * y_factor; + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &fs_metrics); + } + + if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) { + cairo_image_surface_t *surface; + cairo_status_t status; + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) + status = _render_glyph_outline (face, &scaled_font->base.options, + &surface); + else + status = _render_glyph_bitmap (face, &scaled_font->base.options, + &surface); + if (status) { + cairo_ft_scaled_font_unlock_face (abstract_font); + return status; + } + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + surface); + } + + if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { + cairo_path_fixed_t *path; + cairo_status_t status; + + /* + * A kludge -- the above code will trash the outline, + * so reload it. This will probably never occur though + */ + if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) { + error = FT_Load_Glyph (face, + _cairo_scaled_glyph_index(scaled_glyph), + load_flags | FT_LOAD_NO_BITMAP); + + if (error) { + cairo_ft_scaled_font_unlock_face (abstract_font); + return CAIRO_STATUS_NO_MEMORY; + } + } + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) + status = _decompose_glyph_outline (face, &scaled_font->base.options, + &path); + else + status = CAIRO_STATUS_NO_MEMORY; + + if (status) { + cairo_ft_scaled_font_unlock_face (abstract_font); + return status; + } + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + } + cairo_ft_scaled_font_unlock_face (abstract_font); return CAIRO_STATUS_SUCCESS; } +static unsigned long +_cairo_ft_ucs4_to_index (void *abstract_font, + uint32_t ucs4) +{ + cairo_ft_scaled_font_t *scaled_font = abstract_font; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + FT_Face face; + FT_UInt index; + + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return 0; + index = FT_Get_Char_Index (face, ucs4); + _cairo_ft_unscaled_font_unlock_face (unscaled); + return index; +} + +static cairo_int_status_t +_cairo_ft_show_glyphs (void *abstract_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + const cairo_scaled_font_backend_t cairo_ft_scaled_font_backend = { _cairo_ft_scaled_font_create_toy, _cairo_ft_scaled_font_fini, - _cairo_ft_scaled_font_font_extents, - _cairo_ft_scaled_font_text_to_glyphs, - _cairo_ft_scaled_font_glyph_extents, - _cairo_ft_scaled_font_glyph_bbox, - _cairo_ft_scaled_font_show_glyphs, - _cairo_ft_scaled_font_glyph_path, - _cairo_ft_scaled_font_get_glyph_cache_key + _cairo_ft_scaled_glyph_init, + _cairo_ft_ucs4_to_index, + _cairo_ft_show_glyphs, }; /* cairo_ft_font_face_t */ @@ -2300,7 +1979,7 @@ _cairo_ft_font_face_scaled_font_create (void *abstract_face, cairo_scaled_font_t **scaled_font) { cairo_ft_font_face_t *font_face = abstract_face; - int load_flags; + cairo_ft_options_t ft_options; /* The handling of font options is different depending on how the * font face was created. When the user creates a font face with @@ -2311,15 +1990,15 @@ _cairo_ft_font_face_scaled_font_create (void *abstract_face, * cairo_ft_font_options_substitute(), so *just* use those load * flags and ignore the options. */ + + ft_options = font_face->ft_options; if (font_face->unscaled->from_face) - load_flags = _get_options_load_flags (options) | font_face->load_flags; - else - load_flags = font_face->load_flags; + ft_options.load_flags |= _get_options_load_flags (options); *scaled_font = _cairo_ft_scaled_font_create (font_face->unscaled, &font_face->base, font_matrix, ctm, - options, load_flags); + options, ft_options); if (*scaled_font) return CAIRO_STATUS_SUCCESS; else @@ -2333,7 +2012,7 @@ static const cairo_font_face_backend_t _cairo_ft_font_face_backend = { static cairo_font_face_t * _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, - int load_flags) + cairo_ft_options_t ft_options) { cairo_ft_font_face_t *font_face; @@ -2342,7 +2021,8 @@ _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, font_face; font_face = font_face->next) { - if (font_face->load_flags == load_flags) + if (font_face->ft_options.load_flags == ft_options.load_flags && + font_face->ft_options.extra_flags == ft_options.extra_flags) return cairo_font_face_reference (&font_face->base); } @@ -2354,7 +2034,7 @@ _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, font_face->unscaled = unscaled; _cairo_unscaled_font_reference (&unscaled->base); - font_face->load_flags = load_flags; + font_face->ft_options = ft_options; font_face->next = unscaled->faces; unscaled->faces = font_face; @@ -2491,7 +2171,7 @@ cairo_ft_font_face_create_for_pattern (FcPattern *pattern) } font_face = _cairo_ft_font_face_create (unscaled, - _get_pattern_load_flags (pattern)); + _get_pattern_ft_options (pattern)); _cairo_unscaled_font_destroy (&unscaled->base); if (font_face) @@ -2533,6 +2213,7 @@ cairo_ft_font_face_create_for_ft_face (FT_Face face, { cairo_ft_unscaled_font_t *unscaled; cairo_font_face_t *font_face; + cairo_ft_options_t ft_options; unscaled = _cairo_ft_unscaled_font_create_from_face (face); if (unscaled == NULL) { @@ -2540,7 +2221,10 @@ cairo_ft_font_face_create_for_ft_face (FT_Face face, return (cairo_font_face_t *)&_cairo_font_face_nil; } - font_face = _cairo_ft_font_face_create (unscaled, load_flags); + ft_options.load_flags = load_flags; + ft_options.extra_flags = 0; + + font_face = _cairo_ft_font_face_create (unscaled, ft_options); _cairo_unscaled_font_destroy (&unscaled->base); if (font_face) { diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 398323395..0d554a2eb 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -1950,30 +1950,17 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, int *num_glyphs) { cairo_status_t status; - int i; status = _cairo_gstate_ensure_scaled_font (gstate); if (status) return status; - status = _cairo_scaled_font_text_to_glyphs (gstate->scaled_font, + status = _cairo_scaled_font_text_to_glyphs (gstate->scaled_font, x, y, utf8, glyphs, num_glyphs); if (status || !glyphs || !num_glyphs || !(*glyphs) || !(num_glyphs)) return status; - /* The font responded in glyph space, starting from (0,0). Convert to - user space by applying the font transform, then add any current point - offset. */ - - for (i = 0; i < *num_glyphs; ++i) { - cairo_matrix_transform_point (&gstate->font_matrix, - &((*glyphs)[i].x), - &((*glyphs)[i].y)); - (*glyphs)[i].x += x; - (*glyphs)[i].y += y; - } - return CAIRO_STATUS_SUCCESS; } @@ -2050,6 +2037,17 @@ _cairo_gstate_show_glyphs_draw_func (void *closure, if (!src) src = &pattern.base; + status = _cairo_surface_show_glyphs (glyph_info->font, operator, src, + dst, + extents->x, extents->y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height, + glyph_info->glyphs, + glyph_info->num_glyphs); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + status = _cairo_scaled_font_show_glyphs (glyph_info->font, operator, src, dst, @@ -2074,7 +2072,6 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, int i; cairo_glyph_t *transformed_glyphs = NULL; cairo_pattern_union_t pattern; - cairo_box_t bbox; cairo_rectangle_t extents; cairo_show_glyphs_info_t glyph_info; @@ -2102,21 +2099,15 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, } if (_cairo_operator_bounded (gstate->operator)) - { - status = _cairo_scaled_font_glyph_bbox (gstate->scaled_font, - transformed_glyphs, num_glyphs, - &bbox); - if (status) - goto CLEANUP_GLYPHS; - - _cairo_box_round_to_rectangle (&bbox, &extents); - } + status = _cairo_scaled_font_glyph_device_extents (gstate->scaled_font, + transformed_glyphs, + num_glyphs, + &extents); else - { status = _cairo_surface_get_extents (gstate->target, &extents); - if (status) - goto CLEANUP_GLYPHS; - } + + if (status) + goto CLEANUP_GLYPHS; status = _cairo_clip_intersect_to_rectangle (&gstate->clip, &extents); if (status) diff --git a/src/cairo-path.c b/src/cairo-path.c index 0940c4d1e..669e587ac 100644 --- a/src/cairo-path.c +++ b/src/cairo-path.c @@ -132,6 +132,17 @@ _cairo_path_fixed_init_copy (cairo_path_fixed_t *path, return CAIRO_STATUS_SUCCESS; } +cairo_path_fixed_t * +_cairo_path_fixed_create (void) +{ + cairo_path_fixed_t *path = malloc (sizeof (cairo_path_fixed_t)); + + if (!path) + return NULL; + _cairo_path_fixed_init (path); + return path; +} + void _cairo_path_fixed_fini (cairo_path_fixed_t *path) { @@ -155,6 +166,13 @@ _cairo_path_fixed_fini (cairo_path_fixed_t *path) path->has_current_point = 0; } +void +_cairo_path_fixed_destroy (cairo_path_fixed_t *path) +{ + _cairo_path_fixed_fini (path); + free (path); +} + cairo_status_t _cairo_path_fixed_move_to (cairo_path_fixed_t *path, cairo_fixed_t x, diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c new file mode 100644 index 000000000..0744e65b5 --- /dev/null +++ b/src/cairo-scaled-font.c @@ -0,0 +1,1165 @@ +/* $Id: cairo-scaled-font.c,v 1.1 2005-08-31 22:08:02 keithp Exp $ + * + * Copyright © 2005 Keith Packard + * + * 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 Keith Packard + * + * Contributor(s): + * Keith Packard + * Carl D. Worth + * Graydon Hoare + * Owen Taylor + */ + +#include "cairoint.h" + +static cairo_bool_t +_cairo_scaled_glyph_keys_equal (void *abstract_key_a, void *abstract_key_b) +{ + cairo_scaled_glyph_t *key_a = abstract_key_a; + cairo_scaled_glyph_t *key_b = abstract_key_b; + + return (_cairo_scaled_glyph_index (key_a) == + _cairo_scaled_glyph_index (key_b)); +} + +static void +_cairo_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_scaled_font_t *scaled_font = scaled_glyph->scaled_font; + const cairo_surface_backend_t *surface_backend = scaled_font->surface_backend; + + if (surface_backend != NULL && surface_backend->scaled_glyph_fini != NULL) + surface_backend->scaled_glyph_fini (scaled_glyph, scaled_font); + if (scaled_glyph->surface != NULL) + cairo_surface_destroy (&scaled_glyph->surface->base); + if (scaled_glyph->path != NULL) + _cairo_path_fixed_destroy (scaled_glyph->path); +} + +static void +_cairo_scaled_glyph_destroy (void *abstract_glyph) +{ + cairo_scaled_glyph_t *scaled_glyph = abstract_glyph; + _cairo_scaled_glyph_fini (scaled_glyph); + free (scaled_glyph); +} + +static const cairo_scaled_font_t _cairo_scaled_font_nil = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_NO_MEMORY, /* status */ + -1, /* ref_count */ + NULL, /* font_face */ + { 1., 0., 0., 1., 0, 0}, /* font_matrix */ + { 1., 0., 0., 1., 0, 0}, /* ctm */ + { CAIRO_ANTIALIAS_DEFAULT, /* options */ + CAIRO_SUBPIXEL_ORDER_DEFAULT, + CAIRO_HINT_STYLE_DEFAULT, + CAIRO_HINT_METRICS_DEFAULT} , + { 1., 0., 0., 1., 0, 0}, /* scale */ + { 0., 0., 0., 0., 0. }, /* extents */ + NULL, /* glyphs */ + NULL, /* surface_backend */ + NULL, /* surface_private */ + CAIRO_SCALED_FONT_BACKEND_DEFAULT, +}; + +/** + * _cairo_scaled_font_set_error: + * @scaled_font: a scaled_font + * @status: a status value indicating an error, (eg. not + * CAIRO_STATUS_SUCCESS) + * + * Sets scaled_font->status to @status and calls _cairo_error; + * + * All assignments of an error status to scaled_font->status should happen + * through _cairo_scaled_font_set_error() or else _cairo_error() should be + * called immediately after the assignment. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + **/ +void +_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, + cairo_status_t status) +{ + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. It also avoids attempting + * to write to read-only data (eg. from a nil scaled_font). */ + if (scaled_font->status == CAIRO_STATUS_SUCCESS) + scaled_font->status = status; + + _cairo_error (status); +} + +/** + * cairo_scaled_font_status: + * @scaled_font: a #cairo_scaled_font_t + * + * Checks whether an error has previously occurred for this + * scaled_font. + * + * Return value: %CAIRO_STATUS_SUCCESS or another error such as + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_status_t +cairo_scaled_font_status (cairo_scaled_font_t *scaled_font) +{ + return scaled_font->status; +} + +/* Here we keep a unique mapping from + * cairo_font_face_t/matrix/ctm/options => cairo_scaled_font_t. + * + * Here are the things that we want to map: + * + * a) All otherwise referenced cairo_scaled_font_t's + * b) Some number of not otherwise referenced cairo_scaled_font_t's + * + * The implementation uses a hash table which covers (a) + * completely. Then, for (b) we have an array of otherwise + * unreferenced fonts (holdovers) which are expired in + * least-recently-used order. + * + * The cairo_scaled_font_create code gets to treat this like a regular + * hash table. All of the magic for the little holdover cache is in + * cairo_scaled_font_reference and cairo_scaled_font_destroy. + */ + +/* This defines the size of the holdover array ... that is, the number + * of scaled fonts we keep around even when not otherwise referenced + */ +#define CAIRO_SCALED_FONT_MAX_HOLDOVERS 256 + +typedef struct _cairo_scaled_font_map { + cairo_hash_table_t *hash_table; + cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS]; + int num_holdovers; +} cairo_scaled_font_map_t; + +static cairo_scaled_font_map_t *cairo_scaled_font_map = NULL; + +CAIRO_MUTEX_DECLARE (cairo_scaled_font_map_mutex); + +static int +_cairo_scaled_font_keys_equal (void *abstract_key_a, void *abstract_key_b); + +static cairo_scaled_font_map_t * +_cairo_scaled_font_map_lock (void) +{ + CAIRO_MUTEX_LOCK (cairo_scaled_font_map_mutex); + + if (cairo_scaled_font_map == NULL) { + cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t)); + if (cairo_scaled_font_map == NULL) + goto CLEANUP_MUTEX_LOCK; + + cairo_scaled_font_map->hash_table = + _cairo_hash_table_create (_cairo_scaled_font_keys_equal); + + if (cairo_scaled_font_map->hash_table == NULL) + goto CLEANUP_SCALED_FONT_MAP; + + cairo_scaled_font_map->num_holdovers = 0; + } + + return cairo_scaled_font_map; + + CLEANUP_SCALED_FONT_MAP: + free (cairo_scaled_font_map); + CLEANUP_MUTEX_LOCK: + CAIRO_MUTEX_UNLOCK (cairo_scaled_font_map_mutex); + return NULL; +} + +static void +_cairo_scaled_font_map_unlock (void) +{ + CAIRO_MUTEX_UNLOCK (cairo_scaled_font_map_mutex); +} + +void +_cairo_scaled_font_map_destroy (void) +{ + int i; + cairo_scaled_font_map_t *font_map = cairo_scaled_font_map; + cairo_scaled_font_t *scaled_font; + + if (font_map == NULL) + return; + + CAIRO_MUTEX_UNLOCK (cairo_scaled_font_map_mutex); + + for (i = 0; i < font_map->num_holdovers; i++) { + scaled_font = font_map->holdovers[i]; + /* We should only get here through the reset_static_data path + * and there had better not be any active references at that + * point. */ + assert (scaled_font->ref_count == 0); + _cairo_hash_table_remove (font_map->hash_table, + &scaled_font->hash_entry); + _cairo_scaled_font_fini (scaled_font); + free (scaled_font); + } + + _cairo_hash_table_destroy (font_map->hash_table); + + free (cairo_scaled_font_map); + cairo_scaled_font_map = NULL; +} + +/* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/) + * + * Not necessarily better than a lot of other hashes, but should be OK, and + * well tested with binary data. + */ + +#define FNV_32_PRIME ((uint32_t)0x01000193) +#define FNV1_32_INIT ((uint32_t)0x811c9dc5) + +static uint32_t +_hash_bytes_fnv (unsigned char *buffer, + int len, + uint32_t hval) +{ + while (len--) { + hval *= FNV_32_PRIME; + hval ^= *buffer++; + } + + return hval; +} + +static void +_cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font, + cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + uint32_t hash = FNV1_32_INIT; + + scaled_font->status = CAIRO_STATUS_SUCCESS; + scaled_font->font_face = font_face; + scaled_font->font_matrix = *font_matrix; + scaled_font->ctm = *ctm; + scaled_font->options = *options; + + /* We do a bytewise hash on the font matrices, ignoring the + * translation values in the ctm */ + hash = _hash_bytes_fnv ((unsigned char *)(&scaled_font->font_matrix.xx), + sizeof(cairo_matrix_t), hash); + hash = _hash_bytes_fnv ((unsigned char *)(&scaled_font->ctm.xx), + sizeof(double) * 4, hash); + + hash ^= (unsigned long) scaled_font->font_face; + + hash ^= cairo_font_options_hash (&scaled_font->options); + + scaled_font->hash_entry.hash = hash; +} + +static cairo_bool_t +_cairo_scaled_font_keys_equal (void *abstract_key_a, void *abstract_key_b) +{ + cairo_scaled_font_t *key_a = abstract_key_a; + cairo_scaled_font_t *key_b = abstract_key_b; + + return (key_a->font_face == key_b->font_face && + memcmp ((unsigned char *)(&key_a->font_matrix.xx), + (unsigned char *)(&key_b->font_matrix.xx), + sizeof(cairo_matrix_t)) == 0 && + memcmp ((unsigned char *)(&key_a->ctm.xx), + (unsigned char *)(&key_b->ctm.xx), + sizeof(double) * 4) == 0 && + cairo_font_options_equal (&key_a->options, &key_b->options)); +} + +/* + * Basic cairo_scaled_font_t object management + */ + +cairo_status_t +_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, + cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + const cairo_scaled_font_backend_t *backend) +{ + scaled_font->ref_count = 1; + + _cairo_scaled_font_init_key (scaled_font, font_face, + font_matrix, ctm, options); + + cairo_font_face_reference (font_face); + + cairo_matrix_multiply (&scaled_font->scale, + &scaled_font->font_matrix, + &scaled_font->ctm); + + scaled_font->glyphs = _cairo_cache_create (_cairo_scaled_glyph_keys_equal, + _cairo_scaled_glyph_destroy, + 256); + + scaled_font->surface_backend = NULL; + scaled_font->surface_private = NULL; + + scaled_font->backend = backend; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *fs_metrics) +{ + double font_scale_x, font_scale_y; + + _cairo_matrix_compute_scale_factors (&scaled_font->font_matrix, + &font_scale_x, &font_scale_y, + /* XXX */ 1); + + /* + * The font responded in unscaled units, scale by the font + * matrix scale factors to get to user space + */ + + scaled_font->extents.ascent = fs_metrics->ascent * font_scale_y; + scaled_font->extents.descent = fs_metrics->descent * font_scale_y; + scaled_font->extents.height = fs_metrics->height * font_scale_y; + scaled_font->extents.max_x_advance = fs_metrics->max_x_advance * font_scale_x; + scaled_font->extents.max_y_advance = fs_metrics->max_y_advance * font_scale_y; +} + +void +_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + if (scaled_font->font_face != NULL) + cairo_font_face_destroy (scaled_font->font_face); + + if (scaled_font->glyphs != NULL) + _cairo_cache_destroy (scaled_font->glyphs); + + if (scaled_font->surface_backend != NULL && + scaled_font->surface_backend->scaled_font_fini != NULL) + scaled_font->surface_backend->scaled_font_fini (scaled_font); + + scaled_font->backend->fini (scaled_font); + +} + +/** + * cairo_scaled_font_create: + * @font_face: a #cairo_font_face_t + * @font_matrix: font space to user space transformation matrix for the + * font. In the simplest case of a N point font, this matrix is + * just a scale by N, but it can also be used to shear the font + * or stretch it unequally along the two axes. See + * cairo_set_font_matrix(). + * @ctm: user to device transformation matrix with which the font will + * be used. + * @options: options to use when getting metrics for the font and + * rendering with it. + * + * Creates a #cairo_scaled_font_t object from a font face and matrices that + * describe the size of the font and the environment in which it will + * be used. + * + * Return value: a newly created #cairo_scaled_font_t. Destroy with + * cairo_scaled_font_destroy() + **/ +cairo_scaled_font_t * +cairo_scaled_font_create (cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + cairo_status_t status; + cairo_scaled_font_map_t *font_map; + cairo_scaled_font_t key, *scaled_font = NULL; + + font_map = _cairo_scaled_font_map_lock (); + if (font_map == NULL) + goto UNWIND; + + _cairo_scaled_font_init_key (&key, font_face, + font_matrix, ctm, options); + + /* Return existing scaled_font if it exists in the hash table. */ + if (_cairo_hash_table_lookup (font_map->hash_table, &key.hash_entry, + (cairo_hash_entry_t**) &scaled_font)) + { + _cairo_scaled_font_map_unlock (); + return cairo_scaled_font_reference (scaled_font); + } + + /* Otherwise create it and insert it into the hash table. */ + status = font_face->backend->scaled_font_create (font_face, font_matrix, + ctm, options, &scaled_font); + if (status) + goto UNWIND_FONT_MAP_LOCK; + + status = _cairo_hash_table_insert (font_map->hash_table, + &scaled_font->hash_entry); + if (status) + goto UNWIND_SCALED_FONT_CREATE; + + _cairo_scaled_font_map_unlock (); + + return scaled_font; + +UNWIND_SCALED_FONT_CREATE: + /* We can't call _cairo_scaled_font_destroy here since it expects + * that the font has already been successfully inserted into the + * hash table. */ + _cairo_scaled_font_fini (scaled_font); + free (scaled_font); +UNWIND_FONT_MAP_LOCK: + _cairo_scaled_font_map_unlock (); +UNWIND: + return NULL; +} + +/** + * cairo_scaled_font_reference: + * @scaled_font: a #cairo_scaled_font_t, (may be NULL in which case + * this function does nothing) + * + * Increases the reference count on @scaled_font by one. This prevents + * @scaled_font from being destroyed until a matching call to + * cairo_scaled_font_destroy() is made. + * + * Returns: the referenced #cairo_scaled_font_t + **/ +cairo_scaled_font_t * +cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font) +{ + if (scaled_font == NULL) + return NULL; + + if (scaled_font->ref_count == (unsigned int)-1) + return scaled_font; + + /* We would normally assert (scaled_font->ref_count > 0) here, but + * we are using ref_count == 0 as a legitimate case for the + * holdovers array. See below. */ + + /* If the original reference count is 0, then this font must have + * been found in font_map->holdovers, (which means this caching is + * actually working). So now we remove it from the holdovers + * array. */ + if (scaled_font->ref_count == 0) { + cairo_scaled_font_map_t *font_map; + int i; + + font_map = _cairo_scaled_font_map_lock (); + { + for (i = 0; i < font_map->num_holdovers; i++) + if (font_map->holdovers[i] == scaled_font) + break; + assert (i < font_map->num_holdovers); + + font_map->num_holdovers--; + memmove (&font_map->holdovers[i], + &font_map->holdovers[i+1], + (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*)); + } + _cairo_scaled_font_map_unlock (); + } + + scaled_font->ref_count++; + + return scaled_font; +} + +/** + * cairo_scaled_font_destroy: + * @scaled_font: a #cairo_scaled_font_t + * + * Decreases the reference count on @font by one. If the result + * is zero, then @font and all associated resources are freed. + * See cairo_scaled_font_reference(). + **/ +void +cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font) +{ + cairo_scaled_font_map_t *font_map; + + if (scaled_font == NULL) + return; + + if (scaled_font->ref_count == (unsigned int)-1) + return; + + assert (scaled_font->ref_count > 0); + + if (--(scaled_font->ref_count) > 0) + return; + + font_map = _cairo_scaled_font_map_lock (); + assert (font_map != NULL); + { + /* Rather than immediately destroying this object, we put it into + * the font_map->holdovers array in case it will get used again + * soon. To make room for it, we do actually destroy the + * least-recently-used holdover. + */ + if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) { + cairo_scaled_font_t *lru; + + lru = font_map->holdovers[0]; + assert (lru->ref_count == 0); + + _cairo_hash_table_remove (font_map->hash_table, &lru->hash_entry); + + _cairo_scaled_font_fini (lru); + free (lru); + + font_map->num_holdovers--; + memmove (&font_map->holdovers[0], + &font_map->holdovers[1], + font_map->num_holdovers * sizeof (cairo_scaled_font_t*)); + } + + font_map->holdovers[font_map->num_holdovers] = scaled_font; + font_map->num_holdovers++; + } + _cairo_scaled_font_map_unlock (); +} + +/* Public font API follows. */ + +/** + * cairo_scaled_font_extents: + * @scaled_font: a #cairo_scaled_font_t + * @extents: a #cairo_font_extents_t which to store the retrieved extents. + * + * Gets the metrics for a #cairo_scaled_font_t. + **/ +void +cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *extents) +{ + *extents = scaled_font->extents; +} + +/** + * cairo_scaled_font_glyph_extents: + * @scaled_font: a #cairo_scaled_font_t + * @glyphs: an array of glyph IDs with X and Y offsets. + * @num_glyphs: the number of glyphs in the @glyphs array + * @extents: a #cairo_text_extents_t which to store the retrieved extents. + * + * cairo_font_glyph_extents() gets the overall metrics for a string of + * glyphs. The X and Y offsets in @glyphs are taken from an origin of 0,0. + **/ +void +cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int i; + double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0; + double x_pos = 0.0, y_pos = 0.0; + + if (scaled_font->status) + return; + + if (!num_glyphs) { + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; + + return; + } + + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + double left, top, right, bottom; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (status) { + _cairo_scaled_font_set_error (scaled_font, status); + return; + } + + left = scaled_glyph->metrics.x_bearing + glyphs[i].x; + right = left + scaled_glyph->metrics.width; + top = scaled_glyph->metrics.y_bearing + glyphs[i].y; + bottom = top + scaled_glyph->metrics.height; + + if (i == 0) { + min_x = left; + max_x = right; + min_y = top; + max_y = bottom; + } else { + if (left < min_x) min_x = left; + if (right > max_x) max_x = right; + if (top < min_y) min_y = top; + if (bottom > max_y) max_y = bottom; + } + x_pos = glyphs[i].x + scaled_glyph->metrics.x_advance; + y_pos = glyphs[i].y + scaled_glyph->metrics.y_advance; + } + + extents->x_bearing = min_x - glyphs[0].x; + extents->y_bearing = min_y - glyphs[0].y; + extents->width = max_x - min_x; + extents->height = max_y - min_y; + extents->x_advance = x_pos - glyphs[0].x; + extents->y_advance = y_pos - glyphs[0].y; +} + +cairo_status_t +_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, + double x, + double y, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) +{ + size_t i; + uint32_t *ucs4 = NULL; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_utf8_to_ucs4 ((unsigned char*)utf8, -1, &ucs4, num_glyphs); + if (status) + return status; + + *glyphs = (cairo_glyph_t *) malloc ((*num_glyphs) * (sizeof (cairo_glyph_t))); + + if (*glyphs == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + + for (i = 0; i < *num_glyphs; i++) { + (*glyphs)[i].index = (*scaled_font->backend-> + ucs4_to_index) (scaled_font, ucs4[i]); + (*glyphs)[i].x = x; + (*glyphs)[i].y = y; + + status = _cairo_scaled_glyph_lookup (scaled_font, + (*glyphs)[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (status) { + free (*glyphs); + goto FAIL; + } + + x += scaled_glyph->metrics.x_advance; + y += scaled_glyph->metrics.y_advance; + } + + FAIL: + free (ucs4); + + return status; +} + +/* + * Compute a device-space bounding box for the glyphs. + */ +cairo_status_t +_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_rectangle_t *extents) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int i; + int min_x = CAIRO_MAXSHORT, max_x = CAIRO_MINSHORT; + int min_y = CAIRO_MAXSHORT, max_y = CAIRO_MINSHORT; + + if (scaled_font->status) + return scaled_font->status; + + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + int left, top; + int right, bottom; + int x, y; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (status) { + _cairo_scaled_font_set_error (scaled_font, status); + return status; + } + + /* glyph images are snapped to pixel locations */ + x = (int) floor (glyphs[i].x + 0.5); + y = (int) floor (glyphs[i].y + 0.5); + + left = x + _cairo_fixed_integer_floor(scaled_glyph->bbox.p1.x); + top = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + right = x + _cairo_fixed_integer_ceil(scaled_glyph->bbox.p2.x); + bottom = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); + + if (left < min_x) min_x = left; + if (right > max_x) max_x = right; + if (top < min_y) min_y = top; + if (bottom > max_y) max_y = bottom; + } + if (min_x < max_x && min_y < max_y) { + extents->x = min_x; + extents->width = max_x - min_x; + extents->y = min_y; + extents->height = max_y - min_y; + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_status_t status; + cairo_surface_t *mask = NULL; + int i; + + /* These operators aren't interpreted the same way by the backends; + * they are implemented in terms of other operators in cairo-gstate.c + */ + assert (operator != CAIRO_OPERATOR_SOURCE && operator != CAIRO_OPERATOR_CLEAR); + + if (scaled_font->status) + return scaled_font->status; + + if (scaled_font->backend->show_glyphs != NULL) { + status = scaled_font->backend->show_glyphs (scaled_font, + operator, pattern, + surface, + source_x, source_y, + dest_x, dest_y, + width, height, + glyphs, num_glyphs); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + /* Font display routine either does not exist or failed. */ + + status = CAIRO_STATUS_SUCCESS; + + _cairo_cache_preserve (scaled_font->glyphs); + + for (i = 0; i < num_glyphs; i++) { + int x, y; + cairo_surface_pattern_t glyph_pattern; + cairo_image_surface_t *glyph_surface; + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + + if (status) + goto CLEANUP_MASK; + + glyph_surface = scaled_glyph->surface; + + /* Create the mask using the format from the first glyph */ + if (mask == NULL) { + mask = cairo_image_surface_create (glyph_surface->format, + width, height); + if (mask->status) { + status = mask->status; + goto CLEANUP_MASK; + } + + status = _cairo_surface_fill_rectangle (mask, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + 0, 0, + width, height); + if (status) + goto CLEANUP_MASK; + if (glyph_surface->format == CAIRO_FORMAT_ARGB32) + pixman_image_set_component_alpha (((cairo_image_surface_t*) mask)-> + pixman_image, TRUE); + + } + + /* round glyph locations to the nearest pixel */ + x = (int) floor (glyphs[i].x + + glyph_surface->base.device_x_offset + + 0.5); + y = (int) floor (glyphs[i].y + + glyph_surface->base.device_y_offset + + 0.5); + + _cairo_pattern_init_for_surface (&glyph_pattern, &glyph_surface->base); + + status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + &glyph_pattern.base, + NULL, + mask, + 0, 0, + 0, 0, + x - dest_x, + y - dest_y, + glyph_surface->width, + glyph_surface->height); + + _cairo_pattern_fini (&glyph_pattern.base); + if (status) + break; + } + + if (mask != NULL) { + cairo_surface_pattern_t mask_pattern; + + _cairo_pattern_init_for_surface (&mask_pattern, mask); + + status = _cairo_surface_composite (operator, pattern, &mask_pattern.base, + surface, + source_x, source_y, + 0, 0, + dest_x, dest_y, + width, height); + + _cairo_pattern_fini (&mask_pattern.base); + } + +CLEANUP_MASK: + + _cairo_cache_release (scaled_font->glyphs); + + if (mask != NULL) + cairo_surface_destroy (mask); + return status; +} + +typedef struct _cairo_scaled_glyph_path_closure { + cairo_point_t offset; + cairo_path_fixed_t *path; +} cairo_scaled_glyph_path_closure_t; + +static cairo_status_t +_scaled_glyph_path_move_to (void *abstract_closure, cairo_point_t *point) +{ + cairo_scaled_glyph_path_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_move_to (closure->path, + point->x + closure->offset.x, + point->y + closure->offset.y); +} + +static cairo_status_t +_scaled_glyph_path_line_to (void *abstract_closure, cairo_point_t *point) +{ + cairo_scaled_glyph_path_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_line_to (closure->path, + point->x + closure->offset.x, + point->y + closure->offset.y); +} + +static cairo_status_t +_scaled_glyph_path_curve_to (void *abstract_closure, + cairo_point_t *p0, + cairo_point_t *p1, + cairo_point_t *p2) +{ + cairo_scaled_glyph_path_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_curve_to (closure->path, + p0->x + closure->offset.x, + p0->y + closure->offset.y, + p1->x + closure->offset.x, + p1->y + closure->offset.y, + p2->x + closure->offset.x, + p2->y + closure->offset.y); +} + + +static cairo_status_t +_scaled_glyph_path_close_path (void *abstract_closure) +{ + cairo_scaled_glyph_path_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_close_path (closure->path); +} + +cairo_status_t +_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path) +{ + cairo_status_t status; + int i; + cairo_scaled_glyph_path_closure_t closure; + + if (scaled_font->status) + return scaled_font->status; + + closure.path = path; + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + if (status) + return status; + + closure.offset.x = _cairo_fixed_from_double (glyphs[i].x); + closure.offset.y = _cairo_fixed_from_double (glyphs[i].y); + + status = _cairo_path_fixed_interpret (scaled_glyph->path, + CAIRO_DIRECTION_FORWARD, + _scaled_glyph_path_move_to, + _scaled_glyph_path_line_to, + _scaled_glyph_path_curve_to, + _scaled_glyph_path_close_path, + &closure); + } + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_scaled_glyph_set_metrics: + * @scaled_glyph: a #cairo_scaled_glyph_t + * @scaled_font: a #cairo_scaled_font_t + * @fs_metrics: a #cairo_text_extents_t in font space + * + * _cairo_scaled_glyph_set_metrics() stores user space metrics + * for the specified glyph given font space metrics. It is + * called by the font backend when initializing a glyph with + * CAIRO_SCALED_GLYPH_INFO_METRICS. + **/ +void +_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_text_extents_t *fs_metrics) +{ + cairo_bool_t first = TRUE; + double hm, wm; + double min_user_x = 0.0, max_user_x = 0.0, min_user_y = 0.0, max_user_y = 0.0; + double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0; + + for (hm = 0.0; hm <= 1.0; hm += 1.0) + for (wm = 0.0; wm <= 1.0; wm += 1.0) { + double x, y; + + /* Transform this corner to user space */ + x = fs_metrics->x_bearing + fs_metrics->width * wm; + y = fs_metrics->y_bearing + fs_metrics->height * hm; + cairo_matrix_transform_point (&scaled_font->font_matrix, + &x, &y); + if (first) { + min_user_x = max_user_x = x; + min_user_y = max_user_y = y; + } else { + if (x < min_user_x) min_user_x = x; + if (x > max_user_x) max_user_x = x; + if (y < min_user_y) min_user_y = y; + if (y > max_user_y) max_user_y = y; + } + + /* Transform this corner to device space from glyph origin */ + x = fs_metrics->x_bearing + fs_metrics->width * wm; + y = fs_metrics->y_bearing + fs_metrics->height * hm; + cairo_matrix_transform_distance (&scaled_font->scale, + &x, &y); + + if (first) { + min_device_x = max_device_x = x; + min_device_y = max_device_y = y; + } else { + if (x < min_device_x) min_device_x = x; + if (x > max_device_x) max_device_x = x; + if (y < min_device_y) min_device_y = y; + if (y > max_device_y) max_device_y = y; + } + first = FALSE; + } + scaled_glyph->metrics.x_bearing = min_user_x; + scaled_glyph->metrics.y_bearing = min_user_y; + scaled_glyph->metrics.width = max_user_x - min_user_x; + scaled_glyph->metrics.height = max_user_y - min_user_y; + + scaled_glyph->metrics.x_advance = fs_metrics->x_advance; + scaled_glyph->metrics.y_advance = fs_metrics->y_advance; + cairo_matrix_transform_point (&scaled_font->font_matrix, + &scaled_glyph->metrics.x_advance, + &scaled_glyph->metrics.y_advance); + + scaled_glyph->bbox.p1.x = _cairo_fixed_from_double (min_device_x); + scaled_glyph->bbox.p1.y = _cairo_fixed_from_double (min_device_y); + scaled_glyph->bbox.p2.x = _cairo_fixed_from_double (max_device_x); + scaled_glyph->bbox.p2.y = _cairo_fixed_from_double (max_device_y); +} + +void +_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_image_surface_t *surface) +{ + if (scaled_glyph->surface != NULL) + cairo_surface_destroy (&scaled_glyph->surface->base); + scaled_glyph->surface = surface; +} + +void +_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_path_fixed_t *path) +{ + if (scaled_glyph->path != NULL) + _cairo_path_fixed_destroy (scaled_glyph->path); + scaled_glyph->path = path; +} + +/** + * _cairo_scaled_glyph_lookup: + * @scaled_font: a #cairo_scaled_font_t + * @index: the glyph to create + * @info: a #cairo_scaled_glyph_info_t marking which portions of + * the glyph should be filled in. + * @scaled_glyph_ret: a #cairo_scaled_glyph_t * where the glyph + * is returned. + * + * Returns a glyph with the requested portions filled in. Glyph + * lookup is cached and glyph will be automatically freed along + * with the scaled_font so no explicit free is required. + * @info can be one or more of: + * %CAIRO_SCALED_GLYPH_INFO_METRICS - glyph metrics and bounding box + * %CAIRO_SCALED_GLYPH_INFO_SURFACE - surface holding glyph image + * %CAIRO_SCALED_GLYPH_INFO_PATH - path holding glyph outline in device space + **/ +cairo_status_t +_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, + unsigned long index, + cairo_scaled_glyph_info_t info, + cairo_scaled_glyph_t **scaled_glyph_ret) +{ + cairo_status_t status; + cairo_cache_entry_t key; + cairo_scaled_glyph_t *scaled_glyph; + cairo_scaled_glyph_info_t need_info; + + if (scaled_font->status) + return scaled_font->status; + + key.hash = index; + /* + * Check cache for glyph + */ + info |= CAIRO_SCALED_GLYPH_INFO_METRICS; + if (!_cairo_cache_lookup (scaled_font->glyphs, &key, + (cairo_cache_entry_t **) &scaled_glyph)) + { + /* + * On miss, create glyph and insert into cache + */ + scaled_glyph = malloc (sizeof (cairo_scaled_glyph_t)); + if (scaled_glyph == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + _cairo_scaled_font_set_error (scaled_font, status); + return status; + } + + _cairo_scaled_glyph_set_index(scaled_glyph, index); + scaled_glyph->cache_entry.size = 1; /* XXX */ + scaled_glyph->scaled_font = scaled_font; + scaled_glyph->surface = NULL; + scaled_glyph->path = NULL; + scaled_glyph->surface_private = NULL; + + /* ask backend to initialize metrics and shape fields */ + status = (*scaled_font->backend-> + scaled_glyph_init) (scaled_font, scaled_glyph, info); + if (status) { + _cairo_scaled_glyph_destroy (scaled_glyph); + _cairo_scaled_font_set_error (scaled_font, status); + return status; + } + status = _cairo_cache_insert (scaled_font->glyphs, + &scaled_glyph->cache_entry); + if (status) { + _cairo_scaled_glyph_destroy (scaled_glyph); + _cairo_scaled_font_set_error (scaled_font, status); + return status; + } + } + /* + * Check and see if the glyph, as provided, + * already has the requested data and ammend it if not + */ + need_info = 0; + if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0 && + scaled_glyph->surface == NULL) + need_info |= CAIRO_SCALED_GLYPH_INFO_SURFACE; + + if (((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 && + scaled_glyph->path == NULL)) + need_info |= CAIRO_SCALED_GLYPH_INFO_PATH; + + if (need_info) { + status = (*scaled_font->backend-> + scaled_glyph_init) (scaled_font, scaled_glyph, need_info); + if (status) + return status; + } + *scaled_glyph_ret = scaled_glyph; + return CAIRO_STATUS_SUCCESS; +} diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index f1f68d726..c8fa957d8 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -192,16 +192,15 @@ _CAIRO_FORMAT_XRENDER_FORMAT(Display *dpy, cairo_format_t format) } static cairo_surface_t * -_cairo_xlib_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height) +_cairo_xlib_surface_create_similar_with_format (void *abstract_src, + cairo_format_t format, + int width, + int height) { cairo_xlib_surface_t *src = abstract_src; Display *dpy = src->dpy; Pixmap pix; cairo_xlib_surface_t *surface; - cairo_format_t format = _cairo_format_from_content (content); int depth = _CAIRO_FORMAT_DEPTH (format); XRenderPictFormat *xrender_format = _CAIRO_FORMAT_XRENDER_FORMAT (dpy, format); @@ -221,7 +220,7 @@ _cairo_xlib_surface_create_similar (void *abstract_src, cairo_xlib_surface_create_with_xrender_format (dpy, pix, src->screen, xrender_format, width, height); - if (surface->base.status) { + if (surface->base.status != CAIRO_STATUS_SUCCESS) { _cairo_error (CAIRO_STATUS_NO_MEMORY); return (cairo_surface_t*) &_cairo_surface_nil; } @@ -231,23 +230,36 @@ _cairo_xlib_surface_create_similar (void *abstract_src, return &surface->base; } +static cairo_surface_t * +_cairo_xlib_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) +{ + cairo_format_t format = _cairo_format_from_content (content); + return _cairo_xlib_surface_create_similar_with_format (abstract_src, + format, + width, + height); +} + static cairo_status_t _cairo_xlib_surface_finish (void *abstract_surface) { cairo_xlib_surface_t *surface = abstract_surface; - if (surface->dst_picture) + if (surface->dst_picture != None) XRenderFreePicture (surface->dpy, surface->dst_picture); - if (surface->src_picture) + if (surface->src_picture != None) XRenderFreePicture (surface->dpy, surface->src_picture); if (surface->owns_pixmap) XFreePixmap (surface->dpy, surface->drawable); - if (surface->gc) + if (surface->gc != NULL) XFreeGC (surface->dpy, surface->gc); - if (surface->clip_rects) + if (surface->clip_rects != NULL) free (surface->clip_rects); surface->dpy = NULL; @@ -676,10 +688,9 @@ _cairo_xlib_surface_clone_similar (void *abstract_surface, } } else if (_cairo_surface_is_image (src)) { cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; - cairo_content_t content = _cairo_content_from_format (image_src->format); clone = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_similar (surface, content, + _cairo_xlib_surface_create_similar_with_format (surface, image_src->format, image_src->width, image_src->height); if (clone->base.status) return CAIRO_STATUS_NO_MEMORY; @@ -1545,6 +1556,13 @@ _cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs); +static void +_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +static void +_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font); + static const cairo_surface_backend_t cairo_xlib_surface_backend = { _cairo_xlib_surface_create_similar, _cairo_xlib_surface_finish, @@ -1563,7 +1581,11 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = { _cairo_xlib_surface_get_extents, _cairo_xlib_surface_show_glyphs, NULL, /* fill_path */ - _cairo_xlib_surface_get_font_options + _cairo_xlib_surface_get_font_options, + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + _cairo_xlib_surface_scaled_font_fini, + _cairo_xlib_surface_scaled_glyph_fini, }; /** @@ -1872,37 +1894,54 @@ cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, surface->height = height; } -/* RENDER glyphset cache code */ +typedef struct _cairo_xlib_surface_font_private { + Display *dpy; + GlyphSet glyphset; + XRenderPictFormat *format; +} cairo_xlib_surface_font_private_t; -typedef struct glyphset_cache { - cairo_cache_t base; - - Display *display; - Glyph counter; - - XRenderPictFormat *a1_pict_format; - GlyphSet a1_glyphset; - - XRenderPictFormat *a8_pict_format; - GlyphSet a8_glyphset; - - XRenderPictFormat *argb32_pict_format; - GlyphSet argb32_glyphset; - - struct glyphset_cache *next; -} glyphset_cache_t; - -typedef struct { - cairo_glyph_cache_key_t key; - GlyphSet glyphset; - Glyph glyph; - cairo_glyph_size_t size; -} glyphset_cache_entry_t; - -static Glyph -_next_xlib_glyph (glyphset_cache_t *cache) +static cairo_status_t +_cairo_xlib_surface_font_init (Display *dpy, + cairo_scaled_font_t *scaled_font, + cairo_format_t format) { - return ++(cache->counter); + cairo_xlib_surface_font_private_t *font_private; + + font_private = malloc (sizeof (cairo_xlib_surface_font_private_t)); + if (!font_private) + return CAIRO_STATUS_NO_MEMORY; + + font_private->dpy = dpy; + font_private->format = _CAIRO_FORMAT_XRENDER_FORMAT(dpy, format); + font_private->glyphset = XRenderCreateGlyphSet (dpy, font_private->format); + scaled_font->surface_private = font_private; + scaled_font->surface_backend = &cairo_xlib_surface_backend; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private; + + if (font_private) { + XRenderFreeGlyphSet (font_private->dpy, font_private->glyphset); + free (font_private); + } +} + +static void +_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font) +{ + cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private; + + if (font_private != NULL && scaled_glyph->surface_private != NULL) { + unsigned long glyph_index = _cairo_scaled_glyph_index(scaled_glyph); + XRenderFreeGlyphs (font_private->dpy, + font_private->glyphset, + &glyph_index, 1); + } } static cairo_bool_t @@ -1914,59 +1953,24 @@ _native_byte_order_lsb (void) } static cairo_status_t -_xlib_glyphset_cache_create_entry (void *abstract_cache, - void *abstract_key, - void **return_entry) +_cairo_xlib_surface_add_glyph (Display *dpy, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) { - glyphset_cache_t *cache = abstract_cache; - cairo_glyph_cache_key_t *key = abstract_key; - glyphset_cache_entry_t *entry; XGlyphInfo glyph_info; + unsigned long glyph_index; unsigned char *data; - cairo_status_t status; + cairo_xlib_surface_font_private_t *font_private; + cairo_image_surface_t *glyph_surface = scaled_glyph->surface; - cairo_cache_t *im_cache; - cairo_image_glyph_cache_entry_t *im; - - entry = malloc (sizeof (glyphset_cache_entry_t)); - _cairo_lock_global_image_glyph_cache (); - im_cache = _cairo_get_global_image_glyph_cache (); - - if (cache == NULL || entry == NULL || im_cache == NULL) { - _cairo_unlock_global_image_glyph_cache (); - if (entry) - free (entry); - return CAIRO_STATUS_NO_MEMORY; + if (scaled_font->surface_private == NULL) { + status = _cairo_xlib_surface_font_init (dpy, scaled_font, + glyph_surface->format); + if (status) + return status; } - - status = _cairo_cache_lookup (im_cache, key, (void **) (&im), NULL); - if (status != CAIRO_STATUS_SUCCESS || im == NULL) { - _cairo_unlock_global_image_glyph_cache (); - free (entry); - return CAIRO_STATUS_NO_MEMORY; - } - - entry->key = *key; - _cairo_unscaled_font_reference (entry->key.unscaled); - - if (!im->image) { - entry->glyph = None; - entry->glyphset = None; - entry->key.base.memory = 0; - entry->size.x = entry->size.y = entry->size.width = entry->size.height = 0; - - goto out; - } - - entry->glyph = _next_xlib_glyph (cache); - - data = im->image->data; - - entry->size = im->size; - - glyph_info.width = im->size.width; - glyph_info.height = im->size.height; + font_private = scaled_font->surface_private; /* * Most of the font rendering system thinks of glyph tiles as having @@ -2004,18 +2008,21 @@ _xlib_glyphset_cache_create_entry (void *abstract_cache, * sitting around for x and y. */ - glyph_info.x = -im->size.x; - glyph_info.y = -im->size.y; + glyph_info.x = -(int) glyph_surface->base.device_x_offset; + glyph_info.y = -(int) glyph_surface->base.device_y_offset; + glyph_info.width = glyph_surface->width; + glyph_info.height = glyph_surface->height; glyph_info.xOff = 0; glyph_info.yOff = 0; - switch (im->image->format) { + data = glyph_surface->data; + + /* flip formats around */ + switch (scaled_glyph->surface->format) { case CAIRO_FORMAT_A1: /* local bitmaps are always stored with bit == byte */ - if (_native_byte_order_lsb() != - (BitmapBitOrder (cache->display) == LSBFirst)) - { - int c = im->image->stride * im->size.height; + if (_native_byte_order_lsb() != (BitmapBitOrder (dpy) == LSBFirst)) { + int c = glyph_surface->stride * glyph_surface->height; unsigned char *d; unsigned char *new, *n; @@ -2034,21 +2041,17 @@ _xlib_glyphset_cache_create_entry (void *abstract_cache, } data = new; } - entry->glyphset = cache->a1_glyphset; break; case CAIRO_FORMAT_A8: - entry->glyphset = cache->a8_glyphset; break; case CAIRO_FORMAT_ARGB32: - if (_native_byte_order_lsb() != - (ImageByteOrder (cache->display) == LSBFirst)) - { - int c = im->image->stride * im->size.height; + if (_native_byte_order_lsb() != (ImageByteOrder (dpy) == LSBFirst)) { + int c = glyph_surface->stride * glyph_surface->height; unsigned char *d; unsigned char *new, *n; new = malloc (c); - if (!new) + if (new == NULL) return CAIRO_STATUS_NO_MEMORY; n = new; d = data; @@ -2063,7 +2066,6 @@ _xlib_glyphset_cache_create_entry (void *abstract_cache, } data = new; } - entry->glyphset = cache->argb32_glyphset; break; case CAIRO_FORMAT_RGB24: default: @@ -2072,511 +2074,207 @@ _xlib_glyphset_cache_create_entry (void *abstract_cache, } /* XXX assume X server wants pixman padding. Xft assumes this as well */ - XRenderAddGlyphs (cache->display, entry->glyphset, - &(entry->glyph), &(glyph_info), 1, + glyph_index = _cairo_scaled_glyph_index (scaled_glyph); + + XRenderAddGlyphs (dpy, font_private->glyphset, + &glyph_index, &(glyph_info), 1, (char *) data, - im->image->stride * glyph_info.height); + glyph_surface->stride * glyph_surface->height); - if (data != im->image->data) + if (data != glyph_surface->data) free (data); - entry->key.base.memory = im->image->height * im->image->stride; - - out: - *return_entry = entry; - _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 *abstract_cache, - void *abstract_entry) -{ - glyphset_cache_t *cache = abstract_cache; - glyphset_cache_entry_t *entry = abstract_entry; - - _cairo_unscaled_font_destroy (entry->key.unscaled); - if (entry->glyph) - XRenderFreeGlyphs (cache->display, entry->glyphset, - &(entry->glyph), 1); - free (entry); -} - -static 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 -}; - -CAIRO_MUTEX_DECLARE(_xlib_glyphset_caches_mutex); - -static glyphset_cache_t * -_xlib_glyphset_caches = NULL; - -static void -_lock_xlib_glyphset_caches (void) -{ - CAIRO_MUTEX_LOCK(_xlib_glyphset_caches_mutex); -} - -static void -_unlock_xlib_glyphset_caches (glyphset_cache_t *cache) -{ - if (cache) { - _cairo_cache_shrink_to (&cache->base, - CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT); - } - CAIRO_MUTEX_UNLOCK(_xlib_glyphset_caches_mutex); -} - -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 *cache; - - /* XXX: This is not thread-safe. Xft has example code to get - * per-display data via Xlib extension mechanisms. */ - for (cache = _xlib_glyphset_caches; cache != NULL; cache = cache->next) { - if (cache->display == d) - return cache; - } - - cache = malloc (sizeof (glyphset_cache_t)); - if (cache == NULL) - goto ERR; - - if (_cairo_cache_init (&cache->base, - &_xlib_glyphset_cache_backend, 0)) - goto FREE_GLYPHSET_CACHE; - - cache->display = d; - cache->counter = 0; - - cache->a1_pict_format = XRenderFindStandardFormat (d, PictStandardA1); - cache->a1_glyphset = XRenderCreateGlyphSet (d, cache->a1_pict_format); - - cache->a8_pict_format = XRenderFindStandardFormat (d, PictStandardA8); - cache->a8_glyphset = XRenderCreateGlyphSet (d, cache->a8_pict_format); - - cache->argb32_pict_format = XRenderFindStandardFormat (d, PictStandardARGB32); - cache->argb32_glyphset = XRenderCreateGlyphSet (d, cache->argb32_pict_format);; - - cache->next = _xlib_glyphset_caches; - _xlib_glyphset_caches = cache; - - return cache; - - FREE_GLYPHSET_CACHE: - free (cache); - - ERR: - return NULL; -} - #define N_STACK_BUF 1024 -static XRenderPictFormat * -_select_text_mask_format (glyphset_cache_t *cache, - cairo_bool_t have_a1_glyphs, - cairo_bool_t have_a8_glyphs, - cairo_bool_t have_argb32_glyphs) -{ - if (have_a8_glyphs) - return cache->a8_pict_format; - - if (have_a1_glyphs && have_argb32_glyphs) - return cache->a8_pict_format; - - if (have_a1_glyphs) - return cache->a1_pict_format; - - if (have_argb32_glyphs) - return cache->argb32_pict_format; - - /* when there are no glyphs to draw, just pick something */ - return cache->a8_pict_format; -} - static cairo_status_t -_cairo_xlib_surface_show_glyphs32 (cairo_scaled_font_t *scaled_font, +_cairo_xlib_surface_show_glyphs8 (cairo_scaled_font_t *scaled_font, cairo_operator_t operator, - glyphset_cache_t *cache, - 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, count; - int thisX, thisY; - int lastX = 0, lastY = 0; - - cairo_bool_t have_a1, have_a8, have_argb32; - XRenderPictFormat *mask_format; - - /* 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; - - } - - have_a1 = FALSE; - have_a8 = FALSE; - have_argb32 = FALSE; - count = 0; - - for (i = 0; i < num_glyphs; ++i) { - GlyphSet glyphset; - - if (!entries[i]->glyph) - continue; - - glyphset = entries[i]->glyphset; - - if (glyphset == cache->a1_glyphset) - have_a1 = TRUE; - else if (glyphset == cache->a8_glyphset) - have_a8 = TRUE; - else if (glyphset == cache->argb32_glyphset) - have_argb32 = TRUE; - - chars[count] = entries[i]->glyph; - elts[count].chars = &(chars[count]); - elts[count].nchars = 1; - elts[count].glyphset = glyphset; - thisX = (int) floor (glyphs[i].x + 0.5); - thisY = (int) floor (glyphs[i].y + 0.5); - elts[count].xOff = thisX - lastX; - elts[count].yOff = thisY - lastY; - lastX = thisX; - lastY = thisY; - count++; - } - - mask_format = _select_text_mask_format (cache, - have_a1, have_a8, have_argb32); - - XRenderCompositeText32 (self->dpy, - _render_operator (operator), - src->src_picture, - self->dst_picture, - mask_format, - source_x + elts[0].xOff, source_y + elts[0].yOff, - 0, 0, - elts, count); - - 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_scaled_font_t *scaled_font, - cairo_operator_t operator, - glyphset_cache_t *cache, - 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, count; - int thisX, thisY; - int lastX = 0, lastY = 0; - - cairo_bool_t have_a1, have_a8, have_argb32; - XRenderPictFormat *mask_format; - - /* 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; - - } - - have_a1 = FALSE; - have_a8 = FALSE; - have_argb32 = FALSE; - count = 0; - - for (i = 0; i < num_glyphs; ++i) { - GlyphSet glyphset; - - if (!entries[i]->glyph) - continue; - - glyphset = entries[i]->glyphset; - - if (glyphset == cache->a1_glyphset) - have_a1 = TRUE; - else if (glyphset == cache->a8_glyphset) - have_a8 = TRUE; - else if (glyphset == cache->argb32_glyphset) - have_argb32 = TRUE; - - chars[count] = entries[i]->glyph; - elts[count].chars = &(chars[count]); - elts[count].nchars = 1; - elts[count].glyphset = glyphset; - thisX = (int) floor (glyphs[i].x + 0.5); - thisY = (int) floor (glyphs[i].y + 0.5); - elts[count].xOff = thisX - lastX; - elts[count].yOff = thisY - lastY; - lastX = thisX; - lastY = thisY; - count++; - } - - mask_format = _select_text_mask_format (cache, - have_a1, have_a8, have_argb32); - - XRenderCompositeText16 (self->dpy, - _render_operator (operator), - src->src_picture, - self->dst_picture, - mask_format, - source_x + elts[0].xOff, source_y + elts[0].yOff, - 0, 0, - elts, count); - - 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_scaled_font_t *scaled_font, - cairo_operator_t operator, - glyphset_cache_t *cache, - 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) -{ + cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private; XGlyphElt8 *elts = NULL; XGlyphElt8 stack_elts [N_STACK_BUF]; char *chars = NULL; char stack_chars [N_STACK_BUF]; - int i, count; + int i; int thisX, thisY; int lastX = 0, lastY = 0; - cairo_bool_t have_a1, have_a8, have_argb32; - XRenderPictFormat *mask_format; - - if (num_glyphs == 0) - return CAIRO_STATUS_SUCCESS; - /* Acquire arrays of suitable sizes. */ if (num_glyphs < N_STACK_BUF) { elts = stack_elts; chars = stack_chars; - } else { - elts = malloc (num_glyphs * sizeof (XGlyphElt8)); + elts = malloc (num_glyphs * sizeof (XGlyphElt8) + + num_glyphs * sizeof (unsigned char)); if (elts == NULL) - goto FAIL; - - chars = malloc (num_glyphs * sizeof (char)); - if (chars == NULL) - goto FREE_ELTS; + return CAIRO_STATUS_NO_MEMORY; + chars = (char *) (elts + num_glyphs); } - have_a1 = FALSE; - have_a8 = FALSE; - have_argb32 = FALSE; - count = 0; - for (i = 0; i < num_glyphs; ++i) { - GlyphSet glyphset; - - if (!entries[i]->glyph) - continue; - - glyphset = entries[i]->glyphset; - - if (glyphset == cache->a1_glyphset) - have_a1 = TRUE; - else if (glyphset == cache->a8_glyphset) - have_a8 = TRUE; - else if (glyphset == cache->argb32_glyphset) - have_argb32 = TRUE; - - chars[count] = entries[i]->glyph; - elts[count].chars = &(chars[count]); - elts[count].nchars = 1; - elts[count].glyphset = glyphset; + chars[i] = glyphs[i].index; + elts[i].chars = &(chars[i]); + elts[i].nchars = 1; + elts[i].glyphset = font_private->glyphset; thisX = (int) floor (glyphs[i].x + 0.5); thisY = (int) floor (glyphs[i].y + 0.5); - elts[count].xOff = thisX - lastX; - elts[count].yOff = thisY - lastY; + elts[i].xOff = thisX - lastX; + elts[i].yOff = thisY - lastY; lastX = thisX; lastY = thisY; - count++; } - mask_format = _select_text_mask_format (cache, - have_a1, have_a8, have_argb32); + XRenderCompositeText8 (self->dpy, + _render_operator (operator), + src->src_picture, + self->dst_picture, + font_private->format, + source_x + elts[0].xOff, source_y + elts[0].yOff, + 0, 0, + elts, num_glyphs); + + if (elts != stack_elts) + free (elts); - XRenderCompositeText8 (self->dpy, - _render_operator (operator), - src->src_picture, - self->dst_picture, - mask_format, - source_x + elts[0].xOff, source_y + elts[0].yOff, - 0, 0, - elts, count); - - 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; } -/* Handles clearing the regions that are outside of the temporary - * mask created by XRenderCompositeText[N] but should be affected - * by an unbounded operator like CAIRO_OPERATOR_IN - */ static cairo_status_t -_show_glyphs_fixup_unbounded (cairo_xlib_surface_t *self, - cairo_surface_attributes_t *src_attr, - cairo_xlib_surface_t *src, - const cairo_glyph_t *glyphs, - glyphset_cache_entry_t **entries, - int num_glyphs, - int src_x, - int src_y, - int dst_x, - int dst_y, - int width, - int height) +_cairo_xlib_surface_show_glyphs16 (cairo_scaled_font_t *scaled_font, + cairo_operator_t operator, + cairo_xlib_surface_t *src, + cairo_xlib_surface_t *self, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + int num_glyphs) { - int x1 = INT_MAX; - int x2 = INT_MIN; - int y1 = INT_MAX; - int y2 = INT_MIN; - int i; + cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private; + XGlyphElt16 *elts = NULL; + XGlyphElt16 stack_elts [N_STACK_BUF]; + + unsigned short *chars = NULL; + unsigned short stack_chars [N_STACK_BUF]; + + int i; + int thisX, thisY; + 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) + + num_glyphs * sizeof (unsigned short)); + if (elts == NULL) + return CAIRO_STATUS_NO_MEMORY; + + chars = (unsigned short *) (elts + num_glyphs); + } - /* Compute the size of the glyph mask as the bounding box - * of all the glyphs. - */ for (i = 0; i < num_glyphs; ++i) { - int thisX, thisY; - - if (entries[i] == NULL || !entries[i]->glyph) - continue; - + chars[i] = glyphs[i].index; + elts[i].chars = &(chars[i]); + elts[i].nchars = 1; + elts[i].glyphset = font_private->glyphset; thisX = (int) floor (glyphs[i].x + 0.5); thisY = (int) floor (glyphs[i].y + 0.5); - - if (thisX + entries[i]->size.x < x1) - x1 = thisX + entries[i]->size.x; - if (thisX + entries[i]->size.x + entries[i]->size.width > x2) - x2 = thisX + entries[i]->size.x + entries[i]->size.width; - if (thisY + entries[i]->size.y < y1) - y1 = thisY + entries[i]->size.y; - if (thisY + entries[i]->size.y + entries[i]->size.height > y2) - y2 = thisY + entries[i]->size.y + entries[i]->size.height; + elts[i].xOff = thisX - lastX; + elts[i].yOff = thisY - lastY; + lastX = thisX; + lastY = thisY; } - if (x1 >= x2 || y1 >= y2) - x1 = x2 = y1 = y2 = 0; + XRenderCompositeText16 (self->dpy, + _render_operator (operator), + src->src_picture, + self->dst_picture, + font_private->format, + source_x + elts[0].xOff, source_y + elts[0].yOff, + 0, 0, + elts, num_glyphs); - return _cairo_surface_composite_shape_fixup_unbounded (&self->base, - src_attr, src->width, src->height, - x2 - x1, y2 - y1, - src_x, src_y, - dst_x - x1, dst_y - y1, - dst_x, dst_y, width, height); + if (elts != stack_elts) + free (elts); + + return CAIRO_STATUS_SUCCESS; } - + +static cairo_status_t +_cairo_xlib_surface_show_glyphs32 (cairo_scaled_font_t *scaled_font, + cairo_operator_t operator, + cairo_xlib_surface_t *src, + cairo_xlib_surface_t *self, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private; + XGlyphElt32 *elts = NULL; + XGlyphElt32 stack_elts [N_STACK_BUF]; + + unsigned int *chars = NULL; + unsigned int stack_chars [N_STACK_BUF]; + + int i; + int thisX, thisY; + 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) + + num_glyphs * sizeof (unsigned int)); + if (elts == NULL) + return CAIRO_STATUS_NO_MEMORY; + + chars = (unsigned int *) (elts + num_glyphs); + } + + for (i = 0; i < num_glyphs; ++i) { + chars[i] = glyphs[i].index; + elts[i].chars = &(chars[i]); + elts[i].nchars = 1; + elts[i].glyphset = font_private->glyphset; + thisX = (int) floor (glyphs[i].x + 0.5); + thisY = (int) floor (glyphs[i].y + 0.5); + elts[i].xOff = thisX - lastX; + elts[i].yOff = thisY - lastY; + lastX = thisX; + lastY = thisY; + } + + XRenderCompositeText32 (self->dpy, + _render_operator (operator), + src->src_picture, + self->dst_picture, + font_private->format, + source_x + elts[0].xOff, source_y + elts[0].yOff, + 0, 0, + elts, num_glyphs); + + if (elts != stack_elts) + free (elts); + + return CAIRO_STATUS_SUCCESS; +} + static cairo_int_status_t _cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t operator, @@ -2593,15 +2291,14 @@ _cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, { cairo_surface_attributes_t attributes; cairo_int_status_t status; - unsigned int elt_size; cairo_xlib_surface_t *self = abstract_surface; cairo_xlib_surface_t *src; - glyphset_cache_t *cache; - cairo_glyph_cache_key_t key; - glyphset_cache_entry_t **entries; - glyphset_cache_entry_t *stack_entries [N_STACK_BUF]; composite_operation_t operation; + cairo_scaled_glyph_t *scaled_glyph; + cairo_xlib_surface_font_private_t *font_private; int i; + unsigned long max_index = 0; + if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (self) || !self->format) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -2610,6 +2307,12 @@ _cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, if (operation == DO_UNSUPPORTED) return CAIRO_INT_STATUS_UNSUPPORTED; + font_private = scaled_font->surface_private; + if ((scaled_font->surface_backend != NULL && + scaled_font->surface_backend != &cairo_xlib_surface_backend) || + (font_private != NULL && font_private->dpy != self->dpy)) + return CAIRO_INT_STATUS_UNSUPPORTED; + status = _cairo_pattern_acquire_surface (pattern, &self->base, source_x, source_y, width, height, (cairo_surface_t **) &src, @@ -2627,102 +2330,58 @@ _cairo_xlib_surface_show_glyphs (cairo_scaled_font_t *scaled_font, if (status) goto FAIL; - /* 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; - } - - _lock_xlib_glyphset_caches (); - cache = _get_glyphset_cache (self->dpy); - if (cache == NULL) - goto UNLOCK; - - /* Work out the index size to use. */ - elt_size = 8; - status = _cairo_scaled_font_get_glyph_cache_key (scaled_font, &key); - if (status) - goto UNLOCK; - - for (i = 0; i < num_glyphs; ++i) { - key.index = glyphs[i].index; - status = _cairo_cache_lookup (&cache->base, &key, (void **) (&entries[i]), NULL); - 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; + /* Send all unsent glyphs to the server */ + for (i = 0; i < num_glyphs; i++) { + if (glyphs[i].index > max_index) + max_index = glyphs[i].index; + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (status != CAIRO_STATUS_SUCCESS) + return status; + if (scaled_glyph->surface_private == NULL) { + _cairo_xlib_surface_add_glyph (self->dpy, scaled_font, scaled_glyph); + scaled_glyph->surface_private = (void *) 1; } } - + + _cairo_xlib_surface_ensure_dst_picture (self); /* Call the appropriate sub-function. */ - _cairo_xlib_surface_ensure_dst_picture (self); - if (elt_size == 8) - { - status = _cairo_xlib_surface_show_glyphs8 (scaled_font, operator, cache, &key, src, self, + if (max_index < 256) + status = _cairo_xlib_surface_show_glyphs8 (scaled_font, operator, src, self, source_x + attributes.x_offset - dest_x, source_y + attributes.y_offset - dest_y, - glyphs, entries, num_glyphs); - } - else if (elt_size == 16) - { - status = _cairo_xlib_surface_show_glyphs16 (scaled_font, operator, cache, &key, src, self, + glyphs, num_glyphs); + else if (max_index < 65536) + status = _cairo_xlib_surface_show_glyphs16 (scaled_font, operator, src, self, source_x + attributes.x_offset - dest_x, source_y + attributes.y_offset - dest_y, - glyphs, entries, num_glyphs); - } + glyphs, num_glyphs); else - { - status = _cairo_xlib_surface_show_glyphs32 (scaled_font, operator, cache, &key, src, self, + status = _cairo_xlib_surface_show_glyphs32 (scaled_font, operator, src, self, source_x + attributes.x_offset - dest_x, source_y + attributes.y_offset - dest_y, - glyphs, entries, num_glyphs); + glyphs, num_glyphs); + + if (status == CAIRO_STATUS_SUCCESS && !_cairo_operator_bounded (operator)) { + cairo_rectangle_t extents; + status = _cairo_scaled_font_glyph_device_extents (scaled_font, + glyphs, + num_glyphs, + &extents); + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_surface_composite_shape_fixup_unbounded + (&self->base, &attributes, src->width, src->height, + extents.width, extents.height, + source_x, source_y, + dest_x - extents.x, dest_y - extents.y, + dest_x, dest_y, + width, height); } - - if (status == CAIRO_STATUS_SUCCESS && - !_cairo_operator_bounded (operator)) - status = _show_glyphs_fixup_unbounded (self, - &attributes, src, - glyphs, entries, num_glyphs, - source_x, source_y, - dest_x, dest_y, width, height); - - UNLOCK: - _unlock_xlib_glyphset_caches (cache); - - if (num_glyphs >= N_STACK_BUF) - free (entries); - FAIL: _cairo_pattern_release_surface (pattern, &src->base, &attributes); return status; } - -static void -_destroy_glyphset_cache_recurse (glyphset_cache_t *cache) -{ - if (cache == NULL) - return; - - _destroy_glyphset_cache_recurse (cache->next); - _cairo_cache_destroy (&cache->base); - free (cache); -} - -void -_cairo_xlib_surface_reset_static_data (void) -{ - _lock_xlib_glyphset_caches (); - _destroy_glyphset_cache_recurse (_xlib_glyphset_caches); - _xlib_glyphset_caches = NULL; - _unlock_xlib_glyphset_caches (NULL); -} diff --git a/src/cairoint.h b/src/cairoint.h index a43136bea..097cce884 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -184,6 +184,7 @@ typedef cairo_fixed_16_16_t cairo_fixed_t; #define CAIRO_ALPHA_IS_ZERO(alpha) ((alpha) <= 0.0) #include "cairo-hash-private.h" +#include "cairo-cache-private.h" typedef struct _cairo_point { cairo_fixed_t x; @@ -227,7 +228,8 @@ typedef struct _cairo_rectangle { typedef enum cairo_int_status { CAIRO_INT_STATUS_DEGENERATE = 1000, CAIRO_INT_STATUS_UNSUPPORTED, - CAIRO_INT_STATUS_NOTHING_TO_DO + CAIRO_INT_STATUS_NOTHING_TO_DO, + CAIRO_INT_STATUS_CACHE_EMPTY } cairo_int_status_t; typedef enum cairo_direction { @@ -288,6 +290,8 @@ typedef struct _cairo_pen { typedef struct _cairo_color cairo_color_t; typedef struct _cairo_image_surface cairo_image_surface_t; +typedef struct _cairo_surface_backend cairo_surface_backend_t; + cairo_private void _cairo_box_round_to_rectangle (cairo_box_t *box, cairo_rectangle_t *rectangle); @@ -348,108 +352,9 @@ _cairo_user_data_array_set_data (cairo_user_data_array_t *array, void *user_data, cairo_destroy_func_t destroy); -/* 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 { - const cairo_cache_backend_t *backend; - const 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; - -cairo_private cairo_status_t -_cairo_cache_init (cairo_cache_t *cache, - const cairo_cache_backend_t *backend, - unsigned long max_memory); - -cairo_private void -_cairo_cache_destroy (cairo_cache_t *cache); - -cairo_private void -_cairo_cache_shrink_to (cairo_cache_t *cache, - unsigned long max_memory); - -cairo_private cairo_status_t -_cairo_cache_lookup (cairo_cache_t *cache, - void *key, - void **entry_return, - int *created_entry); - -cairo_private cairo_status_t -_cairo_cache_remove (cairo_cache_t *cache, - void *key); - -cairo_private void * -_cairo_cache_random_entry (cairo_cache_t *cache, - int (*predicate) (void*)); - cairo_private unsigned long _cairo_hash_string (const char *c); -#define CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT 0x100000 -#define CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT 0x100000 - typedef struct _cairo_unscaled_font_backend cairo_unscaled_font_backend_t; typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t; typedef struct _cairo_font_face_backend cairo_font_face_backend_t; @@ -471,16 +376,47 @@ struct _cairo_font_options { cairo_hint_metrics_t hint_metrics; }; +typedef struct _cairo_scaled_glyph { + cairo_cache_entry_t cache_entry; /* hash is glyph index */ + cairo_scaled_font_t *scaled_font; /* font the glyph lives in */ + cairo_text_extents_t metrics; /* user-space metrics */ + cairo_box_t bbox; /* device-space bounds */ + cairo_image_surface_t *surface; /* device-space image */ + cairo_path_fixed_t *path; /* device-space outline */ + void *surface_private; /* for the surface backend */ +} cairo_scaled_glyph_t; + +#define _cairo_scaled_glyph_index(g) ((g)->cache_entry.hash) +#define _cairo_scaled_glyph_set_index(g,i) ((g)->cache_entry.hash = (i)) + struct _cairo_scaled_font { + /* must be first to be stored in a hash table */ cairo_hash_entry_t hash_entry; + + /* useful bits for _cairo_scaled_font_nil */ cairo_status_t status; int ref_count; + + /* hash key members */ cairo_font_face_t *font_face; /* may be NULL */ cairo_matrix_t font_matrix; /* font space => user space */ cairo_matrix_t ctm; /* user space => device space */ - cairo_matrix_t scale; /* font space => device space */ cairo_font_options_t options; - + + /* "live" scaled_font members */ + cairo_matrix_t scale; /* font space => device space */ + cairo_font_extents_t extents; /* user space */ + cairo_cache_t *glyphs; /* glyph index -> cairo_scaled_glyph_t */ + + /* + * One surface backend may store data in each glyph. + * Whichever surface manages to store its pointer here + * first gets to store data in each glyph + */ + const cairo_surface_backend_t *surface_backend; + void *surface_private; + + /* font backend managing this scaled font */ const cairo_scaled_font_backend_t *backend; }; @@ -492,40 +428,6 @@ struct _cairo_font_face { const cairo_font_face_backend_t *backend; }; -/* cairo_font.c is responsible for a global glyph cache: - * - * - glyph entries: [[[base], cairo_unscaled_font_t, scale, flags, 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_matrix_t scale; /* translation is ignored */ - int flags; - 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; - -cairo_private void -_cairo_lock_global_image_glyph_cache (void); - -cairo_private void -_cairo_unlock_global_image_glyph_cache (void); - -cairo_private cairo_cache_t * -_cairo_get_global_image_glyph_cache (void); - cairo_private void _cairo_font_reset_static_data (void); @@ -538,22 +440,10 @@ _cairo_xlib_surface_reset_static_data (void); cairo_private void _cairo_xlib_screen_reset_static_data (void); -/* Some glyph cache functions you can reuse. */ - -cairo_private unsigned long -_cairo_glyph_cache_hash (void *cache, void *key); - -cairo_private int -_cairo_glyph_cache_keys_equal (void *cache, - void *k1, - void *k2); - /* the font backend interface */ struct _cairo_unscaled_font_backend { void (*destroy) (void *unscaled_font); - cairo_status_t (*create_glyph) (void *unscaled_font, - cairo_image_glyph_cache_entry_t *entry); }; /* cairo_toy_font_face_t - simple family/slant/weight font faces used for @@ -568,6 +458,12 @@ typedef struct _cairo_toy_font_face { cairo_font_weight_t weight; } cairo_toy_font_face_t; +typedef enum _cairo_scaled_glyph_info { + CAIRO_SCALED_GLYPH_INFO_METRICS = (1 << 0), + CAIRO_SCALED_GLYPH_INFO_SURFACE = (1 << 1), + CAIRO_SCALED_GLYPH_INFO_PATH = (1 << 2) +} cairo_scaled_glyph_info_t; + struct _cairo_scaled_font_backend { cairo_status_t (*create_toy) (cairo_toy_font_face_t *toy_face, @@ -580,28 +476,14 @@ struct _cairo_scaled_font_backend { (*fini) (void *scaled_font); cairo_status_t - (*font_extents) (void *scaled_font, - cairo_font_extents_t *extents); + (*scaled_glyph_init) (void *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info); - cairo_status_t - (*text_to_glyphs) (void *scaled_font, - const char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs); - - cairo_status_t - (*glyph_extents) (void *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents); - - cairo_status_t - (*glyph_bbox) (void *scaled_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox); - - cairo_status_t + unsigned long + (*ucs4_to_index) (void *scaled_font, + uint32_t ucs4); + cairo_int_status_t (*show_glyphs) (void *scaled_font, cairo_operator_t operator, cairo_pattern_t *pattern, @@ -615,15 +497,6 @@ struct _cairo_scaled_font_backend { const cairo_glyph_t *glyphs, int num_glyphs); - cairo_status_t - (*glyph_path) (void *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_fixed_t *path); - - void - (*get_glyph_cache_key) (void *scaled_font, - cairo_glyph_cache_key_t *key); }; struct _cairo_font_face_backend { @@ -660,7 +533,7 @@ extern const cairo_private struct _cairo_scaled_font_backend cairo_atsui_scaled_ #endif -typedef struct _cairo_surface_backend { +struct _cairo_surface_backend { cairo_surface_t * (*create_similar) (void *surface, cairo_content_t content, @@ -796,8 +669,8 @@ typedef struct _cairo_surface_backend { /* * 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. + * resources. If null, render against this surface, using image + * surfaces as glyphs. */ cairo_int_status_t (*show_glyphs) (cairo_scaled_font_t *font, @@ -835,7 +708,13 @@ typedef struct _cairo_surface_backend { int width, int height); -} cairo_surface_backend_t; + void + (*scaled_font_fini) (cairo_scaled_font_t *scaled_font); + + void + (*scaled_glyph_fini) (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font); +}; typedef struct _cairo_format_masks { int bpp; @@ -1384,63 +1263,6 @@ _cairo_unscaled_font_reference (cairo_unscaled_font_t *font); cairo_private void _cairo_unscaled_font_destroy (cairo_unscaled_font_t *font); -cairo_private void -_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, - cairo_font_face_t *font_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - const cairo_scaled_font_backend_t *backend); - -void -_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font); - -cairo_private cairo_status_t -_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font, - cairo_font_extents_t *extents); - -cairo_private cairo_status_t -_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, - const char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs); - -cairo_private cairo_status_t -_cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents); - -cairo_private cairo_status_t -_cairo_scaled_font_glyph_bbox (cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox); - -cairo_private cairo_status_t -_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, - cairo_operator_t operator, - cairo_pattern_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs); - -cairo_private cairo_status_t -_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_fixed_t *path); - -cairo_private cairo_status_t -_cairo_scaled_font_get_glyph_cache_key (cairo_scaled_font_t *scaled_font, - cairo_glyph_cache_key_t *key); - /* cairo-font-options.c */ cairo_private void @@ -1458,9 +1280,15 @@ cairo_private cairo_status_t _cairo_path_fixed_init_copy (cairo_path_fixed_t *path, cairo_path_fixed_t *other); +cairo_path_fixed_t * +_cairo_path_fixed_create (void); + cairo_private void _cairo_path_fixed_fini (cairo_path_fixed_t *path); +void +_cairo_path_fixed_destroy (cairo_path_fixed_t *path); + cairo_private cairo_status_t _cairo_path_fixed_move_to (cairo_path_fixed_t *path, cairo_fixed_t x, @@ -1545,6 +1373,91 @@ _cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path, cairo_gstate_t *gstate, cairo_traps_t *traps); +/* cairo-scaled-font.c */ + +cairo_private cairo_status_t +_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, + cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + const cairo_scaled_font_backend_t *backend); + +cairo_private void +_cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *fs_metrics); + +cairo_private void +_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +cairo_private cairo_status_t +_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *extents); + +cairo_private cairo_status_t +_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, + double x, + double y, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs); + +cairo_private cairo_status_t +_cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); + +cairo_private cairo_status_t +_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_rectangle_t *extents); + +cairo_private cairo_status_t +_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t operator, + cairo_pattern_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs); + +cairo_private cairo_status_t +_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path); + +cairo_private void +_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_text_extents_t *fs_metrics); + +cairo_private void +_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_image_surface_t *surface); + +cairo_private void +_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_path_fixed_t *path); + +cairo_status_t +_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, + unsigned long index, + cairo_scaled_glyph_info_t info, + cairo_scaled_glyph_t **scaled_glyph_ret); + +cairo_private void +_cairo_scaled_font_map_destroy (void); + /* cairo-surface.c */ extern const cairo_private cairo_surface_t _cairo_surface_nil; diff --git a/test/clip-operator-ref.png b/test/clip-operator-ref.png index d1be82b3d..d81a7341f 100644 Binary files a/test/clip-operator-ref.png and b/test/clip-operator-ref.png differ diff --git a/test/operator-clear-ref.png b/test/operator-clear-ref.png index 4ee43f1cc..73082afd3 100644 Binary files a/test/operator-clear-ref.png and b/test/operator-clear-ref.png differ diff --git a/test/operator-source-ref.png b/test/operator-source-ref.png index a0102139d..ab7d263c2 100644 Binary files a/test/operator-source-ref.png and b/test/operator-source-ref.png differ diff --git a/test/text-antialias-gray-ref.png b/test/text-antialias-gray-ref.png index 1cab79a9f..dd2c77511 100644 Binary files a/test/text-antialias-gray-ref.png and b/test/text-antialias-gray-ref.png differ diff --git a/test/text-antialias-gray.c b/test/text-antialias-gray.c index 96044f507..a8e4a34d8 100644 --- a/test/text-antialias-gray.c +++ b/test/text-antialias-gray.c @@ -26,7 +26,7 @@ #include "cairo-test.h" #define WIDTH 31 -#define HEIGHT 20 +#define HEIGHT 22 #define TEXT_SIZE 12 cairo_test_t test = { diff --git a/test/text-antialias-none-ref.png b/test/text-antialias-none-ref.png index 141a97270..79d90fc83 100644 Binary files a/test/text-antialias-none-ref.png and b/test/text-antialias-none-ref.png differ diff --git a/test/text-antialias-none.c b/test/text-antialias-none.c index 50b482628..fb1c97a92 100644 --- a/test/text-antialias-none.c +++ b/test/text-antialias-none.c @@ -26,7 +26,7 @@ #include "cairo-test.h" #define WIDTH 31 -#define HEIGHT 20 +#define HEIGHT 22 #define TEXT_SIZE 12 cairo_test_t test = { diff --git a/test/text-antialias-subpixel-ref.png b/test/text-antialias-subpixel-ref.png index 88c6e062b..88f76c08e 100644 Binary files a/test/text-antialias-subpixel-ref.png and b/test/text-antialias-subpixel-ref.png differ diff --git a/test/text-antialias-subpixel.c b/test/text-antialias-subpixel.c index ead958d11..e4a6d06f5 100644 --- a/test/text-antialias-subpixel.c +++ b/test/text-antialias-subpixel.c @@ -26,7 +26,7 @@ #include "cairo-test.h" #define WIDTH 31 -#define HEIGHT 20 +#define HEIGHT 22 #define TEXT_SIZE 12 cairo_test_t test = { diff --git a/test/unbounded-operator-ref.png b/test/unbounded-operator-ref.png index ff7a9624a..fa3446926 100644 Binary files a/test/unbounded-operator-ref.png and b/test/unbounded-operator-ref.png differ