/** * SECTION:cairo-scaled-font * @Title: cairo_scaled_font_t * @Short_Description: Font face at particular size and options * @See_Also: #cairo_font_face_t, #cairo_matrix_t, #cairo_font_options_t * * #cairo_scaled_font_t represents a realization of a font face at a particular * size and transformation and a certain set of font options.
**/
/* Global Glyph Cache * * We maintain a global pool of glyphs split between all active fonts. This * allows a heavily used individual font to cache more glyphs than we could * manage if we used per-font glyph caches, but at the same time maintains * fairness across all fonts and provides a cap on the maximum number of * global glyphs. * * The glyphs are allocated in pages, which are capped in the global pool. * Using pages means we can reduce the frequency at which we have to probe the * global pool and ameliorates the memory allocation pressure.
*/
/* XXX: This number is arbitrary---we've never done any measurement of this. */ #define MAX_GLYPH_PAGES_CACHED 512 static cairo_cache_t cairo_scaled_glyph_page_cache;
/* * Notes: * * To store rasterizations of glyphs, we use an image surface and the * device offset to represent the glyph origin. * * A device_transform converts from device space (a conceptual space) to * surface space. For simple cases of translation only, it's called a * device_offset and is public API (cairo_surface_[gs]et_device_offset()). * A possibly better name for those functions could have been * cairo_surface_[gs]et_origin(). So, that's what they do: they set where * the device-space origin (0,0) is in the surface. If the origin is inside * the surface, device_offset values are positive. It may look like this: * * Device space: * (-x,-y) <-- negative numbers * +----------------+ * | . | * | . | * |......(0,0) <---|-- device-space origin * | | * | | * +----------------+ * (width-x,height-y) * * Surface space: * (0,0) <-- surface-space origin * +---------------+ * | . | * | . | * |......(x,y) <--|-- device_offset * | | * | | * +---------------+ * (width,height) * * In other words: device_offset is the coordinates of the device-space * origin relative to the top-left of the surface. * * We use device offsets in a couple of places: * * - Public API: To let toolkits like Gtk+ give user a surface that * only represents part of the final destination (say, the expose * area), but has the same device space as the destination. In these * cases device_offset is typically negative. Example: * * application window * +---------------+ * | . | * | (x,y). | * |......+---+ | * | | | <--|-- expose area * | +---+ | * +---------------+ * * In this case, the user of cairo API can set the device_space on * the expose area to (-x,-y) to move the device space origin to that * of the application window, such that drawing in the expose area * surface and painting it in the application window has the same * effect as drawing in the application window directly. Gtk+ has * been using this feature. * * - Glyph surfaces: In most font rendering systems, glyph surfaces * have an origin at (0,0) and a bounding box that is typically * represented as (x_bearing,y_bearing,width,height). Depending on * which way y progresses in the system, y_bearing may typically be * negative (for systems similar to cairo, with origin at top left), * or be positive (in systems like PDF with origin at bottom left). * No matter which is the case, it is important to note that * (x_bearing,y_bearing) is the coordinates of top-left of the glyph * relative to the glyph origin. That is, for example: * * Scaled-glyph space: * * (x_bearing,y_bearing) <-- negative numbers * +----------------+ * | . | * | . | * |......(0,0) <---|-- glyph origin * | | * | | * +----------------+ * (width+x_bearing,height+y_bearing) * * Note the similarity of the origin to the device space. That is * exactly how we use the device_offset to represent scaled glyphs: * to use the device-space origin as the glyph origin. * * Now compare the scaled-glyph space to device-space and surface-space * and convince yourself that: * * (x_bearing,y_bearing) = (-x,-y) = - device_offset * * That's right. If you are not convinced yet, contrast the definition * of the two: * * "(x_bearing,y_bearing) is the coordinates of top-left of the * glyph relative to the glyph origin." * * "In other words: device_offset is the coordinates of the * device-space origin relative to the top-left of the surface." * * and note that glyph origin = device-space origin.
*/
if (scaled_glyph->surface != NULL)
cairo_surface_destroy (&scaled_glyph->surface->base);
if (scaled_glyph->path != NULL)
_cairo_path_fixed_destroy (scaled_glyph->path);
if (scaled_glyph->recording_surface != NULL) {
cairo_status_t status;
/* If the recording surface contains other fonts, destroying * it while holding _cairo_scaled_glyph_page_cache_mutex will * result in deadlock when the recording surface font is * destroyed. Instead, move the recording surface to a list of * surfaces to free and free it in * _cairo_scaled_font_thaw_cache() after
* _cairo_scaled_glyph_page_cache_mutex is unlocked. */
status = _cairo_array_append (&scaled_font->recording_surfaces_to_free, &scaled_glyph->recording_surface);
assert (status == CAIRO_STATUS_SUCCESS);
}
if (scaled_glyph->color_surface != NULL)
cairo_surface_destroy (&scaled_glyph->color_surface->base);
}
/** * _cairo_scaled_font_set_error: * @scaled_font: a scaled_font * @status: a status value indicating an error * * Atomically sets scaled_font->status to @status and calls _cairo_error; * Does nothing if status is %CAIRO_STATUS_SUCCESS. * * All assignments of an error status to scaled_font->status should happen * through _cairo_scaled_font_set_error(). Note that due to the nature of * the atomic operation, it is not safe to call this function on the nil * objects. * * 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. * * Return value: the error status.
**/
cairo_status_t
_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font,
cairo_status_t status)
{ if (status == CAIRO_STATUS_SUCCESS) return status;
/* Don't overwrite an existing error. This preserves the first
* error, which is the most significant. */
_cairo_status_set_error (&scaled_font->status, status);
return _cairo_error (status);
}
/** * cairo_scaled_font_get_type: * @scaled_font: a #cairo_scaled_font_t * * This function returns the type of the backend used to create * a scaled font. See #cairo_font_type_t for available types. * However, this function never returns %CAIRO_FONT_TYPE_TOY. * * Return value: The type of @scaled_font. * * Since: 1.2
**/
cairo_font_type_t
cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font)
{ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) return CAIRO_FONT_TYPE_TOY;
return scaled_font->backend->type;
}
/** * 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. * * Since: 1.0
**/
cairo_status_t
cairo_scaled_font_status (cairo_scaled_font_t *scaled_font)
{ return scaled_font->status;
}
/* Here we keep a unique mapping from * font_face/matrix/ctm/font_options => #cairo_scaled_font_t. * * Here are the things that we want to map: * * a) All otherwise referenced #cairo_scaled_font_t's * b) Some number of not otherwise referenced #cairo_scaled_font_t's * * The implementation uses a hash table which covers (a) * completely. Then, for (b) we have an array of otherwise * unreferenced fonts (holdovers) which are expired in * least-recently-used order. * * The cairo_scaled_font_create() code gets to treat this like a regular * hash table. All of the magic for the little holdover cache is in * cairo_scaled_font_reference() and cairo_scaled_font_destroy().
*/
/* This defines the size of the holdover array ... that is, the number * of scaled fonts we keep around even when not otherwise referenced
*/ #define CAIRO_SCALED_FONT_MAX_HOLDOVERS 256
/* remove scaled_fonts starting from the end so that font_map->holdovers
* is always in a consistent state when we release the mutex. */ while (font_map->num_holdovers) {
scaled_font = font_map->holdovers[font_map->num_holdovers-1];
assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
_cairo_hash_table_remove (font_map->hash_table,
&scaled_font->hash_entry);
font_map->num_holdovers--;
/* This releases the font_map lock to avoid the possibility of a * recursive deadlock when the scaled font destroy closure gets * called
*/
_cairo_scaled_font_fini (scaled_font);
/* The font is locked in _cairo_scaled_glyph_page_can_remove () */
_cairo_scaled_glyph_page_destroy (scaled_font, page);
CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
}
/* If a scaled font wants to unlock the font map while still being * created (needed for user-fonts), we need to take extra care not * ending up with multiple identical scaled fonts being created. * * What we do is, we create a fake identical scaled font, and mark * it as placeholder, lock its mutex, and insert that in the fontmap * hash table. This makes other code trying to create an identical * scaled font to just wait and retry. * * The reason we have to create a fake scaled font instead of just using * scaled_font is for lifecycle management: we need to (or rather, * other code needs to) reference the scaled_font in the hash table. * We can't do that on the input scaled_font as it may be freed by * font backend upon error.
*/
/* full initialization is wasteful, but who cares... */
status = _cairo_scaled_font_init (placeholder_scaled_font,
scaled_font->font_face,
&scaled_font->font_matrix,
&scaled_font->ctm,
&scaled_font->options,
NULL); if (unlikely (status)) goto FREE_PLACEHOLDER;
placeholder_scaled_font->placeholder = TRUE;
placeholder_scaled_font->hash_entry.hash
= _cairo_scaled_font_compute_hash (placeholder_scaled_font);
status = _cairo_hash_table_insert (cairo_scaled_font_map->hash_table,
&placeholder_scaled_font->hash_entry); if (unlikely (status)) goto FINI_PLACEHOLDER;
staticvoid
_cairo_scaled_font_placeholder_wait_for_creation_to_finish (cairo_scaled_font_t *placeholder_scaled_font)
{ /* reference the place holder so it doesn't go away */
cairo_scaled_font_reference (placeholder_scaled_font);
/* now unlock the fontmap mutex so creation has a chance to finish */
CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
/* wait on placeholder mutex until we are awaken */
CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex);
/* ok, creation done. just clean up and back out */
CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
cairo_scaled_font_destroy (placeholder_scaled_font);
/* 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.
*/
/* We do a bytewise hash on the font matrices */
hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash);
hash = _hash_matrix_fnv (&scaled_font->ctm, hash);
hash = _hash_mix_bits (hash);
scaled_font->max_scale = MAX (fabs (scaled_font->scale.xx) + fabs (scaled_font->scale.xy),
fabs (scaled_font->scale.yx) + fabs (scaled_font->scale.yy));
scaled_font->scale_inverse = scaled_font->scale;
status = cairo_matrix_invert (&scaled_font->scale_inverse); if (unlikely (status)) { /* If the font scale matrix is rank 0, just using an all-zero inverse matrix * makes everything work correctly. This make font size 0 work without * producing an error. * * FIXME: If the scale is rank 1, we still go into error mode. But then * again, that's what we do everywhere in cairo. * * Also, the check for == 0. below may be too harsh...
*/ if (_cairo_matrix_is_scale_0 (&scaled_font->scale)) {
cairo_matrix_init (&scaled_font->scale_inverse,
0, 0, 0, 0,
-scaled_font->scale.x0,
-scaled_font->scale.y0);
} else return status;
}
staticvoid _cairo_scaled_font_free_recording_surfaces (cairo_scaled_font_t *scaled_font)
{ int num_recording_surfaces;
cairo_surface_t *surface;
num_recording_surfaces = _cairo_array_num_elements (&scaled_font->recording_surfaces_to_free); if (num_recording_surfaces > 0) { for (int i = 0; i < num_recording_surfaces; i++) {
_cairo_array_copy_element (&scaled_font->recording_surfaces_to_free, i, &surface);
cairo_surface_finish (surface);
cairo_surface_destroy (surface);
}
_cairo_array_truncate (&scaled_font->recording_surfaces_to_free, 0);
}
}
void
_cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font)
{ /* ensure we do not modify an error object */
assert (scaled_font->status == CAIRO_STATUS_SUCCESS);
/* Destroy scaled_font's pages while holding its lock only, and not the * global page cache lock. The destructor can cause us to recurse and
* end up back here for a different scaled_font. */
void
_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font)
{ /* Release the lock to avoid the possibility of a recursive
* deadlock when the scaled font destroy closure gets called. */
CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
_cairo_scaled_font_fini_internal (scaled_font);
CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
}
/** * 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() * * Since: 1.0
**/
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_font_face_t *original_font_face = font_face;
cairo_scaled_font_t key, *old = NULL, *scaled_font = NULL, *dead = NULL; double det;
status = font_face->status; if (unlikely (status)) return _cairo_scaled_font_create_in_error (status);
det = _cairo_matrix_compute_determinant (font_matrix); if (! ISFINITE (det)) return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX));
det = _cairo_matrix_compute_determinant (ctm); if (! ISFINITE (det)) return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX));
status = cairo_font_options_status ((cairo_font_options_t *) options); if (unlikely (status)) return _cairo_scaled_font_create_in_error (status);
/* Note that degenerate ctm or font_matrix *are* allowed.
* We want to support a font size of 0. */
if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { /* We increment the reference count manually here, (rather * than calling into cairo_scaled_font_reference), since we * must modify the reference count while our lock is still
* held. */
_cairo_reference_count_inc (&scaled_font->ref_count);
_cairo_scaled_font_map_unlock (); return scaled_font;
}
/* the font has been put into an error status - abandon the cache */
_cairo_hash_table_remove (font_map->hash_table,
&scaled_font->hash_entry);
scaled_font->hash_entry.hash = ZOMBIE;
dead = scaled_font;
font_map->mru_scaled_font = NULL;
}
while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table,
&key.hash_entry)))
{ if (! scaled_font->placeholder) break;
/* If the scaled font is being created (happens for user-font),
* just wait until it's done, then retry */
_cairo_scaled_font_placeholder_wait_for_creation_to_finish (scaled_font);
}
_cairo_scaled_font_fini_key (&key);
if (scaled_font != NULL) { /* 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, unless we caught the font in the middle of destruction.
*/ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { if (scaled_font->holdover) { int i;
for (i = 0; i < font_map->num_holdovers; i++) { if (font_map->holdovers[i] == scaled_font) {
font_map->num_holdovers--;
memmove (&font_map->holdovers[i],
&font_map->holdovers[i+1],
(font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*)); break;
}
}
scaled_font->holdover = FALSE;
}
/* reset any error status */
scaled_font->status = CAIRO_STATUS_SUCCESS;
}
if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { /* We increment the reference count manually here, (rather * than calling into cairo_scaled_font_reference), since we * must modify the reference count while our lock is still
* held. */
old = font_map->mru_scaled_font;
font_map->mru_scaled_font = scaled_font; /* increment reference count for the mru cache */
_cairo_reference_count_inc (&scaled_font->ref_count); /* and increment for the returned reference */
_cairo_reference_count_inc (&scaled_font->ref_count);
_cairo_scaled_font_map_unlock ();
cairo_scaled_font_destroy (old); if (font_face != original_font_face)
cairo_font_face_destroy (font_face);
return scaled_font;
}
/* the font has been put into an error status - abandon the cache */
_cairo_hash_table_remove (font_map->hash_table,
&scaled_font->hash_entry);
scaled_font->hash_entry.hash = ZOMBIE;
}
/* Otherwise create it and insert it into the hash table. */ if (font_face->backend->get_implementation != NULL) {
font_face = font_face->backend->get_implementation (font_face,
font_matrix,
ctm,
options); if (unlikely (font_face->status)) {
_cairo_scaled_font_map_unlock (); return _cairo_scaled_font_create_in_error (font_face->status);
}
}
status = font_face->backend->scaled_font_create (font_face, font_matrix,
ctm, options, &scaled_font); if (unlikely (status)) {
_cairo_scaled_font_map_unlock (); if (font_face != original_font_face)
cairo_font_face_destroy (font_face);
if (dead != NULL)
cairo_scaled_font_destroy (dead);
return _cairo_scaled_font_create_in_error (status);
} /* Or did we encounter an error whilst constructing the scaled font? */ if (unlikely (scaled_font->status)) {
_cairo_scaled_font_map_unlock (); if (font_face != original_font_face)
cairo_font_face_destroy (font_face);
if (dead != NULL)
cairo_scaled_font_destroy (dead);
return scaled_font;
}
/* Our caching above is defeated if the backend switches fonts on us - * e.g. old incarnations of toy-font-face and lazily resolved * ft-font-faces
*/
assert (scaled_font->font_face == font_face);
assert (! scaled_font->cache_frozen);
assert (! scaled_font->global_cache_frozen);
status = _cairo_hash_table_insert (font_map->hash_table,
&scaled_font->hash_entry); if (likely (status == CAIRO_STATUS_SUCCESS)) {
old = font_map->mru_scaled_font;
font_map->mru_scaled_font = scaled_font;
_cairo_reference_count_inc (&scaled_font->ref_count);
}
_cairo_scaled_font_map_unlock ();
cairo_scaled_font_destroy (old); if (font_face != original_font_face)
cairo_font_face_destroy (font_face);
if (dead != NULL)
cairo_scaled_font_destroy (dead);
if (unlikely (status)) { /* 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_internal (scaled_font);
free (scaled_font); return _cairo_scaled_font_create_in_error (status);
}
/* XXX This should disappear in favour of a common pool of error objects. */
cairo_scaled_font_t *
_cairo_scaled_font_create_in_error (cairo_status_t status)
{
cairo_scaled_font_t *scaled_font;
assert (status != CAIRO_STATUS_SUCCESS);
if (status == CAIRO_STATUS_NO_MEMORY) return (cairo_scaled_font_t *) &_cairo_scaled_font_nil;
/** * 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. * * Use cairo_scaled_font_get_reference_count() to get the number of * references to a #cairo_scaled_font_t. * * Returns: the referenced #cairo_scaled_font_t * * Since: 1.0
**/
cairo_scaled_font_t *
cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font)
{ if (scaled_font == NULL ||
CAIRO_REFERENCE_COUNT_IS_INVALID (&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(). * * Since: 1.0
**/ void
cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
{
cairo_scaled_font_t *lru = NULL;
cairo_scaled_font_map_t *font_map;
/* Another thread may have resurrected the font whilst we waited */ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { if (! scaled_font->placeholder &&
scaled_font->hash_entry.hash != ZOMBIE)
{ /* Another thread may have already inserted us into the holdovers */ if (scaled_font->holdover) goto unlock;
/* Rather than immediately destroying this object, we put it into * the font_map->holdovers array in case it will get used again * soon (and is why we must hold the lock over the atomic op on * the reference count). To make room for it, we do actually * destroy the least-recently-used holdover.
*/
/* If we pulled an item from the holdovers array, (while the font * map lock was held, of course), then there is no way that anyone * else could have acquired a reference to it. So we can now * safely call fini on it without any lock held. This is desirable * as we never want to call into any backend function with a lock
* held. */ if (lru != NULL) {
_cairo_scaled_font_fini_internal (lru);
free (lru);
}
}
/** * cairo_scaled_font_get_reference_count: * @scaled_font: a #cairo_scaled_font_t * * Returns the current reference count of @scaled_font. * * Return value: the current reference count of @scaled_font. If the * object is a nil object, 0 will be returned. * * Since: 1.4
**/ unsignedint
cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font)
{ if (scaled_font == NULL ||
CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) return 0;
/** * cairo_scaled_font_get_user_data: * @scaled_font: a #cairo_scaled_font_t * @key: the address of the #cairo_user_data_key_t the user data was * attached to * * Return user data previously attached to @scaled_font 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. * * Since: 1.4
**/ void *
cairo_scaled_font_get_user_data (cairo_scaled_font_t *scaled_font, const cairo_user_data_key_t *key)
{ return _cairo_user_data_array_get_data (&scaled_font->user_data,
key);
}
/** * cairo_scaled_font_set_user_data: * @scaled_font: a #cairo_scaled_font_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 #cairo_scaled_font_t * @destroy: a #cairo_destroy_func_t which will be called when the * #cairo_t is destroyed or when new user data is attached using the * same key. * * Attach user data to @scaled_font. To remove user data from a surface, * 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. * * Since: 1.4
**/
cairo_status_t
cairo_scaled_font_set_user_data (cairo_scaled_font_t *scaled_font, const cairo_user_data_key_t *key, void *user_data,
cairo_destroy_func_t destroy)
{ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) return scaled_font->status;
/** * 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. * * Since: 1.0
**/ void
cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font,
cairo_font_extents_t *extents)
{ if (scaled_font->status) {
extents->ascent = 0.0;
extents->descent = 0.0;
extents->height = 0.0;
extents->max_x_advance = 0.0;
extents->max_y_advance = 0.0; return;
}
*extents = scaled_font->extents;
}
/** * cairo_scaled_font_text_extents: * @scaled_font: a #cairo_scaled_font_t * @utf8: a NUL-terminated string of text, encoded in UTF-8 * @extents: a #cairo_text_extents_t which to store the retrieved extents. * * Gets the extents for a string of text. The extents describe a * user-space rectangle that encloses the "inked" portion of the text * drawn at the origin (0,0) (as it would be drawn by cairo_show_text() * if the cairo graphics state were set to the same font_face, * font_matrix, ctm, and font_options as @scaled_font). Additionally, * the x_advance and y_advance values indicate the amount by which the * current point would be advanced by cairo_show_text(). * * Note that whitespace characters do not directly contribute to the * size of the rectangle (extents.width and extents.height). They do * contribute indirectly by changing the position of non-whitespace * characters. In particular, trailing whitespace characters are * likely to not affect the size of the rectangle, though they will * affect the x_advance and y_advance values. * * Since: 1.2
**/ void
cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font, constchar *utf8,
cairo_text_extents_t *extents)
{
cairo_status_t status;
cairo_glyph_t *glyphs = NULL; int num_glyphs;
if (scaled_font->status) goto ZERO_EXTENTS;
if (utf8 == NULL) goto ZERO_EXTENTS;
status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0.,
utf8, -1,
&glyphs, &num_glyphs,
NULL, NULL,
NULL); if (unlikely (status)) {
status = _cairo_scaled_font_set_error (scaled_font, status); goto ZERO_EXTENTS;
}
/** * cairo_scaled_font_glyph_extents: * @scaled_font: a #cairo_scaled_font_t * @glyphs: an array of glyph IDs with X and Y offsets. * @num_glyphs: the number of glyphs in the @glyphs array * @extents: a #cairo_text_extents_t which to store the retrieved extents. * * Gets the extents for an array of glyphs. The extents describe a * user-space rectangle that encloses the "inked" portion of the * glyphs, (as they would be drawn by cairo_show_glyphs() if the cairo * graphics state were set to the same font_face, font_matrix, ctm, * and font_options as @scaled_font). Additionally, the x_advance and * y_advance values indicate the amount by which the current point * would be advanced by cairo_show_glyphs(). * * Note that whitespace glyphs do not contribute to the size of the * rectangle (extents.width and extents.height). * * Since: 1.0
**/ void
cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs,
cairo_text_extents_t *extents)
{
cairo_status_t status; int i; double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0;
cairo_bool_t visible = FALSE;
cairo_scaled_glyph_t *scaled_glyph = NULL;
for (i = 0; i < num_glyphs; i++) { double left, top, right, bottom;
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[i].index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
NULL, /* foreground color */
&scaled_glyph); if (unlikely (status)) {
status = _cairo_scaled_font_set_error (scaled_font, status); goto UNLOCK;
}
/* "Ink" extents should skip "invisible" glyphs */ if (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0) continue;
left = scaled_glyph->metrics.x_bearing + glyphs[i].x;
right = left + scaled_glyph->metrics.width;
top = scaled_glyph->metrics.y_bearing + glyphs[i].y;
bottom = top + scaled_glyph->metrics.height;
for (i = 0; i < GLYPH_LUT_SIZE; i++)
glyph_lut_unicode[i] = ~0U;
p = utf8; for (i = 0; i < num_chars; i++) { int idx, num_bytes;
uint32_t unicode;
cairo_scaled_glyph_t *scaled_glyph; struct glyph_lut_elt *glyph_slot;
num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
p += num_bytes;
glyphs[i].x = x;
glyphs[i].y = y;
idx = unicode % ARRAY_LENGTH (glyph_lut);
glyph_slot = &glyph_lut[idx]; if (glyph_lut_unicode[idx] == unicode) {
glyphs[i].index = glyph_slot->index;
x += glyph_slot->x_advance;
y += glyph_slot->y_advance;
} else { unsignedlong g;
g = scaled_font->backend->ucs4_to_index (scaled_font, unicode);
status = _cairo_scaled_glyph_lookup (scaled_font,
g,
CAIRO_SCALED_GLYPH_INFO_METRICS,
NULL, /* foreground color */
&scaled_glyph); if (unlikely (status)) return status;
x += scaled_glyph->metrics.x_advance;
y += scaled_glyph->metrics.y_advance;
static cairo_status_t
cairo_scaled_font_text_to_glyphs_internal_uncached (cairo_scaled_font_t *scaled_font, double x, double y, constchar *utf8,
cairo_glyph_t *glyphs,
cairo_text_cluster_t **clusters, int num_chars)
{ constchar *p; int i;
p = utf8; for (i = 0; i < num_chars; i++) { unsignedlong g; int num_bytes;
uint32_t unicode;
cairo_scaled_glyph_t *scaled_glyph;
cairo_status_t status;
num_bytes = _cairo_utf8_get_char_validated (p, &unicode);
p += num_bytes;
glyphs[i].x = x;
glyphs[i].y = y;
g = scaled_font->backend->ucs4_to_index (scaled_font, unicode);
/* * No advance needed for a single character string. So, let's speed up * one-character strings by skipping glyph lookup.
*/ if (num_chars > 1) {
status = _cairo_scaled_glyph_lookup (scaled_font,
g,
CAIRO_SCALED_GLYPH_INFO_METRICS,
NULL, /* foreground color */
&scaled_glyph); if (unlikely (status)) return status;
x += scaled_glyph->metrics.x_advance;
y += scaled_glyph->metrics.y_advance;
}
/** * cairo_scaled_font_text_to_glyphs: * @scaled_font: a #cairo_scaled_font_t * @x: X position to place first glyph * @y: Y position to place first glyph * @utf8: a string of text encoded in UTF-8 * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated * @glyphs: pointer to array of glyphs to fill * @num_glyphs: pointer to number of glyphs * @clusters: pointer to array of cluster mapping information to fill, or %NULL * @num_clusters: pointer to number of clusters, or %NULL * @cluster_flags: pointer to location to store cluster flags corresponding to the * output @clusters, or %NULL * * Converts UTF-8 text to an array of glyphs, optionally with cluster * mapping, that can be used to render later using @scaled_font. * * If @glyphs initially points to a non-%NULL value, that array is used * as a glyph buffer, and @num_glyphs should point to the number of glyph * entries available there. If the provided glyph array is too short for * the conversion, a new glyph array is allocated using cairo_glyph_allocate() * and placed in @glyphs. Upon return, @num_glyphs always contains the * number of generated glyphs. If the value @glyphs points to has changed * after the call, the user is responsible for freeing the allocated glyph * array using cairo_glyph_free(). This may happen even if the provided * array was large enough. * * If @clusters is not %NULL, @num_clusters and @cluster_flags should not be %NULL, * and cluster mapping will be computed. * The semantics of how cluster array allocation works is similar to the glyph * array. That is, * if @clusters initially points to a non-%NULL value, that array is used * as a cluster buffer, and @num_clusters should point to the number of cluster * entries available there. If the provided cluster array is too short for * the conversion, a new cluster array is allocated using cairo_text_cluster_allocate() * and placed in @clusters. Upon return, @num_clusters always contains the * number of generated clusters. If the value @clusters points at has changed * after the call, the user is responsible for freeing the allocated cluster * array using cairo_text_cluster_free(). This may happen even if the provided * array was large enough. * * In the simplest case, @glyphs and @clusters can point to %NULL initially * and a suitable array will be allocated. In code: * <informalexample><programlisting> * cairo_status_t status; * * cairo_glyph_t *glyphs = NULL; * int num_glyphs; * cairo_text_cluster_t *clusters = NULL; * int num_clusters; * cairo_text_cluster_flags_t cluster_flags; * * status = cairo_scaled_font_text_to_glyphs (scaled_font, * x, y, * utf8, utf8_len, * &glyphs, &num_glyphs, * &clusters, &num_clusters, &cluster_flags); * * if (status == CAIRO_STATUS_SUCCESS) { * cairo_show_text_glyphs (cr, * utf8, utf8_len, * glyphs, num_glyphs, * clusters, num_clusters, cluster_flags); * * cairo_glyph_free (glyphs); * cairo_text_cluster_free (clusters); * } * </programlisting></informalexample> * * If no cluster mapping is needed: * <informalexample><programlisting> * cairo_status_t status; * * cairo_glyph_t *glyphs = NULL; * int num_glyphs; * * status = cairo_scaled_font_text_to_glyphs (scaled_font, * x, y, * utf8, utf8_len, * &glyphs, &num_glyphs, * NULL, NULL, * NULL); * * if (status == CAIRO_STATUS_SUCCESS) { * cairo_show_glyphs (cr, glyphs, num_glyphs); * cairo_glyph_free (glyphs); * } * </programlisting></informalexample> * * If stack-based glyph and cluster arrays are to be used for small * arrays: * <informalexample><programlisting> * cairo_status_t status; * * cairo_glyph_t stack_glyphs[40]; * cairo_glyph_t *glyphs = stack_glyphs; * int num_glyphs = sizeof (stack_glyphs) / sizeof (stack_glyphs[0]); * cairo_text_cluster_t stack_clusters[40]; * cairo_text_cluster_t *clusters = stack_clusters; * int num_clusters = sizeof (stack_clusters) / sizeof (stack_clusters[0]); * cairo_text_cluster_flags_t cluster_flags; * * status = cairo_scaled_font_text_to_glyphs (scaled_font, * x, y, * utf8, utf8_len, * &glyphs, &num_glyphs, * &clusters, &num_clusters, &cluster_flags); * * if (status == CAIRO_STATUS_SUCCESS) { * cairo_show_text_glyphs (cr, * utf8, utf8_len, * glyphs, num_glyphs, * clusters, num_clusters, cluster_flags); * * if (glyphs != stack_glyphs) * cairo_glyph_free (glyphs); * if (clusters != stack_clusters) * cairo_text_cluster_free (clusters); * } * </programlisting></informalexample> * * For details of how @clusters, @num_clusters, and @cluster_flags map input * UTF-8 text to the output glyphs see cairo_show_text_glyphs(). * * The output values can be readily passed to cairo_show_text_glyphs() * cairo_show_glyphs(), or related functions, assuming that the exact * same @scaled_font is used for the operation. * * Return value: %CAIRO_STATUS_SUCCESS upon success, or an error status * if the input values are wrong or if conversion failed. If the input * values are correct but the conversion failed, the error status is also * set on @scaled_font. * * Since: 1.8
**/ #define CACHING_THRESHOLD 16
cairo_status_t
cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, double x, double y, constchar *utf8, int utf8_len,
cairo_glyph_t **glyphs, int *num_glyphs,
cairo_text_cluster_t **clusters, int *num_clusters,
cairo_text_cluster_flags_t *cluster_flags)
{ int num_chars = 0;
cairo_int_status_t status;
cairo_glyph_t *orig_glyphs;
cairo_text_cluster_t *orig_clusters;
status = scaled_font->status; if (unlikely (status)) return status;
/* A slew of sanity checks */
/* glyphs and num_glyphs can't be NULL */
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.