Split out scaled font code to cairo-scaled-font.c

Replace cairo cache implementation (this code from cworth)
No more global glyph cache to clean up
Store glyphs in new per-scaled font caches which hold user-space metrics and device space bounding boxes
Refactor glyph drawing APIs so that the surface API is invoked directly from the gstate code.
Add path creation/destruction routines (to hold glyph paths)
New implementation of scaled fonts which uses per-scaled_font caches for glyphs and keeps user-space metrics, device-space bboxes along with glyph images and/or glyph paths.
Adapt to new scaled font API changes.
New cache and scaled_font APIs
Repond to bug fix in metrics computation for glyphs where y values were rounded up instead of down because of a sign difference between cairo and FreeType.
Reviewed by: otaylor, cworth
This commit is contained in:
Keith Packard 2005-08-31 15:08:02 +00:00
parent 568ce86026
commit b0c58593b3
22 changed files with 2540 additions and 2913 deletions

View file

@ -1,3 +1,98 @@
2005-08-31 Keith Packard <keithp@keithp.com>
Reviewed by: otaylor, cworth
* src/Makefile.am:
Split out scaled font code to cairo-scaled-font.c
* src/cairo-cache-private.h:
* src/cairo-cache.c: (_cairo_cache_init), (_cairo_cache_fini),
(_cairo_cache_create), (_cairo_cache_destroy),
(_cairo_cache_preserve), (_cairo_cache_release),
(_cairo_cache_lookup), (_cairo_cache_remove_random),
(_cairo_cache_insert), (_cairo_cache_remove),
(_cairo_cache_foreach):
Replace cairo cache implementation (this code from cworth)
* src/cairo-debug.c: (cairo_debug_reset_static_data):
* src/cairo-font.c: (_cairo_font_reset_static_data):
No more global glyph cache to clean up
* src/cairo-ft-font.c: (_get_bitmap_surface),
(_render_glyph_outline), (_render_glyph_bitmap),
(_transform_glyph_bitmap), (_get_pattern_ft_options),
(_cairo_ft_scaled_font_create), (_cairo_ft_scaled_font_create_toy),
(_decompose_glyph_outline), (_cairo_ft_scaled_glyph_init),
(_cairo_ft_ucs4_to_index), (_cairo_ft_show_glyphs),
(_cairo_ft_font_face_scaled_font_create),
(_cairo_ft_font_face_create),
(cairo_ft_font_face_create_for_pattern),
(cairo_ft_font_face_create_for_ft_face):
Store glyphs in new per-scaled font caches which
hold user-space metrics and device space bounding boxes
* src/cairo-gstate.c: (_cairo_gstate_text_to_glyphs),
(_cairo_gstate_show_glyphs_draw_func), (_cairo_gstate_show_glyphs):
Refactor glyph drawing APIs so that the surface API is
invoked directly from the gstate code.
* src/cairo-path.c: (_cairo_path_fixed_create),
(_cairo_path_fixed_destroy):
Add path creation/destruction routines (to hold glyph paths)
* src/cairo-scaled-font.c: (_cairo_scaled_glyph_keys_equal),
(_cairo_scaled_glyph_fini), (_cairo_scaled_glyph_destroy),
(_cairo_scaled_font_set_error), (cairo_scaled_font_status),
(_cairo_scaled_font_map_lock), (_cairo_scaled_font_map_unlock),
(_cairo_scaled_font_map_destroy), (_hash_bytes_fnv),
(_cairo_scaled_font_init_key), (_cairo_scaled_font_keys_equal),
(_cairo_scaled_font_init), (_cairo_scaled_font_set_metrics),
(_cairo_scaled_font_fini), (cairo_scaled_font_create),
(cairo_scaled_font_reference), (cairo_scaled_font_destroy),
(cairo_scaled_font_extents), (cairo_scaled_font_glyph_extents),
(_cairo_scaled_font_text_to_glyphs),
(_cairo_scaled_font_glyph_device_extents),
(_cairo_scaled_font_show_glyphs), (_scaled_glyph_path_move_to),
(_scaled_glyph_path_line_to), (_scaled_glyph_path_curve_to),
(_scaled_glyph_path_close_path), (_cairo_scaled_font_glyph_path),
(_cairo_scaled_glyph_set_metrics),
(_cairo_scaled_glyph_set_surface), (_cairo_scaled_glyph_set_path),
(_cairo_scaled_glyph_lookup):
New implementation of scaled fonts which uses per-scaled_font
caches for glyphs and keeps user-space metrics, device-space bboxes
along with glyph images and/or glyph paths.
* src/cairo-xlib-surface.c:
(_cairo_xlib_surface_create_similar_with_format),
(_cairo_xlib_surface_create_similar), (_cairo_xlib_surface_finish),
(_cairo_xlib_surface_clone_similar),
(_cairo_xlib_surface_font_init),
(_cairo_xlib_surface_scaled_font_fini),
(_cairo_xlib_surface_scaled_glyph_fini),
(_cairo_xlib_surface_add_glyph),
(_cairo_xlib_surface_show_glyphs8),
(_cairo_xlib_surface_show_glyphs16),
(_cairo_xlib_surface_show_glyphs32),
(_cairo_xlib_surface_show_glyphs):
Adapt to new scaled font API changes.
* src/cairoint.h:
New cache and scaled_font APIs
* test/clip-operator-ref.png:
* test/operator-clear-ref.png:
* test/operator-source-ref.png:
* test/text-antialias-gray-ref.png:
* test/text-antialias-gray.c:
* test/text-antialias-none-ref.png:
* test/text-antialias-none.c:
* test/text-antialias-subpixel-ref.png:
* test/text-antialias-subpixel.c:
* test/unbounded-operator-ref.png:
Repond to bug fix in metrics computation for glyphs
where y values were rounded up instead of down
because of a sign difference between cairo and FreeType.
2005-08-31 Carl Worth <cworth@cworth.org>
* configure.in: Increment CAIRO_VERSION to 1.1.1 after making

View file

@ -121,6 +121,7 @@ libcairo_la_SOURCES = \
cairo-pen.c \
cairo-polygon.c \
cairo-region.c \
cairo-scaled-font.c \
cairo-slope.c \
cairo-spline.c \
cairo-surface.c \

131
src/cairo-cache-private.h Normal file
View file

@ -0,0 +1,131 @@
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2004 Red Hat, Inc.
* Copyright © 2005 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Red Hat, Inc.
*
* Contributor(s):
* Keith Packard <keithp@keithp.com>
* Graydon Hoare <graydon@redhat.com>
* Carl Worth <cworth@cworth.org>
*/
#ifndef CAIRO_CACHE_PRIVATE_H
#define CAIRO_CACHE_PRIVATE_H
typedef struct _cairo_cache cairo_cache_t;
/**
* cairo_cache_entry_t:
*
* A #cairo_cache_entry_t contains both a key and a value for
* cairo_cache_t. User-derived types for cairo_cache_entry_t must
* be type-compatible with this structure (eg. they must have an
* unsigned long as the first parameter. The easiest way to get this
* is to use:
*
* typedef _my_entry {
* cairo_cache_entry_t base;
* ... Remainder of key and value fields here ..
* } my_entry_t;
*
* which then allows a pointer to my_entry_t to be passed to any of
* the cairo_cache functions as follows without requiring a cast:
*
* _cairo_cache_insert (cache, &my_entry->base, size);
*
* IMPORTANT: The caller is responsible for initializing
* my_entry->base.hash with a hash code derived from the key. The
* essential property of the hash code is that keys_equal must never
* return TRUE for two keys that have different hashes. The best hash
* code will reduce the frequency of two keys with the same code for
* which keys_equal returns FALSE.
*
* The user must also initialize my_entry->base.size to indicate
* the size of the current entry. What units to use for size is
* entirely up to the caller, (though the same units must be used for
* the max_size parameter passed to _cairo_cache_create). If all
* entries are close to the same size, the simplest thing to do is to
* just use units of "entries", (eg. set size==1 in all entries and
* set max_size to the number of entries which you want to be saved
* in the cache).
*
* Which parts of the entry make up the "key" and which part make up
* the value are entirely up to the caller, (as determined by the
* computation going into base.hash as well as the keys_equal
* function). A few of the cairo_cache functions accept an entry which
* will be used exclusively as a "key", (indicated by a parameter name
* of key). In these cases, the value-related fields of the entry need
* not be initialized if so desired.
**/
typedef struct _cairo_cache_entry {
unsigned long hash;
unsigned long size;
} cairo_cache_entry_t;
typedef cairo_bool_t
(*cairo_cache_keys_equal_func_t) (void *key_a, void *key_b);
typedef void
(*cairo_cache_callback_func_t) (void *entry,
void *closure);
cairo_private cairo_cache_t *
_cairo_cache_create (cairo_cache_keys_equal_func_t keys_equal,
cairo_destroy_func_t entry_destroy,
unsigned long max_size);
cairo_private void
_cairo_cache_destroy (cairo_cache_t *cache);
cairo_private void
_cairo_cache_preserve (cairo_cache_t *cache);
cairo_private void
_cairo_cache_release (cairo_cache_t *cache);
cairo_private cairo_bool_t
_cairo_cache_lookup (cairo_cache_t *cache,
cairo_cache_entry_t *key,
cairo_cache_entry_t **entry_return);
cairo_private cairo_status_t
_cairo_cache_insert (cairo_cache_t *cache,
cairo_cache_entry_t *entry);
cairo_private void
_cairo_cache_remove (cairo_cache_t *cache,
cairo_cache_entry_t *key);
cairo_private void
_cairo_cache_foreach (cairo_cache_t *cache,
cairo_cache_callback_func_t cache_callback,
void *closure);
#endif

View file

@ -1,6 +1,7 @@
/* cairo - a vector graphics library with display and print output
*
* This file is Copyright © 2004 Red Hat, Inc.
* Copyright © 2004 Red Hat, Inc.
* Copyright © 2005 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@ -30,481 +31,298 @@
* The Initial Developer of the Original Code is Red Hat, Inc.
*
* Contributor(s):
* Keith Packard
* Keith Packard <keithp@keithp.com>
* Graydon Hoare <graydon@redhat.com>
* Carl Worth <cworth@cworth.org>
*/
#include "cairoint.h"
/*
* This structure, and accompanying table, is borrowed/modified from the
* file xserver/render/glyph.c in the freedesktop.org x server, with
* permission (and suggested modification of doubling sizes) by Keith
* Packard.
*/
struct _cairo_cache {
cairo_hash_table_t *hash_table;
static const cairo_cache_arrangement_t cache_arrangements [] = {
{ 16, 43, 41 },
{ 32, 73, 71 },
{ 64, 151, 149 },
{ 128, 283, 281 },
{ 256, 571, 569 },
{ 512, 1153, 1151 },
{ 1024, 2269, 2267 },
{ 2048, 4519, 4517 },
{ 4096, 9013, 9011 },
{ 8192, 18043, 18041 },
{ 16384, 36109, 36107 },
{ 32768, 72091, 72089 },
{ 65536, 144409, 144407 },
{ 131072, 288361, 288359 },
{ 262144, 576883, 576881 },
{ 524288, 1153459, 1153457 },
{ 1048576, 2307163, 2307161 },
{ 2097152, 4613893, 4613891 },
{ 4194304, 9227641, 9227639 },
{ 8388608, 18455029, 18455027 },
{ 16777216, 36911011, 36911009 },
{ 33554432, 73819861, 73819859 },
{ 67108864, 147639589, 147639587 },
{ 134217728, 295279081, 295279079 },
{ 268435456, 590559793, 590559791 }
cairo_destroy_func_t entry_destroy;
unsigned long max_size;
unsigned long size;
cairo_bool_t preserve_entries;
};
#define N_CACHE_SIZES (sizeof(cache_arrangements)/sizeof(cache_arrangements[0]))
/*
* Entries 'e' are poiners, in one of 3 states:
*
* e == NULL: The entry has never had anything put in it
* e != DEAD_ENTRY: The entry has an active value in it currently
* e == DEAD_ENTRY: The entry *had* a value in it at some point, but the
* entry has been killed. Lookups requesting free space can
* reuse these entries; lookups requesting a precise match
* should neither return these entries nor stop searching when
* seeing these entries.
*
* We expect keys will not be destroyed frequently, so our table does not
* contain any explicit shrinking code nor any chain-coalescing code for
* entries randomly deleted by memory pressure (except during rehashing, of
* course). These assumptions are potentially bad, but they make the
* implementation straightforward.
*
* Revisit later if evidence appears that we're using excessive memory from
* a mostly-dead table.
*
* Generally you do not need to worry about freeing cache entries; the
* cache will expire entries randomly as it experiences memory pressure.
* If max_memory is set, entries are not expired, and must be explicitely
* removed.
*
* This table is open-addressed with double hashing. Each table size is a
* prime chosen to be a little more than double the high water mark for a
* given arrangement, so the tables should remain < 50% full. The table
* size makes for the "first" hash modulus; a second prime (2 less than the
* first prime) serves as the "second" hash modulus, which is co-prime and
* thus guarantees a complete permutation of table indices.
*
*/
#define DEAD_ENTRY ((cairo_cache_entry_base_t *) 1)
#define NULL_ENTRY_P(cache, i) ((cache)->entries[i] == NULL)
#define DEAD_ENTRY_P(cache, i) ((cache)->entries[i] == DEAD_ENTRY)
#define LIVE_ENTRY_P(cache, i) \
(!((NULL_ENTRY_P((cache),(i))) || (DEAD_ENTRY_P((cache),(i)))))
#ifdef NDEBUG
#define _cache_sane_state(c)
#else
static void
_cache_sane_state (cairo_cache_t *cache)
static cairo_status_t
_cairo_cache_init (cairo_cache_t *cache,
cairo_cache_keys_equal_func_t keys_equal,
cairo_destroy_func_t entry_destroy,
unsigned long max_size)
{
assert (cache != NULL);
assert (cache->entries != NULL);
assert (cache->backend != NULL);
assert (cache->arrangement != NULL);
/* Cannot check this, a single object may larger */
/* assert (cache->used_memory <= cache->max_memory); */
assert (cache->live_entries <= cache->arrangement->size);
cache->hash_table = _cairo_hash_table_create (keys_equal);
if (cache->hash_table == NULL)
return CAIRO_STATUS_NO_MEMORY;
cache->entry_destroy = entry_destroy;
cache->max_size = max_size;
cache->size = 0;
cache->preserve_entries = FALSE;
return CAIRO_STATUS_SUCCESS;
}
#endif
static void
_entry_destroy (cairo_cache_t *cache, unsigned long i)
_cairo_cache_fini (cairo_cache_t *cache)
{
_cache_sane_state (cache);
cairo_cache_entry_t *entry;
if (LIVE_ENTRY_P(cache, i))
{
cairo_cache_entry_base_t *entry = cache->entries[i];
assert(cache->live_entries > 0);
assert(cache->used_memory >= entry->memory);
/* We have to manually remove all entries from the cache ourselves
* rather than relying on _cairo_hash_table_destroy to do that
* since otherwise the cache->entry_destroy callback would not get
* called on each entry. */
cache->live_entries--;
cache->used_memory -= entry->memory;
cache->backend->destroy_entry (cache, entry);
cache->entries[i] = DEAD_ENTRY;
}
}
static cairo_cache_entry_base_t **
_cache_lookup (cairo_cache_t *cache,
void *key,
int (*predicate)(void*,void*,void*))
{
cairo_cache_entry_base_t **probe;
unsigned long hash;
unsigned long table_size, i, idx, step;
_cache_sane_state (cache);
assert (key != NULL);
table_size = cache->arrangement->size;
hash = cache->backend->hash (cache, key);
idx = hash % table_size;
step = 0;
for (i = 0; i < table_size; ++i)
{
#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE
cache->probes++;
#endif
assert(idx < table_size);
probe = cache->entries + idx;
/*
* There are two lookup modes: searching for a free slot and searching
* for an exact entry.
*/
if (predicate != NULL)
{
/* We are looking up an exact entry. */
if (*probe == NULL)
{
/* Found an empty spot, there can't be a match */
break;
}
else if (*probe != DEAD_ENTRY
&& (*probe)->hashcode == hash
&& predicate (cache, key, *probe))
{
return probe;
}
}
else
{
/* We are just looking for a free slot. */
if (*probe == NULL
|| *probe == DEAD_ENTRY)
{
return probe;
}
}
if (step == 0) {
step = hash % cache->arrangement->rehash;
if (step == 0)
step = 1;
}
idx += step;
if (idx >= table_size)
idx -= table_size;
while (1) {
entry = _cairo_hash_table_random_entry (cache->hash_table, NULL);
if (entry == NULL)
break;
_cairo_cache_remove (cache, entry);
}
/*
* The table should not have permitted you to get here if you were just
* looking for a free slot: there should have been room.
*/
assert(predicate != NULL);
return NULL;
_cairo_hash_table_destroy (cache->hash_table);
cache->size = 0;
}
static cairo_cache_entry_base_t **
_find_available_entry_for (cairo_cache_t *cache,
void *key)
/**
* _cairo_cache_create:
* @keys_equal: a function to return TRUE if two keys are equal
* @entry_destroy: destroy notifier for cache entries
* @max_size: the maximum size for this cache
*
* Creates a new cache using the keys_equal() function to determine
* the equality of entries.
*
* Data is provided to the cache in the form of user-derived version
* of cairo_cache_entry_t. A cache entry must be able to hold hash
* code, a size, and the key/value pair being stored in the
* cache. Sometimes only the key will be necessary, (as in
* _cairo_cache_remove()), and in these cases the value portion of the
* entry need not be initialized.
*
* The units for max_size can be chosen by the caller, but should be
* consistent with the units of the size field of cache entries. When
* adding an entry with _cairo_cache_insert if the total size of
* entries in the cache would exceed max_size then entries will be
* removed at random until the new entry would fit or the cache is
* empty. Then the new entry is inserted.
*
* There are cases in which the automatic removal of entries is
* undesired. If the cache entries have reference counts, then it is a
* simple matter to use the reference counts to ensure that entries
* continue to live even after being ejected from the cache. However,
* in some cases the memory overhead of adding a reference count to
* the entry would be objectionable. In such cases, the
* _cairo_cache_preserve() and _cairo_cache_release() calls can be
* used to establish a window during which no automatic removal of
* entries will occur.
*
* Return value:
**/
cairo_cache_t *
_cairo_cache_create (cairo_cache_keys_equal_func_t keys_equal,
cairo_destroy_func_t entry_destroy,
unsigned long max_size)
{
return _cache_lookup (cache, key, NULL);
}
cairo_status_t status;
cairo_cache_t *cache;
static cairo_cache_entry_base_t **
_find_exact_live_entry_for (cairo_cache_t *cache,
void *key)
{
return _cache_lookup (cache, key, cache->backend->keys_equal);
}
cache = malloc (sizeof (cairo_cache_t));
if (cache == NULL)
return NULL;
static const cairo_cache_arrangement_t *
_find_cache_arrangement (unsigned long proposed_size)
{
unsigned long idx;
for (idx = 0; idx < N_CACHE_SIZES; ++idx)
if (cache_arrangements[idx].high_water_mark >= proposed_size)
return &cache_arrangements[idx];
return NULL;
}
static cairo_status_t
_resize_cache (cairo_cache_t *cache, unsigned long proposed_size)
{
cairo_cache_t tmp;
cairo_cache_entry_base_t **e;
unsigned long new_size, i;
tmp = *cache;
tmp.arrangement = _find_cache_arrangement (proposed_size);
assert(tmp.arrangement != NULL);
if (tmp.arrangement == cache->arrangement)
return CAIRO_STATUS_SUCCESS;
new_size = tmp.arrangement->size;
tmp.entries = calloc (new_size, sizeof (cairo_cache_entry_base_t *));
if (tmp.entries == NULL)
return CAIRO_STATUS_NO_MEMORY;
for (i = 0; i < cache->arrangement->size; ++i) {
if (LIVE_ENTRY_P(cache, i)) {
e = _find_available_entry_for (&tmp, cache->entries[i]);
assert (e != NULL);
*e = cache->entries[i];
}
}
free (cache->entries);
cache->entries = tmp.entries;
cache->arrangement = tmp.arrangement;
return CAIRO_STATUS_SUCCESS;
}
#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE
static double
_load_factor (cairo_cache_t *cache)
{
return ((double) cache->live_entries)
/ ((double) cache->arrangement->size);
}
#endif
/* Find a random in the cache matching the given predicate. We use the
* same algorithm as the probing algorithm to walk over the entries in
* the hash table in a pseudo-random order. Walking linearly would
* favor entries following gaps in the hash table. We could also
* call rand() repeatedly, which works well for almost-full tables,
* but degrades when the table is almost empty, or predicate
* returns false for most entries.
*/
static cairo_cache_entry_base_t **
_random_entry (cairo_cache_t *cache,
int (*predicate)(void*))
{
cairo_cache_entry_base_t **probe;
unsigned long hash;
unsigned long table_size, i, idx, step;
_cache_sane_state (cache);
table_size = cache->arrangement->size;
hash = rand ();
idx = hash % table_size;
step = 0;
for (i = 0; i < table_size; ++i)
{
assert(idx < table_size);
probe = cache->entries + idx;
if (LIVE_ENTRY_P(cache, idx)
&& (!predicate || predicate (*probe)))
return probe;
if (step == 0) {
step = hash % cache->arrangement->rehash;
if (step == 0)
step = 1;
}
idx += step;
if (idx >= table_size)
idx -= table_size;
status = _cairo_cache_init (cache, keys_equal, entry_destroy, max_size);
if (status) {
free (cache);
return NULL;
}
return NULL;
}
/* public API follows */
cairo_status_t
_cairo_cache_init (cairo_cache_t *cache,
const cairo_cache_backend_t *backend,
unsigned long max_memory)
{
assert (backend != NULL);
if (cache != NULL){
cache->arrangement = &cache_arrangements[0];
cache->max_memory = max_memory;
cache->used_memory = 0;
cache->live_entries = 0;
#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE
cache->hits = 0;
cache->misses = 0;
cache->probes = 0;
#endif
cache->backend = backend;
cache->entries = calloc (cache->arrangement->size,
sizeof(cairo_cache_entry_base_t *));
if (cache->entries == NULL)
return CAIRO_STATUS_NO_MEMORY;
}
_cache_sane_state (cache);
return CAIRO_STATUS_SUCCESS;
return cache;
}
/**
* _cairo_cache_destroy:
* @cache: a cache to destroy
*
* Immediately destroys the given cache, freeing all resources
* associated with it. As part of this process, the entry_destroy()
* function, (as passed to _cairo_cache_create), will be called for
* each entry in the cache.
**/
void
_cairo_cache_destroy (cairo_cache_t *cache)
{
unsigned long i;
if (cache == NULL)
return;
_cache_sane_state (cache);
_cairo_cache_fini (cache);
for (i = 0; i < cache->arrangement->size; ++i)
_entry_destroy (cache, i);
free (cache->entries);
cache->entries = NULL;
cache->backend->destroy_cache (cache);
free (cache);
}
/**
* _cairo_cache_preserve:
* @cache: a cache with some precious entries in it (or about to be
* added)
*
* Disable the automatic ejection of entries from the cache. Future
* calls to _cairo_cache_insert will add new entries to the cache
* regardless of how large the cache grows. See
* _cairo_cache_release().
**/
void
_cairo_cache_shrink_to (cairo_cache_t *cache,
unsigned long max_memory)
_cairo_cache_preserve (cairo_cache_t *cache)
{
unsigned long idx;
/* Make some entries die if we're under memory pressure. */
while (cache->live_entries > 0 && cache->used_memory > max_memory) {
idx = _random_entry (cache, NULL) - cache->entries;
assert (idx < cache->arrangement->size);
_entry_destroy (cache, idx);
}
cache->preserve_entries = TRUE;
}
cairo_status_t
_cairo_cache_lookup (cairo_cache_t *cache,
void *key,
void **entry_return,
int *created_entry)
/**
* _cairo_cache_release:
* @cache: a cache, just after the entries in it have become less
* previous
*
* Cancel the effects of _cairo_cache_preserve(). That is, allow the
* cache to resume ejecting entries when it is larger than max_size as
* passed to cairo_cache_create. If the cache is already larger than
* max_size, no entries will be immediately removed, but the cache
* will be brought down to size at the time of the next call to
* _cairo_cache_insert.
**/
void
_cairo_cache_release (cairo_cache_t *cache)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_cache_entry_base_t **slot = NULL, *new_entry;
_cache_sane_state (cache);
#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE
if ((cache->hits + cache->misses) % 0xffff == 0)
printf("cache %p stats: size %ld, live %ld, load %.2f\n"
" mem %ld/%ld, hit %ld, miss %ld\n"
" probe %ld, %.2f probe/access\n",
cache,
cache->arrangement->size,
cache->live_entries,
_load_factor (cache),
cache->used_memory,
cache->max_memory,
cache->hits,
cache->misses,
cache->probes,
((double) cache->probes)
/ ((double) (cache->hits +
cache->misses + 1)));
#endif
/* See if we have an entry in the table already. */
slot = _find_exact_live_entry_for (cache, key);
if (slot != NULL) {
#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE
cache->hits++;
#endif
*entry_return = *slot;
if (created_entry)
*created_entry = 0;
return status;
}
#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE
cache->misses++;
#endif
/* Build the new entry. */
status = cache->backend->create_entry (cache, key,
(void **)&new_entry);
if (status != CAIRO_STATUS_SUCCESS)
return status;
/* Store the hash value in case the backend forgot. */
new_entry->hashcode = cache->backend->hash (cache, key);
if (cache->live_entries && cache->max_memory)
_cairo_cache_shrink_to (cache, cache->max_memory);
/* Can't assert this; new_entry->memory may be larger than max_memory */
/* assert(cache->max_memory >= (cache->used_memory + new_entry->memory)); */
/* Make room in the table for a new slot. */
status = _resize_cache (cache, cache->live_entries + 1);
if (status != CAIRO_STATUS_SUCCESS) {
cache->backend->destroy_entry (cache, new_entry);
return status;
}
slot = _find_available_entry_for (cache, key);
assert(slot != NULL);
/* Store entry in slot and increment statistics. */
*slot = new_entry;
cache->live_entries++;
cache->used_memory += new_entry->memory;
_cache_sane_state (cache);
*entry_return = new_entry;
if (created_entry)
*created_entry = 1;
return status;
cache->preserve_entries = FALSE;
}
cairo_status_t
_cairo_cache_remove (cairo_cache_t *cache,
void *key)
/**
* _cairo_cache_lookup:
* @cache: a cache
* @key: the key of interest
* @entry_return: pointer for return value
*
* Performs a lookup in @cache looking for an entry which has a key
* that matches @key, (as determined by the keys_equal() function
* passed to _cairo_cache_create).
*
* Return value: TRUE if there is an entry in the cache that matches
* @key, (which will now be in *entry_return). FALSE otherwise, (in
* which case *entry_return will be NULL).
**/
cairo_private cairo_bool_t
_cairo_cache_lookup (cairo_cache_t *cache,
cairo_cache_entry_t *key,
cairo_cache_entry_t **entry_return)
{
cairo_cache_entry_base_t **slot;
return _cairo_hash_table_lookup (cache->hash_table,
(cairo_hash_entry_t *) key,
(cairo_hash_entry_t **) entry_return);
}
_cache_sane_state (cache);
/**
* _cairo_cache_remove_random:
* @cache: a cache
*
* Remove a random entry from the cache.
*
* Return value: CAIRO_STATUS_SUCCESS if an entry was successfully
* removed. CAIRO_INT_STATUS_CACHE_EMPTY if there are no entries that
* can be removed.
**/
static cairo_int_status_t
_cairo_cache_remove_random (cairo_cache_t *cache)
{
cairo_cache_entry_t *entry;
/* See if we have an entry in the table already. */
slot = _find_exact_live_entry_for (cache, key);
if (slot != NULL)
_entry_destroy (cache, slot - cache->entries);
entry = _cairo_hash_table_random_entry (cache->hash_table, NULL);
if (entry == NULL)
return CAIRO_INT_STATUS_CACHE_EMPTY;
_cairo_cache_remove (cache, entry);
return CAIRO_STATUS_SUCCESS;
}
void *
_cairo_cache_random_entry (cairo_cache_t *cache,
int (*predicate)(void*))
/**
* _cairo_cache_insert:
* @cache: a cache
* @entry: an entry to be inserted
*
* Insert @entry into the cache. If an entry exists in the cache with
* a matching key, then the old entry will be removed first, (and the
* entry_destroy() callback will be called on it).
*
* Return value: CAIRO_STATUS_SUCCESS if successful or
* CAIRO_STATUS_NO_MEMORY if insufficient memory is available.
**/
cairo_private cairo_status_t
_cairo_cache_insert (cairo_cache_t *cache,
cairo_cache_entry_t *entry)
{
cairo_cache_entry_base_t **slot = _random_entry (cache, predicate);
cairo_status_t status;
return slot ? *slot : NULL;
if (! cache->preserve_entries) {
while (cache->size + entry->size > cache->max_size) {
status = _cairo_cache_remove_random (cache);
if (status) {
if (status == CAIRO_INT_STATUS_CACHE_EMPTY)
break;
return status;
}
}
}
status = _cairo_hash_table_insert (cache->hash_table,
(cairo_hash_entry_t *) entry);
if (status)
return status;
cache->size += entry->size;
return CAIRO_STATUS_SUCCESS;
}
/**
* _cairo_cache_remove:
* @cache: a cache
* @entry: key of entry to be removed
*
* Remove an entry from the cache which has a key that matches @key,
* if any (as determined by the keys_equal() function passed to
* _cairo_cache_create).
**/
cairo_private void
_cairo_cache_remove (cairo_cache_t *cache,
cairo_cache_entry_t *entry)
{
cache->size -= entry->size;
_cairo_hash_table_remove (cache->hash_table,
(cairo_hash_entry_t *) entry);
if (cache->entry_destroy)
cache->entry_destroy (entry);
}
/**
* _cairo_cache_foreach:
* @cache: a cache
* @cache_callback: function to be called for each entry
* @closure: additional argument to be passed to @cache_callback
*
* Call @cache_callback for each entry in the cache, in a
* non-specified order.
**/
void
_cairo_cache_foreach (cairo_cache_t *cache,
cairo_cache_callback_func_t cache_callback,
void *closure)
{
_cairo_hash_table_foreach (cache->hash_table,
cache_callback,
closure);
}
unsigned long

View file

@ -60,7 +60,6 @@ void
cairo_debug_reset_static_data (void)
{
#if CAIRO_HAS_XLIB_SURFACE
_cairo_xlib_surface_reset_static_data ();
_cairo_xlib_screen_reset_static_data ();
#endif

View file

@ -413,561 +413,6 @@ static const cairo_font_face_backend_t _cairo_toy_font_face_backend = {
_cairo_toy_font_face_scaled_font_create
};
/* cairo_scaled_font_t */
static const cairo_scaled_font_t _cairo_scaled_font_nil = {
{ 0 }, /* hash_entry */
CAIRO_STATUS_NO_MEMORY, /* status */
-1, /* ref_count */
NULL, /* font_face */
{ 1., 0., 0., 1., 0, 0}, /* font_matrix */
{ 1., 0., 0., 1., 0, 0}, /* ctm */
{ 1., 0., 0., 1., 0, 0}, /* scale */
{ CAIRO_ANTIALIAS_DEFAULT, /* options */
CAIRO_SUBPIXEL_ORDER_DEFAULT,
CAIRO_HINT_STYLE_DEFAULT,
CAIRO_HINT_METRICS_DEFAULT} ,
CAIRO_SCALED_FONT_BACKEND_DEFAULT,
};
/**
* _cairo_scaled_font_set_error:
* @scaled_font: a scaled_font
* @status: a status value indicating an error, (eg. not
* CAIRO_STATUS_SUCCESS)
*
* Sets scaled_font->status to @status and calls _cairo_error;
*
* All assignments of an error status to scaled_font->status should happen
* through _cairo_scaled_font_set_error() or else _cairo_error() should be
* called immediately after the assignment.
*
* The purpose of this function is to allow the user to set a
* breakpoint in _cairo_error() to generate a stack trace for when the
* user causes cairo to detect an error.
**/
void
_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font,
cairo_status_t status)
{
/* Don't overwrite an existing error. This preserves the first
* error, which is the most significant. It also avoids attempting
* to write to read-only data (eg. from a nil scaled_font). */
if (scaled_font->status == CAIRO_STATUS_SUCCESS)
scaled_font->status = status;
_cairo_error (status);
}
/**
* cairo_scaled_font_status:
* @scaled_font: a #cairo_scaled_font_t
*
* Checks whether an error has previously occurred for this
* scaled_font.
*
* Return value: %CAIRO_STATUS_SUCCESS or another error such as
* %CAIRO_STATUS_NO_MEMORY.
**/
cairo_status_t
cairo_scaled_font_status (cairo_scaled_font_t *scaled_font)
{
return scaled_font->status;
}
/* Here we keep a unique mapping from
* cairo_font_face_t/matrix/ctm/options => cairo_scaled_font_t.
*
* Here are the things that we want to map:
*
* a) All otherwise referenced cairo_scaled_font_t's
* b) Some number of not otherwise referenced cairo_scaled_font_t's
*
* The implementation uses a hash table which covers (a)
* completely. Then, for (b) we have an array of otherwise
* unreferenced fonts (holdovers) which are expired in
* least-recently-used order.
*
* The cairo_scaled_font_create code gets to treat this like a regular
* hash table. All of the magic for the little holdover cache is in
* cairo_scaled_font_reference and cairo_scaled_font_destroy.
*/
/* This defines the size of the holdover array ... that is, the number
* of scaled fonts we keep around even when not otherwise referenced
*/
#define CAIRO_SCALED_FONT_MAX_HOLDOVERS 24
typedef struct _cairo_scaled_font_map {
cairo_hash_table_t *hash_table;
cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS];
int num_holdovers;
} cairo_scaled_font_map_t;
static cairo_scaled_font_map_t *cairo_scaled_font_map = NULL;
CAIRO_MUTEX_DECLARE (cairo_scaled_font_map_mutex);
static int
_cairo_scaled_font_keys_equal (void *abstract_key_a, void *abstract_key_b);
static cairo_scaled_font_map_t *
_cairo_scaled_font_map_lock (void)
{
CAIRO_MUTEX_LOCK (cairo_scaled_font_map_mutex);
if (cairo_scaled_font_map == NULL)
{
cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t));
if (cairo_scaled_font_map == NULL)
goto CLEANUP_MUTEX_LOCK;
cairo_scaled_font_map->hash_table =
_cairo_hash_table_create (_cairo_scaled_font_keys_equal);
if (cairo_scaled_font_map->hash_table == NULL)
goto CLEANUP_SCALED_FONT_MAP;
cairo_scaled_font_map->num_holdovers = 0;
}
return cairo_scaled_font_map;
CLEANUP_SCALED_FONT_MAP:
free (cairo_scaled_font_map);
CLEANUP_MUTEX_LOCK:
CAIRO_MUTEX_UNLOCK (cairo_scaled_font_map_mutex);
return NULL;
}
static void
_cairo_scaled_font_map_unlock (void)
{
CAIRO_MUTEX_UNLOCK (cairo_scaled_font_map_mutex);
}
static void
_cairo_scaled_font_map_destroy (void)
{
int i;
cairo_scaled_font_map_t *font_map = cairo_scaled_font_map;
cairo_scaled_font_t *scaled_font;
if (font_map == NULL)
return;
CAIRO_MUTEX_UNLOCK (cairo_scaled_font_map_mutex);
for (i = 0; i < font_map->num_holdovers; i++) {
scaled_font = font_map->holdovers[i];
/* We should only get here through the reset_static_data path
* and there had better not be any active references at that
* point. */
assert (scaled_font->ref_count == 0);
_cairo_hash_table_remove (font_map->hash_table,
&scaled_font->hash_entry);
_cairo_scaled_font_fini (scaled_font);
free (scaled_font);
}
_cairo_hash_table_destroy (font_map->hash_table);
free (cairo_scaled_font_map);
cairo_scaled_font_map = NULL;
}
/* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/)
*
* Not necessarily better than a lot of other hashes, but should be OK, and
* well tested with binary data.
*/
#define FNV_32_PRIME ((uint32_t)0x01000193)
#define FNV1_32_INIT ((uint32_t)0x811c9dc5)
static uint32_t
_hash_bytes_fnv (unsigned char *buffer,
int len,
uint32_t hval)
{
while (len--) {
hval *= FNV_32_PRIME;
hval ^= *buffer++;
}
return hval;
}
static void
_cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font,
cairo_font_face_t *font_face,
const cairo_matrix_t *font_matrix,
const cairo_matrix_t *ctm,
const cairo_font_options_t *options)
{
uint32_t hash = FNV1_32_INIT;
scaled_font->status = CAIRO_STATUS_SUCCESS;
scaled_font->font_face = font_face;
scaled_font->font_matrix = *font_matrix;
scaled_font->ctm = *ctm;
scaled_font->options = *options;
/* We do a bytewise hash on the font matrices, ignoring the
* translation values. */
hash = _hash_bytes_fnv ((unsigned char *)(&scaled_font->font_matrix.xx),
sizeof(double) * 4,
hash);
hash = _hash_bytes_fnv ((unsigned char *)(&scaled_font->ctm.xx),
sizeof(double) * 4,
hash);
hash ^= (unsigned long) scaled_font->font_face;
hash ^= cairo_font_options_hash (&scaled_font->options);
scaled_font->hash_entry.hash = hash;
}
static cairo_bool_t
_cairo_scaled_font_keys_equal (void *abstract_key_a, void *abstract_key_b)
{
cairo_scaled_font_t *key_a = abstract_key_a;
cairo_scaled_font_t *key_b = abstract_key_b;
return (key_a->font_face == key_b->font_face &&
memcmp ((unsigned char *)(&key_a->font_matrix.xx),
(unsigned char *)(&key_b->font_matrix.xx),
sizeof(double) * 4) == 0 &&
memcmp ((unsigned char *)(&key_a->ctm.xx),
(unsigned char *)(&key_b->ctm.xx),
sizeof(double) * 4) == 0 &&
cairo_font_options_equal (&key_a->options, &key_b->options));
}
void
_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font,
cairo_font_face_t *font_face,
const cairo_matrix_t *font_matrix,
const cairo_matrix_t *ctm,
const cairo_font_options_t *options,
const cairo_scaled_font_backend_t *backend)
{
scaled_font->ref_count = 1;
_cairo_scaled_font_init_key (scaled_font, font_face,
font_matrix, ctm, options);
cairo_font_face_reference (font_face);
cairo_matrix_multiply (&scaled_font->scale,
&scaled_font->font_matrix,
&scaled_font->ctm);
scaled_font->backend = backend;
}
void
_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font)
{
if (scaled_font->font_face)
cairo_font_face_destroy (scaled_font->font_face);
scaled_font->backend->fini (scaled_font);
}
/**
* cairo_scaled_font_create:
* @font_face: a #cairo_font_face_t
* @font_matrix: font space to user space transformation matrix for the
* font. In the simplest case of a N point font, this matrix is
* just a scale by N, but it can also be used to shear the font
* or stretch it unequally along the two axes. See
* cairo_set_font_matrix().
* @ctm: user to device transformation matrix with which the font will
* be used.
* @options: options to use when getting metrics for the font and
* rendering with it.
*
* Creates a #cairo_scaled_font_t object from a font face and matrices that
* describe the size of the font and the environment in which it will
* be used.
*
* Return value: a newly created #cairo_scaled_font_t. Destroy with
* cairo_scaled_font_destroy()
**/
cairo_scaled_font_t *
cairo_scaled_font_create (cairo_font_face_t *font_face,
const cairo_matrix_t *font_matrix,
const cairo_matrix_t *ctm,
const cairo_font_options_t *options)
{
cairo_status_t status;
cairo_scaled_font_map_t *font_map;
cairo_scaled_font_t key, *scaled_font = NULL;
font_map = _cairo_scaled_font_map_lock ();
if (font_map == NULL)
goto UNWIND;
_cairo_scaled_font_init_key (&key, font_face,
font_matrix, ctm, options);
/* Return existing scaled_font if it exists in the hash table. */
if (_cairo_hash_table_lookup (font_map->hash_table, &key.hash_entry,
(cairo_hash_entry_t**) &scaled_font))
{
_cairo_scaled_font_map_unlock ();
return cairo_scaled_font_reference (scaled_font);
}
/* Otherwise create it and insert it into the hash table. */
status = font_face->backend->scaled_font_create (font_face, font_matrix,
ctm, options, &scaled_font);
if (status)
goto UNWIND_FONT_MAP_LOCK;
status = _cairo_hash_table_insert (font_map->hash_table,
&scaled_font->hash_entry);
if (status)
goto UNWIND_SCALED_FONT_CREATE;
_cairo_scaled_font_map_unlock ();
return scaled_font;
UNWIND_SCALED_FONT_CREATE:
/* We can't call _cairo_scaled_font_destroy here since it expects
* that the font has already been successfully inserted into the
* hash table. */
_cairo_scaled_font_fini (scaled_font);
free (scaled_font);
UNWIND_FONT_MAP_LOCK:
_cairo_scaled_font_map_unlock ();
UNWIND:
return NULL;
}
/**
* cairo_scaled_font_reference:
* @scaled_font: a #cairo_scaled_font_t, (may be NULL in which case
* this function does nothing)
*
* Increases the reference count on @scaled_font by one. This prevents
* @scaled_font from being destroyed until a matching call to
* cairo_scaled_font_destroy() is made.
*
* Returns: the referenced #cairo_scaled_font_t
**/
cairo_scaled_font_t *
cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font)
{
if (scaled_font == NULL)
return NULL;
if (scaled_font->ref_count == (unsigned int)-1)
return scaled_font;
/* We would normally assert (scaled_font->ref_count > 0) here, but
* we are using ref_count == 0 as a legitimate case for the
* holdovers array. See below. */
/* If the original reference count is 0, then this font must have
* been found in font_map->holdovers, (which means this caching is
* actually working). So now we remove it from the holdovers
* array. */
if (scaled_font->ref_count == 0) {
cairo_scaled_font_map_t *font_map;
int i;
font_map = _cairo_scaled_font_map_lock ();
{
for (i = 0; i < font_map->num_holdovers; i++)
if (font_map->holdovers[i] == scaled_font)
break;
assert (i < font_map->num_holdovers);
font_map->num_holdovers--;
memmove (&font_map->holdovers[i],
&font_map->holdovers[i+1],
(font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*));
}
_cairo_scaled_font_map_unlock ();
}
scaled_font->ref_count++;
return scaled_font;
}
/**
* cairo_scaled_font_destroy:
* @scaled_font: a #cairo_scaled_font_t
*
* Decreases the reference count on @font by one. If the result
* is zero, then @font and all associated resources are freed.
* See cairo_scaled_font_reference().
**/
void
cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
{
cairo_scaled_font_map_t *font_map;
if (scaled_font == NULL)
return;
if (scaled_font->ref_count == (unsigned int)-1)
return;
assert (scaled_font->ref_count > 0);
if (--(scaled_font->ref_count) > 0)
return;
font_map = _cairo_scaled_font_map_lock ();
assert (font_map != NULL);
{
/* Rather than immediately destroying this object, we put it into
* the font_map->holdovers array in case it will get used again
* soon. To make room for it, we do actually destroy the
* least-recently-used holdover.
*/
if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) {
cairo_scaled_font_t *lru;
lru = font_map->holdovers[0];
assert (lru->ref_count == 0);
_cairo_hash_table_remove (font_map->hash_table, &lru->hash_entry);
_cairo_scaled_font_fini (lru);
free (lru);
font_map->num_holdovers--;
memmove (&font_map->holdovers[0],
&font_map->holdovers[1],
font_map->num_holdovers * sizeof (cairo_scaled_font_t*));
}
font_map->holdovers[font_map->num_holdovers] = scaled_font;
font_map->num_holdovers++;
}
_cairo_scaled_font_map_unlock ();
}
cairo_status_t
_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
const char *utf8,
cairo_glyph_t **glyphs,
int *num_glyphs)
{
if (scaled_font->status)
return scaled_font->status;
return scaled_font->backend->text_to_glyphs (scaled_font, utf8, glyphs, num_glyphs);
}
cairo_status_t
_cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_text_extents_t *extents)
{
if (scaled_font->status)
return scaled_font->status;
return scaled_font->backend->glyph_extents (scaled_font, glyphs, num_glyphs, extents);
}
cairo_status_t
_cairo_scaled_font_glyph_bbox (cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_box_t *bbox)
{
if (scaled_font->status)
return scaled_font->status;
return scaled_font->backend->glyph_bbox (scaled_font, glyphs, num_glyphs, bbox);
}
cairo_status_t
_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font,
cairo_operator_t operator,
cairo_pattern_t *pattern,
cairo_surface_t *surface,
int source_x,
int source_y,
int dest_x,
int dest_y,
unsigned int width,
unsigned int height,
cairo_glyph_t *glyphs,
int num_glyphs)
{
cairo_status_t status;
/* These operators aren't interpreted the same way by the backends;
* they are implemented in terms of other operators in cairo-gstate.c
*/
assert (operator != CAIRO_OPERATOR_SOURCE && operator != CAIRO_OPERATOR_CLEAR);
if (scaled_font->status)
return scaled_font->status;
status = _cairo_surface_show_glyphs (scaled_font, operator, pattern,
surface,
source_x, source_y,
dest_x, dest_y,
width, height,
glyphs, num_glyphs);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
/* Surface display routine either does not exist or failed. */
return scaled_font->backend->show_glyphs (scaled_font, operator, pattern,
surface,
source_x, source_y,
dest_x, dest_y,
width, height,
glyphs, num_glyphs);
}
cairo_status_t
_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_path_fixed_t *path)
{
if (scaled_font->status)
return scaled_font->status;
return scaled_font->backend->glyph_path (scaled_font, glyphs, num_glyphs, path);
}
cairo_status_t
_cairo_scaled_font_get_glyph_cache_key (cairo_scaled_font_t *scaled_font,
cairo_glyph_cache_key_t *key)
{
if (scaled_font->status)
return scaled_font->status;
scaled_font->backend->get_glyph_cache_key (scaled_font, key);
return CAIRO_STATUS_SUCCESS;
}
cairo_status_t
_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font,
cairo_font_extents_t *extents)
{
if (scaled_font->status)
return scaled_font->status;
return scaled_font->backend->font_extents (scaled_font, extents);
}
void
_cairo_unscaled_font_init (cairo_unscaled_font_t *unscaled_font,
const cairo_unscaled_font_backend_t *backend)
@ -1001,303 +446,11 @@ _cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font)
free (unscaled_font);
}
/* Public font API follows. */
/**
* cairo_scaled_font_extents:
* @scaled_font: a #cairo_scaled_font_t
* @extents: a #cairo_font_extents_t which to store the retrieved extents.
*
* Gets the metrics for a #cairo_scaled_font_t.
**/
void
cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font,
cairo_font_extents_t *extents)
{
cairo_int_status_t status;
double font_scale_x, font_scale_y;
if (scaled_font->status)
return;
status = _cairo_scaled_font_font_extents (scaled_font, extents);
if (status) {
_cairo_scaled_font_set_error (scaled_font, status);
return;
}
_cairo_matrix_compute_scale_factors (&scaled_font->font_matrix,
&font_scale_x, &font_scale_y,
/* XXX */ 1);
/*
* The font responded in unscaled units, scale by the font
* matrix scale factors to get to user space
*/
extents->ascent *= font_scale_y;
extents->descent *= font_scale_y;
extents->height *= font_scale_y;
extents->max_x_advance *= font_scale_x;
extents->max_y_advance *= font_scale_y;
}
/**
* cairo_font_glyph_extents:
* @scaled_font: a #cairo_scaled_font_t
* @glyphs: an array of glyph IDs with X and Y offsets.
* @num_glyphs: the number of glyphs in the @glyphs array
* @extents: a #cairo_text_extents_t which to store the retrieved extents.
*
* cairo_font_glyph_extents() gets the overall metrics for a string of
* glyphs. The X and Y offsets in @glyphs are taken from an origin of 0,0.
**/
void
cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_text_extents_t *extents)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_glyph_t origin_glyph;
cairo_text_extents_t origin_extents;
int i;
double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0;
double x_pos = 0.0, y_pos = 0.0;
int set = 0;
if (scaled_font->status)
return;
if (!num_glyphs)
{
extents->x_bearing = 0.0;
extents->y_bearing = 0.0;
extents->width = 0.0;
extents->height = 0.0;
extents->x_advance = 0.0;
extents->y_advance = 0.0;
return;
}
for (i = 0; i < num_glyphs; i++)
{
double x, y;
double wm, hm;
origin_glyph = glyphs[i];
origin_glyph.x = 0.0;
origin_glyph.y = 0.0;
status = _cairo_scaled_font_glyph_extents (scaled_font,
&origin_glyph, 1,
&origin_extents);
/*
* Transform font space metrics into user space metrics
* by running the corners through the font matrix and
* expanding the bounding box as necessary
*/
x = origin_extents.x_bearing;
y = origin_extents.y_bearing;
cairo_matrix_transform_point (&scaled_font->font_matrix,
&x, &y);
for (hm = 0.0; hm <= 1.0; hm += 1.0)
for (wm = 0.0; wm <= 1.0; wm += 1.0)
{
x = origin_extents.x_bearing + origin_extents.width * wm;
y = origin_extents.y_bearing + origin_extents.height * hm;
cairo_matrix_transform_point (&scaled_font->font_matrix,
&x, &y);
x += glyphs[i].x;
y += glyphs[i].y;
if (!set)
{
min_x = max_x = x;
min_y = max_y = y;
set = 1;
}
else
{
if (x < min_x) min_x = x;
if (x > max_x) max_x = x;
if (y < min_y) min_y = y;
if (y > max_y) max_y = y;
}
}
x = origin_extents.x_advance;
y = origin_extents.y_advance;
cairo_matrix_transform_point (&scaled_font->font_matrix,
&x, &y);
x_pos = glyphs[i].x + x;
y_pos = glyphs[i].y + y;
}
extents->x_bearing = min_x - glyphs[0].x;
extents->y_bearing = min_y - glyphs[0].y;
extents->width = max_x - min_x;
extents->height = max_y - min_y;
extents->x_advance = x_pos - glyphs[0].x;
extents->y_advance = y_pos - glyphs[0].y;
}
/* Now we implement functions to access a default global image & metrics
* cache.
*/
unsigned long
_cairo_glyph_cache_hash (void *cache, void *key)
{
cairo_glyph_cache_key_t *in;
in = (cairo_glyph_cache_key_t *) key;
return
((unsigned long) in->unscaled)
^ ((unsigned long) in->scale.xx)
^ ((unsigned long) in->scale.yx)
^ ((unsigned long) in->scale.xy)
^ ((unsigned long) in->scale.yy)
^ (in->flags * 1451) /* 1451 is just an abitrary prime */
^ in->index;
}
int
_cairo_glyph_cache_keys_equal (void *cache,
void *k1,
void *k2)
{
cairo_glyph_cache_key_t *a, *b;
a = (cairo_glyph_cache_key_t *) k1;
b = (cairo_glyph_cache_key_t *) k2;
return (a->index == b->index)
&& (a->unscaled == b->unscaled)
&& (a->flags == b->flags)
&& (a->scale.xx == b->scale.xx)
&& (a->scale.yx == b->scale.yx)
&& (a->scale.xy == b->scale.xy)
&& (a->scale.yy == b->scale.yy);
}
static cairo_status_t
_image_glyph_cache_create_entry (void *cache,
void *key,
void **return_value)
{
cairo_glyph_cache_key_t *k = (cairo_glyph_cache_key_t *) key;
cairo_image_glyph_cache_entry_t *im;
cairo_status_t status;
im = calloc (1, sizeof (cairo_image_glyph_cache_entry_t));
if (im == NULL)
return CAIRO_STATUS_NO_MEMORY;
im->key = *k;
status = im->key.unscaled->backend->create_glyph (im->key.unscaled,
im);
if (status != CAIRO_STATUS_SUCCESS) {
free (im);
return status;
}
_cairo_unscaled_font_reference (im->key.unscaled);
im->key.base.memory =
sizeof (cairo_image_glyph_cache_entry_t)
+ (im->image ?
sizeof (cairo_image_surface_t)
+ 28 * sizeof (int) /* rough guess at size of pixman image structure */
+ (im->image->height * im->image->stride) : 0);
*return_value = im;
return CAIRO_STATUS_SUCCESS;
}
static void
_image_glyph_cache_destroy_entry (void *cache,
void *value)
{
cairo_image_glyph_cache_entry_t *im;
im = (cairo_image_glyph_cache_entry_t *) value;
_cairo_unscaled_font_destroy (im->key.unscaled);
cairo_surface_destroy (&(im->image->base));
free (im);
}
static void
_image_glyph_cache_destroy_cache (void *cache)
{
free (cache);
}
static const cairo_cache_backend_t cairo_image_cache_backend = {
_cairo_glyph_cache_hash,
_cairo_glyph_cache_keys_equal,
_image_glyph_cache_create_entry,
_image_glyph_cache_destroy_entry,
_image_glyph_cache_destroy_cache
};
CAIRO_MUTEX_DECLARE(_global_image_glyph_cache_mutex);
static cairo_cache_t *
_global_image_glyph_cache = NULL;
void
_cairo_lock_global_image_glyph_cache (void)
{
CAIRO_MUTEX_LOCK (_global_image_glyph_cache_mutex);
}
void
_cairo_unlock_global_image_glyph_cache (void)
{
if (_global_image_glyph_cache) {
_cairo_cache_shrink_to (_global_image_glyph_cache,
CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT);
}
CAIRO_MUTEX_UNLOCK (_global_image_glyph_cache_mutex);
}
cairo_cache_t *
_cairo_get_global_image_glyph_cache (void)
{
if (_global_image_glyph_cache == NULL) {
_global_image_glyph_cache = malloc (sizeof (cairo_cache_t));
if (_global_image_glyph_cache == NULL)
goto FAIL;
if (_cairo_cache_init (_global_image_glyph_cache,
&cairo_image_cache_backend,
0))
goto FAIL;
}
return _global_image_glyph_cache;
FAIL:
if (_global_image_glyph_cache)
free (_global_image_glyph_cache);
_global_image_glyph_cache = NULL;
return NULL;
}
void
_cairo_font_reset_static_data (void)
{
_cairo_scaled_font_map_destroy ();
_cairo_lock_global_image_glyph_cache();
_cairo_cache_destroy (_global_image_glyph_cache);
_global_image_glyph_cache = NULL;
_cairo_unlock_global_image_glyph_cache();
CAIRO_MUTEX_LOCK (cairo_toy_font_face_hash_table_mutex);
_cairo_hash_table_destroy (cairo_toy_font_face_hash_table);
cairo_toy_font_face_hash_table = NULL;

File diff suppressed because it is too large Load diff

View file

@ -1950,30 +1950,17 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
int *num_glyphs)
{
cairo_status_t status;
int i;
status = _cairo_gstate_ensure_scaled_font (gstate);
if (status)
return status;
status = _cairo_scaled_font_text_to_glyphs (gstate->scaled_font,
status = _cairo_scaled_font_text_to_glyphs (gstate->scaled_font, x, y,
utf8, glyphs, num_glyphs);
if (status || !glyphs || !num_glyphs || !(*glyphs) || !(num_glyphs))
return status;
/* The font responded in glyph space, starting from (0,0). Convert to
user space by applying the font transform, then add any current point
offset. */
for (i = 0; i < *num_glyphs; ++i) {
cairo_matrix_transform_point (&gstate->font_matrix,
&((*glyphs)[i].x),
&((*glyphs)[i].y));
(*glyphs)[i].x += x;
(*glyphs)[i].y += y;
}
return CAIRO_STATUS_SUCCESS;
}
@ -2050,6 +2037,17 @@ _cairo_gstate_show_glyphs_draw_func (void *closure,
if (!src)
src = &pattern.base;
status = _cairo_surface_show_glyphs (glyph_info->font, operator, src,
dst,
extents->x, extents->y,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height,
glyph_info->glyphs,
glyph_info->num_glyphs);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
status = _cairo_scaled_font_show_glyphs (glyph_info->font,
operator,
src, dst,
@ -2074,7 +2072,6 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate,
int i;
cairo_glyph_t *transformed_glyphs = NULL;
cairo_pattern_union_t pattern;
cairo_box_t bbox;
cairo_rectangle_t extents;
cairo_show_glyphs_info_t glyph_info;
@ -2102,21 +2099,15 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate,
}
if (_cairo_operator_bounded (gstate->operator))
{
status = _cairo_scaled_font_glyph_bbox (gstate->scaled_font,
transformed_glyphs, num_glyphs,
&bbox);
if (status)
goto CLEANUP_GLYPHS;
_cairo_box_round_to_rectangle (&bbox, &extents);
}
status = _cairo_scaled_font_glyph_device_extents (gstate->scaled_font,
transformed_glyphs,
num_glyphs,
&extents);
else
{
status = _cairo_surface_get_extents (gstate->target, &extents);
if (status)
goto CLEANUP_GLYPHS;
}
if (status)
goto CLEANUP_GLYPHS;
status = _cairo_clip_intersect_to_rectangle (&gstate->clip, &extents);
if (status)

View file

@ -132,6 +132,17 @@ _cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
return CAIRO_STATUS_SUCCESS;
}
cairo_path_fixed_t *
_cairo_path_fixed_create (void)
{
cairo_path_fixed_t *path = malloc (sizeof (cairo_path_fixed_t));
if (!path)
return NULL;
_cairo_path_fixed_init (path);
return path;
}
void
_cairo_path_fixed_fini (cairo_path_fixed_t *path)
{
@ -155,6 +166,13 @@ _cairo_path_fixed_fini (cairo_path_fixed_t *path)
path->has_current_point = 0;
}
void
_cairo_path_fixed_destroy (cairo_path_fixed_t *path)
{
_cairo_path_fixed_fini (path);
free (path);
}
cairo_status_t
_cairo_path_fixed_move_to (cairo_path_fixed_t *path,
cairo_fixed_t x,

1165
src/cairo-scaled-font.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -184,6 +184,7 @@ typedef cairo_fixed_16_16_t cairo_fixed_t;
#define CAIRO_ALPHA_IS_ZERO(alpha) ((alpha) <= 0.0)
#include "cairo-hash-private.h"
#include "cairo-cache-private.h"
typedef struct _cairo_point {
cairo_fixed_t x;
@ -227,7 +228,8 @@ typedef struct _cairo_rectangle {
typedef enum cairo_int_status {
CAIRO_INT_STATUS_DEGENERATE = 1000,
CAIRO_INT_STATUS_UNSUPPORTED,
CAIRO_INT_STATUS_NOTHING_TO_DO
CAIRO_INT_STATUS_NOTHING_TO_DO,
CAIRO_INT_STATUS_CACHE_EMPTY
} cairo_int_status_t;
typedef enum cairo_direction {
@ -288,6 +290,8 @@ typedef struct _cairo_pen {
typedef struct _cairo_color cairo_color_t;
typedef struct _cairo_image_surface cairo_image_surface_t;
typedef struct _cairo_surface_backend cairo_surface_backend_t;
cairo_private void
_cairo_box_round_to_rectangle (cairo_box_t *box, cairo_rectangle_t *rectangle);
@ -348,108 +352,9 @@ _cairo_user_data_array_set_data (cairo_user_data_array_t *array,
void *user_data,
cairo_destroy_func_t destroy);
/* cairo_cache.c structures and functions */
typedef struct _cairo_cache_backend {
unsigned long (*hash) (void *cache,
void *key);
int (*keys_equal) (void *cache,
void *k1,
void *k2);
cairo_status_t (*create_entry) (void *cache,
void *key,
void **entry_return);
void (*destroy_entry) (void *cache,
void *entry);
void (*destroy_cache) (void *cache);
} cairo_cache_backend_t;
/*
* The cairo_cache system makes the following assumptions about
* entries in its cache:
*
* - a pointer to an entry can be cast to a cairo_cache_entry_base_t.
* - a pointer to an entry can also be cast to the "key type".
*
* The practical effect of this is that your entries must be laid
* out this way:
*
* struct my_entry {
* cairo_cache_entry_base_t;
* my_key_value_1;
* my_key_value_2;
* ...
* my_value;
* };
*/
typedef struct {
unsigned long memory;
unsigned long hashcode;
} cairo_cache_entry_base_t;
typedef struct {
unsigned long high_water_mark;
unsigned long size;
unsigned long rehash;
} cairo_cache_arrangement_t;
#undef CAIRO_MEASURE_CACHE_PERFORMANCE
typedef struct {
const cairo_cache_backend_t *backend;
const cairo_cache_arrangement_t *arrangement;
cairo_cache_entry_base_t **entries;
unsigned long max_memory;
unsigned long used_memory;
unsigned long live_entries;
#ifdef CAIRO_MEASURE_CACHE_PERFORMANCE
unsigned long hits;
unsigned long misses;
unsigned long probes;
#endif
} cairo_cache_t;
cairo_private cairo_status_t
_cairo_cache_init (cairo_cache_t *cache,
const cairo_cache_backend_t *backend,
unsigned long max_memory);
cairo_private void
_cairo_cache_destroy (cairo_cache_t *cache);
cairo_private void
_cairo_cache_shrink_to (cairo_cache_t *cache,
unsigned long max_memory);
cairo_private cairo_status_t
_cairo_cache_lookup (cairo_cache_t *cache,
void *key,
void **entry_return,
int *created_entry);
cairo_private cairo_status_t
_cairo_cache_remove (cairo_cache_t *cache,
void *key);
cairo_private void *
_cairo_cache_random_entry (cairo_cache_t *cache,
int (*predicate) (void*));
cairo_private unsigned long
_cairo_hash_string (const char *c);
#define CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT 0x100000
#define CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT 0x100000
typedef struct _cairo_unscaled_font_backend cairo_unscaled_font_backend_t;
typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t;
typedef struct _cairo_font_face_backend cairo_font_face_backend_t;
@ -471,16 +376,47 @@ struct _cairo_font_options {
cairo_hint_metrics_t hint_metrics;
};
typedef struct _cairo_scaled_glyph {
cairo_cache_entry_t cache_entry; /* hash is glyph index */
cairo_scaled_font_t *scaled_font; /* font the glyph lives in */
cairo_text_extents_t metrics; /* user-space metrics */
cairo_box_t bbox; /* device-space bounds */
cairo_image_surface_t *surface; /* device-space image */
cairo_path_fixed_t *path; /* device-space outline */
void *surface_private; /* for the surface backend */
} cairo_scaled_glyph_t;
#define _cairo_scaled_glyph_index(g) ((g)->cache_entry.hash)
#define _cairo_scaled_glyph_set_index(g,i) ((g)->cache_entry.hash = (i))
struct _cairo_scaled_font {
/* must be first to be stored in a hash table */
cairo_hash_entry_t hash_entry;
/* useful bits for _cairo_scaled_font_nil */
cairo_status_t status;
int ref_count;
/* hash key members */
cairo_font_face_t *font_face; /* may be NULL */
cairo_matrix_t font_matrix; /* font space => user space */
cairo_matrix_t ctm; /* user space => device space */
cairo_matrix_t scale; /* font space => device space */
cairo_font_options_t options;
/* "live" scaled_font members */
cairo_matrix_t scale; /* font space => device space */
cairo_font_extents_t extents; /* user space */
cairo_cache_t *glyphs; /* glyph index -> cairo_scaled_glyph_t */
/*
* One surface backend may store data in each glyph.
* Whichever surface manages to store its pointer here
* first gets to store data in each glyph
*/
const cairo_surface_backend_t *surface_backend;
void *surface_private;
/* font backend managing this scaled font */
const cairo_scaled_font_backend_t *backend;
};
@ -492,40 +428,6 @@ struct _cairo_font_face {
const cairo_font_face_backend_t *backend;
};
/* cairo_font.c is responsible for a global glyph cache:
*
* - glyph entries: [[[base], cairo_unscaled_font_t, scale, flags, index],
* image, size, extents]
*
* Surfaces may build their own glyph caches if they have surface-specific
* glyph resources to maintain; those caches can feed off of the global
* caches if need be (eg. cairo_xlib_surface.c does this).
*/
typedef struct {
cairo_cache_entry_base_t base;
cairo_unscaled_font_t *unscaled;
cairo_matrix_t scale; /* translation is ignored */
int flags;
unsigned long index;
} cairo_glyph_cache_key_t;
typedef struct {
cairo_glyph_cache_key_t key;
cairo_image_surface_t *image;
cairo_glyph_size_t size;
cairo_text_extents_t extents;
} cairo_image_glyph_cache_entry_t;
cairo_private void
_cairo_lock_global_image_glyph_cache (void);
cairo_private void
_cairo_unlock_global_image_glyph_cache (void);
cairo_private cairo_cache_t *
_cairo_get_global_image_glyph_cache (void);
cairo_private void
_cairo_font_reset_static_data (void);
@ -538,22 +440,10 @@ _cairo_xlib_surface_reset_static_data (void);
cairo_private void
_cairo_xlib_screen_reset_static_data (void);
/* Some glyph cache functions you can reuse. */
cairo_private unsigned long
_cairo_glyph_cache_hash (void *cache, void *key);
cairo_private int
_cairo_glyph_cache_keys_equal (void *cache,
void *k1,
void *k2);
/* the font backend interface */
struct _cairo_unscaled_font_backend {
void (*destroy) (void *unscaled_font);
cairo_status_t (*create_glyph) (void *unscaled_font,
cairo_image_glyph_cache_entry_t *entry);
};
/* cairo_toy_font_face_t - simple family/slant/weight font faces used for
@ -568,6 +458,12 @@ typedef struct _cairo_toy_font_face {
cairo_font_weight_t weight;
} cairo_toy_font_face_t;
typedef enum _cairo_scaled_glyph_info {
CAIRO_SCALED_GLYPH_INFO_METRICS = (1 << 0),
CAIRO_SCALED_GLYPH_INFO_SURFACE = (1 << 1),
CAIRO_SCALED_GLYPH_INFO_PATH = (1 << 2)
} cairo_scaled_glyph_info_t;
struct _cairo_scaled_font_backend {
cairo_status_t
(*create_toy) (cairo_toy_font_face_t *toy_face,
@ -580,28 +476,14 @@ struct _cairo_scaled_font_backend {
(*fini) (void *scaled_font);
cairo_status_t
(*font_extents) (void *scaled_font,
cairo_font_extents_t *extents);
(*scaled_glyph_init) (void *scaled_font,
cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_glyph_info_t info);
cairo_status_t
(*text_to_glyphs) (void *scaled_font,
const char *utf8,
cairo_glyph_t **glyphs,
int *num_glyphs);
cairo_status_t
(*glyph_extents) (void *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_text_extents_t *extents);
cairo_status_t
(*glyph_bbox) (void *scaled_font,
const cairo_glyph_t *glyphs,
int num_glyphs,
cairo_box_t *bbox);
cairo_status_t
unsigned long
(*ucs4_to_index) (void *scaled_font,
uint32_t ucs4);
cairo_int_status_t
(*show_glyphs) (void *scaled_font,
cairo_operator_t operator,
cairo_pattern_t *pattern,
@ -615,15 +497,6 @@ struct _cairo_scaled_font_backend {
const cairo_glyph_t *glyphs,
int num_glyphs);
cairo_status_t
(*glyph_path) (void *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_path_fixed_t *path);
void
(*get_glyph_cache_key) (void *scaled_font,
cairo_glyph_cache_key_t *key);
};
struct _cairo_font_face_backend {
@ -660,7 +533,7 @@ extern const cairo_private struct _cairo_scaled_font_backend cairo_atsui_scaled_
#endif
typedef struct _cairo_surface_backend {
struct _cairo_surface_backend {
cairo_surface_t *
(*create_similar) (void *surface,
cairo_content_t content,
@ -796,8 +669,8 @@ typedef struct _cairo_surface_backend {
/*
* This is an optional entry to let the surface manage its own glyph
* resources. If null, the font will be asked to render against this
* surface, using image surfaces as glyphs.
* resources. If null, render against this surface, using image
* surfaces as glyphs.
*/
cairo_int_status_t
(*show_glyphs) (cairo_scaled_font_t *font,
@ -835,7 +708,13 @@ typedef struct _cairo_surface_backend {
int width,
int height);
} cairo_surface_backend_t;
void
(*scaled_font_fini) (cairo_scaled_font_t *scaled_font);
void
(*scaled_glyph_fini) (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_font_t *scaled_font);
};
typedef struct _cairo_format_masks {
int bpp;
@ -1384,63 +1263,6 @@ _cairo_unscaled_font_reference (cairo_unscaled_font_t *font);
cairo_private void
_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font);
cairo_private void
_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font,
cairo_font_face_t *font_face,
const cairo_matrix_t *font_matrix,
const cairo_matrix_t *ctm,
const cairo_font_options_t *options,
const cairo_scaled_font_backend_t *backend);
void
_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font);
cairo_private cairo_status_t
_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font,
cairo_font_extents_t *extents);
cairo_private cairo_status_t
_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
const char *utf8,
cairo_glyph_t **glyphs,
int *num_glyphs);
cairo_private cairo_status_t
_cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_text_extents_t *extents);
cairo_private cairo_status_t
_cairo_scaled_font_glyph_bbox (cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_box_t *bbox);
cairo_private cairo_status_t
_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font,
cairo_operator_t operator,
cairo_pattern_t *source,
cairo_surface_t *surface,
int source_x,
int source_y,
int dest_x,
int dest_y,
unsigned int width,
unsigned int height,
cairo_glyph_t *glyphs,
int num_glyphs);
cairo_private cairo_status_t
_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_path_fixed_t *path);
cairo_private cairo_status_t
_cairo_scaled_font_get_glyph_cache_key (cairo_scaled_font_t *scaled_font,
cairo_glyph_cache_key_t *key);
/* cairo-font-options.c */
cairo_private void
@ -1458,9 +1280,15 @@ cairo_private cairo_status_t
_cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
cairo_path_fixed_t *other);
cairo_path_fixed_t *
_cairo_path_fixed_create (void);
cairo_private void
_cairo_path_fixed_fini (cairo_path_fixed_t *path);
void
_cairo_path_fixed_destroy (cairo_path_fixed_t *path);
cairo_private cairo_status_t
_cairo_path_fixed_move_to (cairo_path_fixed_t *path,
cairo_fixed_t x,
@ -1545,6 +1373,91 @@ _cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path,
cairo_gstate_t *gstate,
cairo_traps_t *traps);
/* cairo-scaled-font.c */
cairo_private cairo_status_t
_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font,
cairo_font_face_t *font_face,
const cairo_matrix_t *font_matrix,
const cairo_matrix_t *ctm,
const cairo_font_options_t *options,
const cairo_scaled_font_backend_t *backend);
cairo_private void
_cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font,
cairo_font_extents_t *fs_metrics);
cairo_private void
_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font);
cairo_private cairo_status_t
_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font,
cairo_font_extents_t *extents);
cairo_private cairo_status_t
_cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font,
double x,
double y,
const char *utf8,
cairo_glyph_t **glyphs,
int *num_glyphs);
cairo_private cairo_status_t
_cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_text_extents_t *extents);
cairo_private cairo_status_t
_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font,
const cairo_glyph_t *glyphs,
int num_glyphs,
cairo_rectangle_t *extents);
cairo_private cairo_status_t
_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font,
cairo_operator_t operator,
cairo_pattern_t *source,
cairo_surface_t *surface,
int source_x,
int source_y,
int dest_x,
int dest_y,
unsigned int width,
unsigned int height,
cairo_glyph_t *glyphs,
int num_glyphs);
cairo_private cairo_status_t
_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_path_fixed_t *path);
cairo_private void
_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_font_t *scaled_font,
cairo_text_extents_t *fs_metrics);
cairo_private void
_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_font_t *scaled_font,
cairo_image_surface_t *surface);
cairo_private void
_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_font_t *scaled_font,
cairo_path_fixed_t *path);
cairo_status_t
_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
unsigned long index,
cairo_scaled_glyph_info_t info,
cairo_scaled_glyph_t **scaled_glyph_ret);
cairo_private void
_cairo_scaled_font_map_destroy (void);
/* cairo-surface.c */
extern const cairo_private cairo_surface_t _cairo_surface_nil;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 727 B

After

Width:  |  Height:  |  Size: 740 B

View file

@ -26,7 +26,7 @@
#include "cairo-test.h"
#define WIDTH 31
#define HEIGHT 20
#define HEIGHT 22
#define TEXT_SIZE 12
cairo_test_t test = {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 276 B

View file

@ -26,7 +26,7 @@
#include "cairo-test.h"
#define WIDTH 31
#define HEIGHT 20
#define HEIGHT 22
#define TEXT_SIZE 12
cairo_test_t test = {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -26,7 +26,7 @@
#include "cairo-test.h"
#define WIDTH 31
#define HEIGHT 20
#define HEIGHT 22
#define TEXT_SIZE 12
cairo_test_t test = {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB