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