From b0c58593b30c1fa085b1e7c8e4897da623b8686d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 31 Aug 2005 15:08:02 +0000 Subject: [PATCH] Split out scaled font code to cairo-scaled-font.c Replace cairo cache implementation (this code from cworth) No more global glyph cache to clean up Store glyphs in new per-scaled font caches which hold user-space metrics and device space bounding boxes Refactor glyph drawing APIs so that the surface API is invoked directly from the gstate code. Add path creation/destruction routines (to hold glyph paths) 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. Adapt to new scaled font API changes. New cache and scaled_font APIs 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. Reviewed by: otaylor, cworth --- ChangeLog | 95 +++ src/Makefile.am | 1 + src/cairo-cache-private.h | 131 +++ src/cairo-cache.c | 674 ++++++--------- src/cairo-debug.c | 1 - src/cairo-font.c | 847 ------------------- src/cairo-ft-font.c | 1140 +++++++++---------------- src/cairo-gstate.c | 47 +- src/cairo-path.c | 18 + src/cairo-scaled-font.c | 1165 ++++++++++++++++++++++++++ src/cairo-xlib-surface.c | 937 +++++++-------------- src/cairoint.h | 391 ++++----- test/clip-operator-ref.png | Bin 37330 -> 37134 bytes test/operator-clear-ref.png | Bin 4988 -> 4971 bytes test/operator-source-ref.png | Bin 19958 -> 19982 bytes test/text-antialias-gray-ref.png | Bin 727 -> 740 bytes test/text-antialias-gray.c | 2 +- test/text-antialias-none-ref.png | Bin 278 -> 276 bytes test/text-antialias-none.c | 2 +- test/text-antialias-subpixel-ref.png | Bin 1081 -> 1121 bytes test/text-antialias-subpixel.c | 2 +- test/unbounded-operator-ref.png | Bin 11929 -> 11994 bytes 22 files changed, 2540 insertions(+), 2913 deletions(-) create mode 100644 src/cairo-cache-private.h create mode 100644 src/cairo-scaled-font.c 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 d1be82b3d4fc46ed0000559f2d6e55d58ad04d88..d81a7341f50febc57e8a85ad36cf8063bb3e372c 100644 GIT binary patch literal 37134 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 literal 37330 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