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.
This commit is contained in:
Graydon Hoare 2004-10-08 12:09:49 +00:00
parent 56ccb88376
commit 30131aa463
24 changed files with 5593 additions and 2027 deletions

190
ChangeLog
View file

@ -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.

View file

@ -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 \

454
src/cairo-cache.c Normal file
View file

@ -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;
}

View file

@ -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. */
static void
_cairo_glyph_cache_reference (cairo_glyph_cache_t *glyph_cache);
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;
cairo_font_t *
_cairo_font_create (const char *family,
cairo_font_slant_t slant,
cairo_font_weight_t weight)
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 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,336 +98,250 @@ _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;
}
static void
_font_cache_destroy_entry (void *cache,
void *entry)
{
cairo_font_cache_entry_t *e;
e = (cairo_font_cache_entry_t *) entry;
_cairo_unscaled_font_destroy (e->unscaled);
free ((void *) e->key.family);
free (e);
}
static void
_font_cache_destroy_cache (void *cache)
{
free (cache);
}
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
};
static void
_lock_global_font_cache (void)
{
/* FIXME: implement locking. */
}
static void
_unlock_global_font_cache (void)
{
/* FIXME: implement locking. */
}
static cairo_cache_t *
_global_font_cache = NULL;
static cairo_cache_t *
_get_global_font_cache (void)
{
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;
}
/* 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)
{
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;
}
void
_cairo_font_init (cairo_font_t *scaled,
cairo_font_scale_t *scale,
cairo_unscaled_font_t *unscaled)
{
scaled->scale = *scale;
scaled->unscaled = unscaled;
scaled->refcount = 1;
}
cairo_status_t
_cairo_font_init (cairo_font_t *font,
const struct cairo_font_backend *backend)
_cairo_unscaled_font_init (cairo_unscaled_font_t *font,
const struct cairo_font_backend *backend)
{
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;
return CAIRO_STATUS_SUCCESS;
}
cairo_font_t *
_cairo_font_copy (cairo_font_t *font)
{
cairo_font_t *newfont = NULL;
char *tmp = NULL;
if (font == NULL || font->backend->copy == NULL)
return NULL;
newfont = font->backend->copy (font);
if (newfont == NULL) {
free (tmp);
return NULL;
cairo_status_t
_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->text_to_glyphs (font, scale, utf8, glyphs, num_glyphs);
}
cairo_status_t
_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->glyph_extents(font, scale, glyphs, num_glyphs, extents);
}
cairo_status_t
_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->glyph_bbox (font, scale, glyphs, num_glyphs, bbox);
}
cairo_status_t
_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)
{
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;
}
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;
/* 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_scale (cairo_font_t *font, double scale)
_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 cairo_matrix_scale (&font->matrix, scale, scale);
return font->backend->glyph_path (font, scale, glyphs, num_glyphs, path);
}
cairo_status_t
_cairo_font_transform (cairo_font_t *font, cairo_matrix_t *matrix)
_cairo_unscaled_font_font_extents (cairo_unscaled_font_t *font,
cairo_font_scale_t *scale,
cairo_font_extents_t *extents)
{
return cairo_matrix_multiply (&font->matrix, matrix, &font->matrix);
return font->backend->font_extents(font, scale, extents);
}
cairo_status_t
_cairo_font_text_extents (cairo_font_t *font,
const unsigned char *utf8,
cairo_text_extents_t *extents)
void
_cairo_unscaled_font_reference (cairo_unscaled_font_t *font)
{
return font->backend->text_extents(font, utf8, extents);
font->refcount++;
}
cairo_status_t
_cairo_font_glyph_extents (cairo_font_t *font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_text_extents_t *extents)
{
return font->backend->glyph_extents(font, glyphs, num_glyphs, extents);
}
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)
{
return font->backend->text_bbox (font, surface, x, y, utf8, bbox);
}
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)
{
return font->backend->glyph_bbox (font, surface, glyphs, num_glyphs, bbox);
}
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)
{
return font->backend->show_text(font, operator, source,
surface, source_x, source_y, x, y, utf8);
}
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)
{
return font->backend->show_glyphs(font, operator, source,
surface, source_x, source_y,
glyphs, num_glyphs);
}
cairo_status_t
_cairo_font_text_path (cairo_font_t *font,
double x,
double y,
const unsigned char *utf8,
cairo_path_t *path)
{
return font->backend->text_path(font, x, y, utf8, path);
}
cairo_status_t
_cairo_font_glyph_path (cairo_font_t *font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_path_t *path)
{
return font->backend->glyph_path(font, glyphs, num_glyphs, path);
}
cairo_status_t
_cairo_font_font_extents (cairo_font_t *font,
cairo_font_extents_t *extents)
{
return font->backend->font_extents(font, extents);
}
static void
_cairo_glyph_cache_pop_last (cairo_glyph_cache_t *glyph_cache)
{
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;
free (remove);
glyph_cache->n_nodes--;
}
}
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;
}
static void
_cairo_glyph_cache_reference (cairo_glyph_cache_t *glyph_cache)
{
if (glyph_cache == NULL)
void
_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font)
{
if (--(font->refcount) > 0)
return;
glyph_cache->ref_count++;
if (font->backend)
font->backend->destroy (font);
}
static void
_cairo_glyph_cache_destroy (cairo_glyph_cache_t *glyph_cache)
{
if (glyph_cache == NULL)
return;
glyph_cache->ref_count--;
if (glyph_cache->ref_count)
return;
while (glyph_cache->last)
_cairo_glyph_cache_pop_last (glyph_cache);
free (glyph_cache);
}
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)
{
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_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)
{
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);
*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++;
}
}
return glyph_surface.surface;
}
/* public font interface follows */
/* Public font API follows. */
void
cairo_font_reference (cairo_font_t *font)
@ -401,22 +355,171 @@ cairo_font_destroy (cairo_font_t *font)
if (--(font->refcount) > 0)
return;
_cairo_glyph_cache_destroy (font->glyph_cache);
if (font->unscaled)
_cairo_unscaled_font_destroy (font->unscaled);
if (font->backend->destroy)
font->backend->destroy (font);
free (font);
}
void
cairo_font_set_transform (cairo_font_t *font,
cairo_matrix_t *matrix)
{
cairo_matrix_copy (&(font->matrix), matrix);
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);
}
void
cairo_font_current_transform (cairo_font_t *font,
cairo_matrix_t *matrix)
{
cairo_matrix_copy (matrix, &(font->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);
}
/* 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;
}
int
_cairo_glyph_cache_keys_equal (void *cache,
void *k1,
void *k2)
{
cairo_glyph_cache_key_t *a, *b;
a = (cairo_glyph_cache_key_t *) k1;
b = (cairo_glyph_cache_key_t *) k2;
return (a->index == b->index)
&& (a->unscaled == b->unscaled)
&& (a->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]);
}
static cairo_status_t
_image_glyph_cache_create_entry (void *cache,
void *key,
void **return_value)
{
cairo_glyph_cache_key_t *k = (cairo_glyph_cache_key_t *) key;
cairo_image_glyph_cache_entry_t *im;
cairo_status_t status;
im = calloc (1, sizeof (cairo_image_glyph_cache_entry_t));
if (im == NULL)
return CAIRO_STATUS_NO_MEMORY;
im->key = *k;
status = im->key.unscaled->backend->create_glyph (im);
if (status != CAIRO_STATUS_SUCCESS) {
free (im);
return status;
}
_cairo_unscaled_font_reference (im->key.unscaled);
im->key.base.memory =
sizeof (cairo_image_glyph_cache_entry_t)
+ (im->image ?
sizeof (cairo_image_surface_t)
+ 28 * sizeof (int) /* rough guess at size of pixman image structure */
+ (im->image->height * im->image->stride) : 0);
*return_value = im;
return CAIRO_STATUS_SUCCESS;
}
static void
_image_glyph_cache_destroy_entry (void *cache,
void *value)
{
cairo_image_glyph_cache_entry_t *im;
im = (cairo_image_glyph_cache_entry_t *) value;
_cairo_unscaled_font_destroy (im->key.unscaled);
cairo_surface_destroy (&(im->image->base));
free (im);
}
static void
_image_glyph_cache_destroy_cache (void *cache)
{
free (cache);
}
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
};
void
_cairo_lock_global_image_glyph_cache()
{
/* FIXME: implement locking. */
}
void
_cairo_unlock_global_image_glyph_cache()
{
/* 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;
}

File diff suppressed because it is too large Load diff

View file

@ -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 *

View file

@ -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;
gstate->font = _cairo_font_create (family, slant, weight);
tmp = _cairo_unscaled_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,82 +2015,250 @@ 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;
}
void
_cairo_gstate_set_font_transform (cairo_gstate_t *gstate,
cairo_matrix_t *matrix)
{
cairo_matrix_copy (&gstate->font_matrix, matrix);
}
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).
*
*/
static void
_build_font_scale (cairo_gstate_t *gstate,
cairo_font_scale_t *sc)
{
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_current_font_extents (cairo_gstate_t *gstate,
cairo_font_extents_t *extents)
{
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_font_scale_t sc;
double dummy = 0.0;
cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix);
_build_font_scale (gstate, &sc);
status = _cairo_unscaled_font_font_extents (gstate->font, &sc, extents);
/* The font responded in device space; convert to user space. */
cairo_matrix_transform_distance (&gstate->ctm_inverse,
&dummy,
&extents->ascent);
cairo_matrix_transform_distance (&gstate->ctm_inverse,
&dummy,
&extents->descent);
cairo_matrix_transform_distance (&gstate->ctm_inverse,
&dummy,
&extents->height);
cairo_matrix_transform_distance (&gstate->ctm_inverse,
&extents->max_x_advance,
&extents->max_y_advance);
return status;
}
cairo_status_t
_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
const unsigned char *utf8,
cairo_glyph_t **glyphs,
int *nglyphs)
{
cairo_status_t status;
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) {
dev_x = 0.0;
dev_y = 0.0;
} else {
dev_x = _cairo_fixed_to_double (point.x);
dev_y = _cairo_fixed_to_double (point.y);
}
status = _cairo_unscaled_font_text_to_glyphs (gstate->font,
&sc, utf8, glyphs, nglyphs);
if (status || !glyphs || !nglyphs || !(*glyphs) || !(nglyphs))
return status;
/* The font responded in device space, starting from (0,0); add any
current point offset in device space, and convert to user space. */
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;
}
cairo_status_t
_cairo_gstate_set_font (cairo_gstate_t *gstate,
cairo_font_t *font)
{
if (gstate->font != NULL)
cairo_font_destroy (gstate->font);
gstate->font = font;
cairo_font_reference (gstate->font);
_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;
}
cairo_status_t
_cairo_gstate_text_extents (cairo_gstate_t *gstate,
const unsigned char *utf8,
cairo_text_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);
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;
return status;
}
cairo_status_t
_cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
cairo_glyph_t *glyphs,
@ -2093,129 +2266,43 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
cairo_text_extents_t *extents)
{
cairo_status_t status;
cairo_matrix_t saved_font_matrix;
double scale_x, scale_y;
cairo_glyph_t *transformed_glyphs;
cairo_font_scale_t sc;
int i;
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);
_build_font_scale (gstate, &sc);
status = _cairo_font_glyph_extents (gstate->font,
glyphs, num_glyphs,
extents);
transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t));
if (transformed_glyphs == NULL)
return CAIRO_STATUS_NO_MEMORY;
cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix);
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;
return status;
}
cairo_status_t
_cairo_gstate_show_text (cairo_gstate_t *gstate,
const unsigned char *utf8)
{
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;
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);
_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)
return status;
if (gstate->clip.surface)
for (i = 0; i < num_glyphs; ++i)
{
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);
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);
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);
if (status)
goto BAIL;
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);
BAIL:
cairo_surface_destroy (intermediate);
transformed_glyphs[i] = glyphs[i];
cairo_matrix_transform_point (&gstate->ctm,
&transformed_glyphs[i].x,
&transformed_glyphs[i].y);
}
else
{
status = _cairo_font_show_text (gstate->font,
gstate->operator, pattern.source,
gstate->surface,
-pattern.source_offset.x,
-pattern.source_offset.y,
x, y, utf8);
}
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;

454
src/cairo-hash.c Normal file
View file

@ -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;
}

View file

@ -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 */
};

View file

@ -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 */
};

View file

@ -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

View file

@ -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;
}

View file

@ -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

454
src/cairo_cache.c Normal file
View file

@ -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;
}

View file

@ -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. */
static void
_cairo_glyph_cache_reference (cairo_glyph_cache_t *glyph_cache);
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;
cairo_font_t *
_cairo_font_create (const char *family,
cairo_font_slant_t slant,
cairo_font_weight_t weight)
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 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,336 +98,250 @@ _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;
}
static void
_font_cache_destroy_entry (void *cache,
void *entry)
{
cairo_font_cache_entry_t *e;
e = (cairo_font_cache_entry_t *) entry;
_cairo_unscaled_font_destroy (e->unscaled);
free ((void *) e->key.family);
free (e);
}
static void
_font_cache_destroy_cache (void *cache)
{
free (cache);
}
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
};
static void
_lock_global_font_cache (void)
{
/* FIXME: implement locking. */
}
static void
_unlock_global_font_cache (void)
{
/* FIXME: implement locking. */
}
static cairo_cache_t *
_global_font_cache = NULL;
static cairo_cache_t *
_get_global_font_cache (void)
{
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;
}
/* 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)
{
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;
}
void
_cairo_font_init (cairo_font_t *scaled,
cairo_font_scale_t *scale,
cairo_unscaled_font_t *unscaled)
{
scaled->scale = *scale;
scaled->unscaled = unscaled;
scaled->refcount = 1;
}
cairo_status_t
_cairo_font_init (cairo_font_t *font,
const struct cairo_font_backend *backend)
_cairo_unscaled_font_init (cairo_unscaled_font_t *font,
const struct cairo_font_backend *backend)
{
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;
return CAIRO_STATUS_SUCCESS;
}
cairo_font_t *
_cairo_font_copy (cairo_font_t *font)
{
cairo_font_t *newfont = NULL;
char *tmp = NULL;
if (font == NULL || font->backend->copy == NULL)
return NULL;
newfont = font->backend->copy (font);
if (newfont == NULL) {
free (tmp);
return NULL;
cairo_status_t
_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->text_to_glyphs (font, scale, utf8, glyphs, num_glyphs);
}
cairo_status_t
_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->glyph_extents(font, scale, glyphs, num_glyphs, extents);
}
cairo_status_t
_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->glyph_bbox (font, scale, glyphs, num_glyphs, bbox);
}
cairo_status_t
_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)
{
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;
}
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;
/* 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_scale (cairo_font_t *font, double scale)
_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 cairo_matrix_scale (&font->matrix, scale, scale);
return font->backend->glyph_path (font, scale, glyphs, num_glyphs, path);
}
cairo_status_t
_cairo_font_transform (cairo_font_t *font, cairo_matrix_t *matrix)
_cairo_unscaled_font_font_extents (cairo_unscaled_font_t *font,
cairo_font_scale_t *scale,
cairo_font_extents_t *extents)
{
return cairo_matrix_multiply (&font->matrix, matrix, &font->matrix);
return font->backend->font_extents(font, scale, extents);
}
cairo_status_t
_cairo_font_text_extents (cairo_font_t *font,
const unsigned char *utf8,
cairo_text_extents_t *extents)
void
_cairo_unscaled_font_reference (cairo_unscaled_font_t *font)
{
return font->backend->text_extents(font, utf8, extents);
font->refcount++;
}
cairo_status_t
_cairo_font_glyph_extents (cairo_font_t *font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_text_extents_t *extents)
{
return font->backend->glyph_extents(font, glyphs, num_glyphs, extents);
}
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)
{
return font->backend->text_bbox (font, surface, x, y, utf8, bbox);
}
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)
{
return font->backend->glyph_bbox (font, surface, glyphs, num_glyphs, bbox);
}
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)
{
return font->backend->show_text(font, operator, source,
surface, source_x, source_y, x, y, utf8);
}
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)
{
return font->backend->show_glyphs(font, operator, source,
surface, source_x, source_y,
glyphs, num_glyphs);
}
cairo_status_t
_cairo_font_text_path (cairo_font_t *font,
double x,
double y,
const unsigned char *utf8,
cairo_path_t *path)
{
return font->backend->text_path(font, x, y, utf8, path);
}
cairo_status_t
_cairo_font_glyph_path (cairo_font_t *font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_path_t *path)
{
return font->backend->glyph_path(font, glyphs, num_glyphs, path);
}
cairo_status_t
_cairo_font_font_extents (cairo_font_t *font,
cairo_font_extents_t *extents)
{
return font->backend->font_extents(font, extents);
}
static void
_cairo_glyph_cache_pop_last (cairo_glyph_cache_t *glyph_cache)
{
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;
free (remove);
glyph_cache->n_nodes--;
}
}
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;
}
static void
_cairo_glyph_cache_reference (cairo_glyph_cache_t *glyph_cache)
{
if (glyph_cache == NULL)
void
_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font)
{
if (--(font->refcount) > 0)
return;
glyph_cache->ref_count++;
if (font->backend)
font->backend->destroy (font);
}
static void
_cairo_glyph_cache_destroy (cairo_glyph_cache_t *glyph_cache)
{
if (glyph_cache == NULL)
return;
glyph_cache->ref_count--;
if (glyph_cache->ref_count)
return;
while (glyph_cache->last)
_cairo_glyph_cache_pop_last (glyph_cache);
free (glyph_cache);
}
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)
{
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_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)
{
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);
*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++;
}
}
return glyph_surface.surface;
}
/* public font interface follows */
/* Public font API follows. */
void
cairo_font_reference (cairo_font_t *font)
@ -401,22 +355,171 @@ cairo_font_destroy (cairo_font_t *font)
if (--(font->refcount) > 0)
return;
_cairo_glyph_cache_destroy (font->glyph_cache);
if (font->unscaled)
_cairo_unscaled_font_destroy (font->unscaled);
if (font->backend->destroy)
font->backend->destroy (font);
free (font);
}
void
cairo_font_set_transform (cairo_font_t *font,
cairo_matrix_t *matrix)
{
cairo_matrix_copy (&(font->matrix), matrix);
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);
}
void
cairo_font_current_transform (cairo_font_t *font,
cairo_matrix_t *matrix)
{
cairo_matrix_copy (matrix, &(font->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);
}
/* 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;
}
int
_cairo_glyph_cache_keys_equal (void *cache,
void *k1,
void *k2)
{
cairo_glyph_cache_key_t *a, *b;
a = (cairo_glyph_cache_key_t *) k1;
b = (cairo_glyph_cache_key_t *) k2;
return (a->index == b->index)
&& (a->unscaled == b->unscaled)
&& (a->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]);
}
static cairo_status_t
_image_glyph_cache_create_entry (void *cache,
void *key,
void **return_value)
{
cairo_glyph_cache_key_t *k = (cairo_glyph_cache_key_t *) key;
cairo_image_glyph_cache_entry_t *im;
cairo_status_t status;
im = calloc (1, sizeof (cairo_image_glyph_cache_entry_t));
if (im == NULL)
return CAIRO_STATUS_NO_MEMORY;
im->key = *k;
status = im->key.unscaled->backend->create_glyph (im);
if (status != CAIRO_STATUS_SUCCESS) {
free (im);
return status;
}
_cairo_unscaled_font_reference (im->key.unscaled);
im->key.base.memory =
sizeof (cairo_image_glyph_cache_entry_t)
+ (im->image ?
sizeof (cairo_image_surface_t)
+ 28 * sizeof (int) /* rough guess at size of pixman image structure */
+ (im->image->height * im->image->stride) : 0);
*return_value = im;
return CAIRO_STATUS_SUCCESS;
}
static void
_image_glyph_cache_destroy_entry (void *cache,
void *value)
{
cairo_image_glyph_cache_entry_t *im;
im = (cairo_image_glyph_cache_entry_t *) value;
_cairo_unscaled_font_destroy (im->key.unscaled);
cairo_surface_destroy (&(im->image->base));
free (im);
}
static void
_image_glyph_cache_destroy_cache (void *cache)
{
free (cache);
}
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
};
void
_cairo_lock_global_image_glyph_cache()
{
/* FIXME: implement locking. */
}
void
_cairo_unlock_global_image_glyph_cache()
{
/* 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;
}

File diff suppressed because it is too large Load diff

View file

@ -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 *

View file

@ -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;
gstate->font = _cairo_font_create (family, slant, weight);
tmp = _cairo_unscaled_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,82 +2015,250 @@ 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;
}
void
_cairo_gstate_set_font_transform (cairo_gstate_t *gstate,
cairo_matrix_t *matrix)
{
cairo_matrix_copy (&gstate->font_matrix, matrix);
}
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).
*
*/
static void
_build_font_scale (cairo_gstate_t *gstate,
cairo_font_scale_t *sc)
{
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_current_font_extents (cairo_gstate_t *gstate,
cairo_font_extents_t *extents)
{
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_font_scale_t sc;
double dummy = 0.0;
cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix);
_build_font_scale (gstate, &sc);
status = _cairo_unscaled_font_font_extents (gstate->font, &sc, extents);
/* The font responded in device space; convert to user space. */
cairo_matrix_transform_distance (&gstate->ctm_inverse,
&dummy,
&extents->ascent);
cairo_matrix_transform_distance (&gstate->ctm_inverse,
&dummy,
&extents->descent);
cairo_matrix_transform_distance (&gstate->ctm_inverse,
&dummy,
&extents->height);
cairo_matrix_transform_distance (&gstate->ctm_inverse,
&extents->max_x_advance,
&extents->max_y_advance);
return status;
}
cairo_status_t
_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
const unsigned char *utf8,
cairo_glyph_t **glyphs,
int *nglyphs)
{
cairo_status_t status;
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) {
dev_x = 0.0;
dev_y = 0.0;
} else {
dev_x = _cairo_fixed_to_double (point.x);
dev_y = _cairo_fixed_to_double (point.y);
}
status = _cairo_unscaled_font_text_to_glyphs (gstate->font,
&sc, utf8, glyphs, nglyphs);
if (status || !glyphs || !nglyphs || !(*glyphs) || !(nglyphs))
return status;
/* The font responded in device space, starting from (0,0); add any
current point offset in device space, and convert to user space. */
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;
}
cairo_status_t
_cairo_gstate_set_font (cairo_gstate_t *gstate,
cairo_font_t *font)
{
if (gstate->font != NULL)
cairo_font_destroy (gstate->font);
gstate->font = font;
cairo_font_reference (gstate->font);
_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;
}
cairo_status_t
_cairo_gstate_text_extents (cairo_gstate_t *gstate,
const unsigned char *utf8,
cairo_text_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);
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;
return status;
}
cairo_status_t
_cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
cairo_glyph_t *glyphs,
@ -2093,129 +2266,43 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
cairo_text_extents_t *extents)
{
cairo_status_t status;
cairo_matrix_t saved_font_matrix;
double scale_x, scale_y;
cairo_glyph_t *transformed_glyphs;
cairo_font_scale_t sc;
int i;
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);
_build_font_scale (gstate, &sc);
status = _cairo_font_glyph_extents (gstate->font,
glyphs, num_glyphs,
extents);
transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t));
if (transformed_glyphs == NULL)
return CAIRO_STATUS_NO_MEMORY;
cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix);
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;
return status;
}
cairo_status_t
_cairo_gstate_show_text (cairo_gstate_t *gstate,
const unsigned char *utf8)
{
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;
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);
_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)
return status;
if (gstate->clip.surface)
for (i = 0; i < num_glyphs; ++i)
{
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);
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);
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);
if (status)
goto BAIL;
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);
BAIL:
cairo_surface_destroy (intermediate);
transformed_glyphs[i] = glyphs[i];
cairo_matrix_transform_point (&gstate->ctm,
&transformed_glyphs[i].x,
&transformed_glyphs[i].y);
}
else
{
status = _cairo_font_show_text (gstate->font,
gstate->operator, pattern.source,
gstate->surface,
-pattern.source_offset.x,
-pattern.source_offset.y,
x, y, utf8);
}
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;

View file

@ -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 */
};

View file

@ -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 */
};

View file

@ -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 */
};

View file

@ -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

View file

@ -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;
}

View file

@ -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,
@ -955,19 +1094,11 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
int num_glyphs,
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,
@ -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_unscaled_font_t * __internal_linkage
_cairo_unscaled_font_create (const char *family,
cairo_font_slant_t slant,
cairo_font_weight_t weight);
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_init (cairo_font_t *font,
const struct cairo_font_backend *backend);
_cairo_unscaled_font_init (cairo_unscaled_font_t *font,
const struct cairo_font_backend *backend);
extern cairo_font_t * __internal_linkage
_cairo_font_copy (cairo_font_t *font);
extern void __internal_linkage
_cairo_unscaled_font_reference (cairo_unscaled_font_t *font);
extern void __internal_linkage
_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font);
extern cairo_status_t __internal_linkage
_cairo_font_scale (cairo_font_t *font, double scale);
_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_transform (cairo_font_t *font, cairo_matrix_t *matrix);
_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_font_extents (cairo_font_t *font,
cairo_font_extents_t *extents);
_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_text_extents (cairo_font_t *font,
const unsigned char *utf8,
cairo_text_extents_t *extents);
_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_glyph_extents (cairo_font_t *font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_text_extents_t *extents);
_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_text_bbox (cairo_font_t *font,
cairo_surface_t *surface,
double x,
double y,
const unsigned char *utf8,
cairo_box_t *bbox);
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);
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);
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);
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);
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