mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-25 16:28:14 +02:00
1382 lines
37 KiB
C
1382 lines
37 KiB
C
/* cairo - a vector graphics library with display and print output
|
|
*
|
|
* Copyright © 2002 University of Southern California
|
|
* 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 University of Southern
|
|
* California.
|
|
*
|
|
* Contributor(s):
|
|
* Carl D. Worth <cworth@cworth.org>
|
|
* Graydon Hoare <graydon@redhat.com>
|
|
* Owen Taylor <otaylor@redhat.com>
|
|
*/
|
|
|
|
#include "cairoint.h"
|
|
|
|
/* Forward declare so we can use it as an arbitrary backend for
|
|
* _cairo_font_face_nil.
|
|
*/
|
|
static const cairo_font_face_backend_t _cairo_simple_font_face_backend;
|
|
|
|
/* cairo_font_face_t */
|
|
|
|
const cairo_font_face_t _cairo_font_face_nil = {
|
|
CAIRO_STATUS_NO_MEMORY, /* status */
|
|
-1, /* ref_count */
|
|
{ 0, 0, 0, NULL }, /* user_data */
|
|
&_cairo_simple_font_face_backend
|
|
};
|
|
|
|
void
|
|
_cairo_font_face_init (cairo_font_face_t *font_face,
|
|
const cairo_font_face_backend_t *backend)
|
|
{
|
|
font_face->status = CAIRO_STATUS_SUCCESS;
|
|
font_face->ref_count = 1;
|
|
font_face->backend = backend;
|
|
|
|
_cairo_user_data_array_init (&font_face->user_data);
|
|
}
|
|
|
|
/**
|
|
* cairo_font_face_reference:
|
|
* @font_face: a #cairo_font_face_t, (may be NULL in which case this
|
|
* function does nothing).
|
|
*
|
|
* Increases the reference count on @font_face by one. This prevents
|
|
* @font_face from being destroyed until a matching call to
|
|
* cairo_font_face_destroy() is made.
|
|
**/
|
|
void
|
|
cairo_font_face_reference (cairo_font_face_t *font_face)
|
|
{
|
|
if (font_face == NULL)
|
|
return;
|
|
|
|
if (font_face->ref_count == (unsigned int)-1)
|
|
return;
|
|
|
|
font_face->ref_count++;
|
|
}
|
|
|
|
/**
|
|
* cairo_font_face_destroy:
|
|
* @font_face: a #cairo_font_face_t
|
|
*
|
|
* Decreases the reference count on @font_face by one. If the result
|
|
* is zero, then @font_face and all associated resources are freed.
|
|
* See cairo_font_face_reference().
|
|
**/
|
|
void
|
|
cairo_font_face_destroy (cairo_font_face_t *font_face)
|
|
{
|
|
if (font_face == NULL)
|
|
return;
|
|
|
|
if (font_face->ref_count == (unsigned int)-1)
|
|
return;
|
|
|
|
if (--(font_face->ref_count) > 0)
|
|
return;
|
|
|
|
font_face->backend->destroy (font_face);
|
|
|
|
/* We allow resurrection to deal with some memory management for the
|
|
* FreeType backend where cairo_ft_font_face_t and cairo_ft_unscaled_font_t
|
|
* need to effectively mutually reference each other
|
|
*/
|
|
if (font_face->ref_count > 0)
|
|
return;
|
|
|
|
_cairo_user_data_array_fini (&font_face->user_data);
|
|
|
|
free (font_face);
|
|
}
|
|
|
|
/**
|
|
* cairo_font_face_status:
|
|
* @surface: a #cairo_font_face_t
|
|
*
|
|
* Checks whether an error has previously occurred for this
|
|
* font face
|
|
*
|
|
* Return value: %CAIRO_STATUS_SUCCESS or another error such as
|
|
* %CAIRO_STATUS_NO_MEMORY.
|
|
**/
|
|
cairo_status_t
|
|
cairo_font_face_status (cairo_font_face_t *font_face)
|
|
{
|
|
return font_face->status;
|
|
}
|
|
|
|
/**
|
|
* cairo_font_face_get_user_data:
|
|
* @font_face: a #cairo_font_face_t
|
|
* @key: the address of the #cairo_user_data_key_t the user data was
|
|
* attached to
|
|
*
|
|
* Return user data previously attached to @font_face using the specified
|
|
* key. If no user data has been attached with the given key this
|
|
* function returns %NULL.
|
|
*
|
|
* Return value: the user data previously attached or %NULL.
|
|
**/
|
|
void *
|
|
cairo_font_face_get_user_data (cairo_font_face_t *font_face,
|
|
const cairo_user_data_key_t *key)
|
|
{
|
|
return _cairo_user_data_array_get_data (&font_face->user_data,
|
|
key);
|
|
}
|
|
|
|
/**
|
|
* cairo_font_face_set_user_data:
|
|
* @font_face: a #cairo_font_face_t
|
|
* @key: the address of a #cairo_user_data_key_t to attach the user data to
|
|
* @user_data: the user data to attach to the font face
|
|
* @destroy: a #cairo_destroy_func_t which will be called when the
|
|
* font face is destroyed or when new user data is attached using the
|
|
* same key.
|
|
*
|
|
* Attach user data to @font_face. To remove user data from a font face,
|
|
* call this function with the key that was used to set it and %NULL
|
|
* for @data.
|
|
*
|
|
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
|
|
* slot could not be allocated for the user data.
|
|
**/
|
|
cairo_status_t
|
|
cairo_font_face_set_user_data (cairo_font_face_t *font_face,
|
|
const cairo_user_data_key_t *key,
|
|
void *user_data,
|
|
cairo_destroy_func_t destroy)
|
|
{
|
|
if (font_face->ref_count == -1)
|
|
return CAIRO_STATUS_NO_MEMORY;
|
|
|
|
return _cairo_user_data_array_set_data (&font_face->user_data,
|
|
key, user_data, destroy);
|
|
}
|
|
|
|
/* cairo_simple_font_face_t - simple family/slant/weight font faces used for
|
|
* the built-in font API
|
|
*/
|
|
|
|
typedef struct _cairo_simple_font_face cairo_simple_font_face_t;
|
|
|
|
struct _cairo_simple_font_face {
|
|
cairo_font_face_t base;
|
|
char *family;
|
|
cairo_font_slant_t slant;
|
|
cairo_font_weight_t weight;
|
|
};
|
|
|
|
/* We maintain a global cache from family/weight/slant => cairo_font_face_t
|
|
* for cairo_simple_font_t. The primary purpose of this cache is to provide
|
|
* unique cairo_font_face_t values so that our cache from
|
|
* cairo_font_face_t => cairo_scaled_font_t works. For this reason, we don't need
|
|
* this cache to keep font faces alive; we just add them to the cache and
|
|
* remove them again when freed.
|
|
*/
|
|
|
|
typedef struct {
|
|
cairo_cache_entry_base_t base;
|
|
const char *family;
|
|
cairo_font_slant_t slant;
|
|
cairo_font_weight_t weight;
|
|
} cairo_simple_cache_key_t;
|
|
|
|
typedef struct {
|
|
cairo_simple_cache_key_t key;
|
|
cairo_simple_font_face_t *font_face;
|
|
} cairo_simple_cache_entry_t;
|
|
|
|
static const cairo_cache_backend_t _cairo_simple_font_cache_backend;
|
|
|
|
CAIRO_MUTEX_DECLARE(_global_simple_cache_mutex);
|
|
|
|
static void
|
|
_lock_global_simple_cache (void)
|
|
{
|
|
CAIRO_MUTEX_LOCK (_global_simple_cache_mutex);
|
|
}
|
|
|
|
static void
|
|
_unlock_global_simple_cache (void)
|
|
{
|
|
CAIRO_MUTEX_UNLOCK (_global_simple_cache_mutex);
|
|
}
|
|
|
|
static cairo_cache_t *
|
|
_get_global_simple_cache (void)
|
|
{
|
|
static cairo_cache_t *global_simple_cache = NULL;
|
|
|
|
if (global_simple_cache == NULL)
|
|
{
|
|
global_simple_cache = malloc (sizeof (cairo_cache_t));
|
|
if (!global_simple_cache)
|
|
goto FAIL;
|
|
|
|
if (_cairo_cache_init (global_simple_cache,
|
|
&_cairo_simple_font_cache_backend,
|
|
0)) /* No memory limit */
|
|
goto FAIL;
|
|
}
|
|
return global_simple_cache;
|
|
|
|
FAIL:
|
|
if (global_simple_cache)
|
|
free (global_simple_cache);
|
|
global_simple_cache = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
static unsigned long
|
|
_cairo_simple_font_cache_hash (void *cache, void *key)
|
|
{
|
|
cairo_simple_cache_key_t *k = (cairo_simple_cache_key_t *) key;
|
|
unsigned long hash;
|
|
|
|
/* 1607 and 1451 are just a couple random primes. */
|
|
hash = _cairo_hash_string (k->family);
|
|
hash += ((unsigned long) k->slant) * 1607;
|
|
hash += ((unsigned long) k->weight) * 1451;
|
|
|
|
return hash;
|
|
}
|
|
|
|
static int
|
|
_cairo_simple_font_cache_keys_equal (void *cache,
|
|
void *k1,
|
|
void *k2)
|
|
{
|
|
cairo_simple_cache_key_t *a;
|
|
cairo_simple_cache_key_t *b;
|
|
a = (cairo_simple_cache_key_t *) k1;
|
|
b = (cairo_simple_cache_key_t *) k2;
|
|
|
|
return strcmp (a->family, b->family) == 0 &&
|
|
a->slant == b->slant &&
|
|
a->weight == b->weight;
|
|
}
|
|
|
|
static cairo_simple_font_face_t *
|
|
_cairo_simple_font_face_create_from_cache_key (cairo_simple_cache_key_t *key)
|
|
{
|
|
cairo_simple_font_face_t *simple_face;
|
|
|
|
simple_face = malloc (sizeof (cairo_simple_font_face_t));
|
|
if (!simple_face)
|
|
return NULL;
|
|
|
|
simple_face->family = strdup (key->family);
|
|
if (!simple_face->family) {
|
|
free (simple_face);
|
|
return NULL;
|
|
}
|
|
|
|
simple_face->slant = key->slant;
|
|
simple_face->weight = key->weight;
|
|
|
|
_cairo_font_face_init (&simple_face->base, &_cairo_simple_font_face_backend);
|
|
|
|
return simple_face;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_simple_font_cache_create_entry (void *cache,
|
|
void *key,
|
|
void **return_entry)
|
|
{
|
|
cairo_simple_cache_key_t *k = (cairo_simple_cache_key_t *) key;
|
|
cairo_simple_cache_entry_t *entry;
|
|
|
|
entry = malloc (sizeof (cairo_simple_cache_entry_t));
|
|
if (entry == NULL)
|
|
return CAIRO_STATUS_NO_MEMORY;
|
|
|
|
entry->font_face = _cairo_simple_font_face_create_from_cache_key (k);
|
|
if (!entry->font_face) {
|
|
free (entry);
|
|
return CAIRO_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
entry->key.base.memory = 0;
|
|
entry->key.family = entry->font_face->family;
|
|
entry->key.slant = entry->font_face->slant;
|
|
entry->key.weight = entry->font_face->weight;
|
|
|
|
*return_entry = entry;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Entries are never spontaneously destroyed; but only when
|
|
* we remove them from the cache specifically. We free entry->font_face
|
|
* in the code that removes the entry from the cache
|
|
*/
|
|
static void
|
|
_cairo_simple_font_cache_destroy_entry (void *cache,
|
|
void *entry)
|
|
{
|
|
cairo_simple_cache_entry_t *e = (cairo_simple_cache_entry_t *) entry;
|
|
|
|
free (e);
|
|
}
|
|
|
|
static void
|
|
_cairo_simple_font_cache_destroy_cache (void *cache)
|
|
{
|
|
free (cache);
|
|
}
|
|
|
|
static const cairo_cache_backend_t _cairo_simple_font_cache_backend = {
|
|
_cairo_simple_font_cache_hash,
|
|
_cairo_simple_font_cache_keys_equal,
|
|
_cairo_simple_font_cache_create_entry,
|
|
_cairo_simple_font_cache_destroy_entry,
|
|
_cairo_simple_font_cache_destroy_cache
|
|
};
|
|
|
|
static void
|
|
_cairo_simple_font_face_destroy (void *abstract_face)
|
|
{
|
|
cairo_simple_font_face_t *simple_face = abstract_face;
|
|
cairo_cache_t *cache;
|
|
cairo_simple_cache_key_t key;
|
|
|
|
if (simple_face == NULL)
|
|
return;
|
|
|
|
_lock_global_simple_cache ();
|
|
cache = _get_global_simple_cache ();
|
|
assert (cache);
|
|
|
|
key.family = simple_face->family;
|
|
key.slant = simple_face->slant;
|
|
key.weight = simple_face->weight;
|
|
|
|
_cairo_cache_remove (cache, &key);
|
|
|
|
_unlock_global_simple_cache ();
|
|
|
|
free (simple_face->family);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_simple_font_face_create_font (void *abstract_face,
|
|
const cairo_matrix_t *font_matrix,
|
|
const cairo_matrix_t *ctm,
|
|
const cairo_font_options_t *options,
|
|
cairo_scaled_font_t **scaled_font)
|
|
{
|
|
const cairo_scaled_font_backend_t * backend = CAIRO_SCALED_FONT_BACKEND_DEFAULT;
|
|
|
|
cairo_simple_font_face_t *simple_face = abstract_face;
|
|
|
|
return backend->create (simple_face->family, simple_face->slant, simple_face->weight,
|
|
font_matrix, ctm, options, scaled_font);
|
|
}
|
|
|
|
static const cairo_font_face_backend_t _cairo_simple_font_face_backend = {
|
|
_cairo_simple_font_face_destroy,
|
|
_cairo_simple_font_face_create_font,
|
|
};
|
|
|
|
/**
|
|
* _cairo_simple_font_face_create:
|
|
* @family: a font family name, encoded in UTF-8
|
|
* @slant: the slant for the font
|
|
* @weight: the weight for the font
|
|
*
|
|
* Creates a font face from a triplet of family, slant, and weight.
|
|
* These font faces are used in implementation of the the #cairo_t "toy"
|
|
* font API.
|
|
*
|
|
* Return value: a newly created #cairo_font_face_t, destroy with
|
|
* cairo_font_face_destroy()
|
|
**/
|
|
cairo_font_face_t *
|
|
_cairo_simple_font_face_create (const char *family,
|
|
cairo_font_slant_t slant,
|
|
cairo_font_weight_t weight)
|
|
{
|
|
cairo_simple_cache_entry_t *entry;
|
|
cairo_simple_cache_key_t key;
|
|
cairo_cache_t *cache;
|
|
cairo_status_t status;
|
|
cairo_bool_t created_entry;
|
|
|
|
key.family = family;
|
|
key.slant = slant;
|
|
key.weight = weight;
|
|
|
|
_lock_global_simple_cache ();
|
|
cache = _get_global_simple_cache ();
|
|
if (cache == NULL) {
|
|
_unlock_global_simple_cache ();
|
|
_cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
return (cairo_font_face_t *)&_cairo_font_face_nil;
|
|
}
|
|
status = _cairo_cache_lookup (cache, &key, (void **) &entry, &created_entry);
|
|
if (status == CAIRO_STATUS_SUCCESS && !created_entry)
|
|
cairo_font_face_reference (&entry->font_face->base);
|
|
|
|
_unlock_global_simple_cache ();
|
|
if (status) {
|
|
_cairo_error (status);
|
|
return (cairo_font_face_t *)&_cairo_font_face_nil;
|
|
}
|
|
|
|
return &entry->font_face->base;
|
|
}
|
|
|
|
/* cairo_scaled_font_t */
|
|
|
|
static const cairo_scaled_font_t _cairo_scaled_font_nil = {
|
|
CAIRO_STATUS_NO_MEMORY, /* status */
|
|
-1, /* ref_count */
|
|
{ 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} ,
|
|
NULL, /* font_face */
|
|
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)
|
|
{
|
|
scaled_font->status = status;
|
|
|
|
_cairo_error (status);
|
|
}
|
|
|
|
/**
|
|
* cairo_scaled_font_status:
|
|
* @surface: 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 cache from cairo_font_face_t/matrix/ctm => cairo_scaled_font_t.
|
|
*
|
|
* The implementation is messy because we want
|
|
*
|
|
* - All otherwise referenced cairo_scaled_font_t's to be in the cache
|
|
* - Some number of not otherwise referenced cairo_scaled_font_t's
|
|
*
|
|
* For this reason, we actually use *two* caches ... a finite size
|
|
* cache that references the cairo_scaled_font_t as a first level (the outer
|
|
* cache), then an infinite size cache as the second level (the inner
|
|
* cache). A single cache could be used at the cost of complicating
|
|
* cairo-cache.c
|
|
*/
|
|
|
|
/* This defines the size of the outer cache ... that is, the number
|
|
* of scaled fonts we keep around even when not otherwise referenced
|
|
*/
|
|
#define MAX_CACHED_FONTS 24
|
|
|
|
typedef struct {
|
|
cairo_cache_entry_base_t base;
|
|
cairo_font_face_t *font_face;
|
|
const cairo_matrix_t *font_matrix;
|
|
const cairo_matrix_t *ctm;
|
|
cairo_font_options_t options;
|
|
} cairo_font_cache_key_t;
|
|
|
|
typedef struct {
|
|
cairo_font_cache_key_t key;
|
|
cairo_scaled_font_t *scaled_font;
|
|
} cairo_font_cache_entry_t;
|
|
|
|
static const cairo_cache_backend_t _cairo_outer_font_cache_backend;
|
|
static const cairo_cache_backend_t _cairo_inner_font_cache_backend;
|
|
|
|
CAIRO_MUTEX_DECLARE(_global_font_cache_mutex);
|
|
|
|
static void
|
|
_lock_global_font_cache (void)
|
|
{
|
|
CAIRO_MUTEX_LOCK (_global_font_cache_mutex);
|
|
}
|
|
|
|
static void
|
|
_unlock_global_font_cache (void)
|
|
{
|
|
CAIRO_MUTEX_UNLOCK (_global_font_cache_mutex);
|
|
}
|
|
|
|
static cairo_cache_t *
|
|
_get_outer_font_cache (void)
|
|
{
|
|
static cairo_cache_t *outer_font_cache = NULL;
|
|
|
|
if (outer_font_cache == NULL)
|
|
{
|
|
outer_font_cache = malloc (sizeof (cairo_cache_t));
|
|
if (!outer_font_cache)
|
|
goto FAIL;
|
|
|
|
if (_cairo_cache_init (outer_font_cache,
|
|
&_cairo_outer_font_cache_backend,
|
|
MAX_CACHED_FONTS))
|
|
goto FAIL;
|
|
}
|
|
return outer_font_cache;
|
|
|
|
FAIL:
|
|
if (outer_font_cache)
|
|
free (outer_font_cache);
|
|
outer_font_cache = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
static cairo_cache_t *
|
|
_get_inner_font_cache (void)
|
|
{
|
|
static cairo_cache_t *inner_font_cache = NULL;
|
|
|
|
if (inner_font_cache == NULL)
|
|
{
|
|
inner_font_cache = malloc (sizeof (cairo_cache_t));
|
|
if (!inner_font_cache)
|
|
goto FAIL;
|
|
|
|
if (_cairo_cache_init (inner_font_cache,
|
|
&_cairo_inner_font_cache_backend,
|
|
MAX_CACHED_FONTS))
|
|
goto FAIL;
|
|
}
|
|
return inner_font_cache;
|
|
|
|
FAIL:
|
|
if (inner_font_cache)
|
|
free (inner_font_cache);
|
|
inner_font_cache = NULL;
|
|
return 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 unsigned long
|
|
_cairo_font_cache_hash (void *cache, void *key)
|
|
{
|
|
cairo_font_cache_key_t *k = (cairo_font_cache_key_t *) key;
|
|
uint32_t hash = FNV1_32_INIT;
|
|
|
|
/* We do a bytewise hash on the font matrices */
|
|
hash = _hash_bytes_fnv ((unsigned char *)(&k->font_matrix->xx),
|
|
sizeof(double) * 4,
|
|
hash);
|
|
hash = _hash_bytes_fnv ((unsigned char *)(&k->ctm->xx),
|
|
sizeof(double) * 4,
|
|
hash);
|
|
|
|
return (hash ^
|
|
(unsigned long)k->font_face ^
|
|
cairo_font_options_hash (&k->options));
|
|
}
|
|
|
|
static int
|
|
_cairo_font_cache_keys_equal (void *cache,
|
|
void *k1,
|
|
void *k2)
|
|
{
|
|
cairo_font_cache_key_t *a;
|
|
cairo_font_cache_key_t *b;
|
|
a = (cairo_font_cache_key_t *) k1;
|
|
b = (cairo_font_cache_key_t *) k2;
|
|
|
|
return (a->font_face == b->font_face &&
|
|
memcmp ((unsigned char *)(&a->font_matrix->xx),
|
|
(unsigned char *)(&b->font_matrix->xx),
|
|
sizeof(double) * 4) == 0 &&
|
|
memcmp ((unsigned char *)(&a->ctm->xx),
|
|
(unsigned char *)(&b->ctm->xx),
|
|
sizeof(double) * 4) == 0 &&
|
|
cairo_font_options_equal (&a->options, &b->options));
|
|
}
|
|
|
|
/* The cache lookup failed in the outer cache, so we pull
|
|
* the font from the inner cache (if that in turns fails,
|
|
* it will create the font
|
|
*/
|
|
static cairo_status_t
|
|
_cairo_outer_font_cache_create_entry (void *cache,
|
|
void *key,
|
|
void **return_entry)
|
|
{
|
|
cairo_font_cache_entry_t *entry;
|
|
cairo_font_cache_entry_t *inner_entry;
|
|
cairo_bool_t created_entry;
|
|
cairo_status_t status;
|
|
|
|
entry = malloc (sizeof (cairo_font_cache_entry_t));
|
|
if (entry == NULL)
|
|
return CAIRO_STATUS_NO_MEMORY;
|
|
|
|
cache = _get_inner_font_cache ();
|
|
if (cache == NULL) {
|
|
_unlock_global_font_cache ();
|
|
return CAIRO_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
status = _cairo_cache_lookup (cache, key, (void **) &inner_entry, &created_entry);
|
|
if (status) {
|
|
free (entry);
|
|
return status;
|
|
}
|
|
|
|
entry->scaled_font = inner_entry->scaled_font;
|
|
if (!created_entry)
|
|
cairo_scaled_font_reference (entry->scaled_font);
|
|
|
|
entry->key.base.memory = 1;
|
|
entry->key.font_face = entry->scaled_font->font_face;
|
|
entry->key.font_matrix = &entry->scaled_font->font_matrix;
|
|
entry->key.ctm = &entry->scaled_font->ctm;
|
|
entry->key.options = ((cairo_font_cache_key_t *) key)->options;
|
|
|
|
*return_entry = entry;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_outer_font_cache_destroy_entry (void *cache,
|
|
void *entry)
|
|
{
|
|
cairo_font_cache_entry_t *e = (cairo_font_cache_entry_t *) entry;
|
|
|
|
cairo_scaled_font_destroy (e->scaled_font);
|
|
|
|
free (e);
|
|
}
|
|
|
|
/* Called when the lookup fails in the inner cache as well; there
|
|
* is no existing font, so we have to create one.
|
|
*/
|
|
static cairo_status_t
|
|
_cairo_inner_font_cache_create_entry (void *cache,
|
|
void *key,
|
|
void **return_entry)
|
|
{
|
|
cairo_font_cache_key_t *k = (cairo_font_cache_key_t *) key;
|
|
cairo_font_cache_entry_t *entry;
|
|
cairo_status_t status;
|
|
|
|
entry = malloc (sizeof (cairo_font_cache_entry_t));
|
|
if (entry == NULL)
|
|
return CAIRO_STATUS_NO_MEMORY;
|
|
|
|
status = k->font_face->backend->create_font (k->font_face,
|
|
k->font_matrix,
|
|
k->ctm,
|
|
&k->options,
|
|
&entry->scaled_font);
|
|
if (status) {
|
|
free (entry);
|
|
return status;
|
|
}
|
|
|
|
entry->scaled_font->font_face = k->font_face;
|
|
cairo_font_face_reference (k->font_face);
|
|
|
|
entry->key.base.memory = 0;
|
|
entry->key.font_face = k->font_face;
|
|
entry->key.font_matrix = &entry->scaled_font->font_matrix;
|
|
entry->key.ctm = &entry->scaled_font->ctm;
|
|
entry->key.options = k->options;
|
|
|
|
*return_entry = entry;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Entries in the inner font cache are never spontaneously destroyed;
|
|
* but only when we remove them from the cache specifically. We free
|
|
* entry->scaled_font in the code that removes the entry from the cache
|
|
*/
|
|
static void
|
|
_cairo_inner_font_cache_destroy_entry (void *cache,
|
|
void *entry)
|
|
{
|
|
free (entry);
|
|
}
|
|
|
|
static void
|
|
_cairo_font_cache_destroy_cache (void *cache)
|
|
{
|
|
free (cache);
|
|
}
|
|
|
|
static const cairo_cache_backend_t _cairo_outer_font_cache_backend = {
|
|
_cairo_font_cache_hash,
|
|
_cairo_font_cache_keys_equal,
|
|
_cairo_outer_font_cache_create_entry,
|
|
_cairo_outer_font_cache_destroy_entry,
|
|
_cairo_font_cache_destroy_cache
|
|
};
|
|
|
|
static const cairo_cache_backend_t _cairo_inner_font_cache_backend = {
|
|
_cairo_font_cache_hash,
|
|
_cairo_font_cache_keys_equal,
|
|
_cairo_inner_font_cache_create_entry,
|
|
_cairo_inner_font_cache_destroy_entry,
|
|
_cairo_font_cache_destroy_cache
|
|
};
|
|
|
|
/**
|
|
* 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_font_cache_entry_t *entry;
|
|
cairo_font_cache_key_t key;
|
|
cairo_cache_t *cache;
|
|
cairo_status_t status;
|
|
|
|
if (font_face->status)
|
|
return (cairo_scaled_font_t*) &_cairo_scaled_font_nil;
|
|
|
|
key.font_face = font_face;
|
|
key.font_matrix = font_matrix;
|
|
key.ctm = ctm;
|
|
key.options = *options;
|
|
|
|
_lock_global_font_cache ();
|
|
cache = _get_outer_font_cache ();
|
|
if (cache == NULL) {
|
|
_unlock_global_font_cache ();
|
|
_cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
return (cairo_scaled_font_t*) &_cairo_scaled_font_nil;
|
|
}
|
|
|
|
status = _cairo_cache_lookup (cache, &key, (void **) &entry, NULL);
|
|
if (status == CAIRO_STATUS_SUCCESS)
|
|
cairo_scaled_font_reference (entry->scaled_font);
|
|
|
|
_unlock_global_font_cache ();
|
|
if (status) {
|
|
_cairo_error (status);
|
|
return (cairo_scaled_font_t*) &_cairo_scaled_font_nil;
|
|
}
|
|
|
|
return entry->scaled_font;
|
|
}
|
|
|
|
void
|
|
_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font,
|
|
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->status = CAIRO_STATUS_SUCCESS;
|
|
|
|
scaled_font->font_matrix = *font_matrix;
|
|
scaled_font->ctm = *ctm;
|
|
cairo_matrix_multiply (&scaled_font->scale, &scaled_font->font_matrix, &scaled_font->ctm);
|
|
|
|
scaled_font->options = *options;
|
|
|
|
scaled_font->ref_count = 1;
|
|
scaled_font->backend = backend;
|
|
}
|
|
|
|
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;
|
|
|
|
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)
|
|
{
|
|
unscaled_font->ref_count = 1;
|
|
unscaled_font->backend = backend;
|
|
}
|
|
|
|
void
|
|
_cairo_unscaled_font_reference (cairo_unscaled_font_t *unscaled_font)
|
|
{
|
|
if (unscaled_font == NULL)
|
|
return;
|
|
|
|
unscaled_font->ref_count++;
|
|
}
|
|
|
|
void
|
|
_cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font)
|
|
{
|
|
if (unscaled_font == NULL)
|
|
return;
|
|
|
|
if (--(unscaled_font->ref_count) > 0)
|
|
return;
|
|
|
|
unscaled_font->backend->destroy (unscaled_font);
|
|
|
|
free (unscaled_font);
|
|
}
|
|
|
|
|
|
|
|
/* Public font API follows. */
|
|
|
|
/**
|
|
* 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.
|
|
**/
|
|
void
|
|
cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font)
|
|
{
|
|
if (scaled_font == NULL)
|
|
return;
|
|
|
|
if (scaled_font->ref_count == (unsigned int)-1)
|
|
return;
|
|
|
|
scaled_font->ref_count++;
|
|
}
|
|
|
|
/**
|
|
* 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_font_cache_key_t key;
|
|
cairo_cache_t *cache;
|
|
|
|
if (scaled_font == NULL)
|
|
return;
|
|
|
|
if (scaled_font->ref_count == (unsigned int)-1)
|
|
return;
|
|
|
|
if (--(scaled_font->ref_count) > 0)
|
|
return;
|
|
|
|
if (scaled_font->font_face) {
|
|
_lock_global_font_cache ();
|
|
cache = _get_inner_font_cache ();
|
|
assert (cache);
|
|
|
|
key.font_face = scaled_font->font_face;
|
|
key.font_matrix = &scaled_font->font_matrix;
|
|
key.ctm = &scaled_font->ctm;
|
|
key.options = scaled_font->options;
|
|
|
|
_cairo_cache_remove (cache, &key);
|
|
_unlock_global_font_cache ();
|
|
|
|
cairo_font_face_destroy (scaled_font->font_face);
|
|
}
|
|
|
|
scaled_font->backend->destroy (scaled_font);
|
|
|
|
free (scaled_font);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* Return value: %CAIRO_STATUS_SUCCESS on success. Otherwise, an
|
|
* error such as %CAIRO_STATUS_NO_MEMORY.
|
|
**/
|
|
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) {
|
|
_cairo_scaled_font_set_error (scaled_font, 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) {
|
|
_cairo_scaled_font_set_error (scaled_font, 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()
|
|
{
|
|
CAIRO_MUTEX_LOCK (_global_image_glyph_cache_mutex);
|
|
}
|
|
|
|
void
|
|
_cairo_unlock_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 ()
|
|
{
|
|
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;
|
|
}
|