/* 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;
/* Don't overwrite an existing error. This preserves the first
* error, which is the most significant. */
_cairo_status_set_error (&scaled_font->status, status);
/* Here we keep a unique mapping from *font_face/matrix/ctm/font_options=>#cairo_scaled_font_t. * *Herearethethingsthatwewanttomap: * *a)Allotherwisereferenced#cairo_scaled_font_t's *b)Somenumberofnototherwisereferenced#cairo_scaled_font_t's * *Theimplementationusesahashtablewhichcovers(a) *completely.Then,for(b)wehaveanarrayofotherwise *unreferencedfonts(holdovers)whichareexpiredin *least-recently-usedorder. * *Thecairo_scaled_font_create()codegetstotreatthislikearegular *hashtable.Allofthemagicforthelittleholdovercacheisin *cairo_scaled_font_reference()andcairo_scaled_font_destroy().
*/
/* This defines the size of the holdover array ... that is, the number *ofscaledfontswekeeparoundevenwhennototherwisereferenced
*/ #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 *recursivedeadlockwhenthescaledfontdestroyclosuregets *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(neededforuser-fonts),weneedtotakeextracarenot *endingupwithmultipleidenticalscaledfontsbeingcreated. * *Whatwedois,wecreateafakeidenticalscaledfont,andmark *itasplaceholder,lockitsmutex,andinsertthatinthefontmap *hashtable.Thismakesothercodetryingtocreateanidentical *scaledfonttojustwaitandretry. * *Thereasonwehavetocreateafakescaledfontinsteadofjustusing *scaled_fontisforlifecyclemanagement:weneedto(orrather, *othercodeneedsto)referencethescaled_fontinthehashtable. *Wecan'tdothatontheinputscaled_fontasitmaybefreedby *fontbackenduponerror.
*/
/* 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);
/* 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);
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 *globalpagecachelock.Thedestructorcancauseustorecurseand
* 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);
}
if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { /* We increment the reference count manually here, (rather *thancallingintocairo_scaled_font_reference),sincewe *mustmodifythereferencecountwhileourlockisstill
* 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 *beenfoundinfont_map->holdovers,(whichmeansthiscachingis *actuallyworking).Sonowweremoveitfromtheholdovers *array,unlesswecaughtthefontinthemiddleofdestruction.
*/ 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 *thancallingintocairo_scaled_font_reference),sincewe *mustmodifythereferencecountwhileourlockisstill
* 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.oldincarnationsoftoy-font-faceandlazilyresolved *ft-font-faces
*/
assert (scaled_font->font_face == font_face);
assert (! scaled_font->cache_frozen);
assert (! scaled_font->global_cache_frozen);
/* 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;
/* 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 *thefont_map->holdoversarrayincaseitwillgetusedagain *soon(andiswhywemustholdthelockovertheatomicopon *thereferencecount).Tomakeroomforit,wedoactually *destroytheleast-recently-usedholdover.
*/
/* If we pulled an item from the holdovers array, (while the font *maplockwasheld,ofcourse),thenthereisnowaythatanyone *elsecouldhaveacquiredareferencetoit.Sowecannow *safelycallfinionitwithoutanylockheld.Thisisdesirable *asweneverwanttocallintoanybackendfunctionwithalock
* held. */ if (lru != NULL) {
_cairo_scaled_font_fini_internal (lru);
free (lru);
}
}
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);
/* *Noadvanceneededforasinglecharacterstring.So,let'sspeedup *one-characterstringsbyskippingglyphlookup.
*/ 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;
}
if (scaled_font->backend->text_to_glyphs) {
status = scaled_font->backend->text_to_glyphs (scaled_font, x, y,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
cluster_flags); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { if (status == CAIRO_INT_STATUS_SUCCESS) { /* The checks here are crude; we only should do them in *user-fontbackend,buttheydon'thurthere.Thisstuff
* can be hard to get right. */
if (*num_glyphs < 0) {
status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); goto DONE;
} if (*num_glyphs != 0 && *glyphs == NULL) {
status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto DONE;
}
if (clusters) { if (*num_clusters < 0) {
status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); goto DONE;
} if (*num_clusters != 0 && *clusters == NULL) {
status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
goto DONE;
}
/* Don't trust the backend, validate clusters! */
status =
_cairo_validate_text_clusters (utf8, utf8_len,
*glyphs, *num_glyphs,
*clusters, *num_clusters,
*cluster_flags);
}
}
goto DONE;
}
}
if (*num_glyphs < num_chars) {
*glyphs = cairo_glyph_allocate (num_chars);
if (unlikely (*glyphs == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto DONE;
}
}
*num_glyphs = num_chars;
if (clusters) {
if (*num_clusters < num_chars) {
*clusters = cairo_text_cluster_allocate (num_chars);
if (unlikely (*clusters == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto DONE;
}
}
*num_clusters = num_chars;
}
if (num_chars > CACHING_THRESHOLD)
status = cairo_scaled_font_text_to_glyphs_internal_cached (scaled_font,
x, y,
utf8,
*glyphs,
clusters,
num_chars);
else
status = cairo_scaled_font_text_to_glyphs_internal_uncached (scaled_font,
x, y,
utf8,
*glyphs,
clusters,
num_chars);
DONE: /* error that should be logged on scaled_font happened */
_cairo_scaled_font_thaw_cache (scaled_font);
if (unlikely (status)) {
*num_glyphs = 0;
if (*glyphs != orig_glyphs) {
cairo_glyph_free (*glyphs);
*glyphs = orig_glyphs;
}
if (clusters) {
*num_clusters = 0;
if (*clusters != orig_clusters) {
cairo_text_cluster_free (*clusters);
*clusters = orig_clusters;
}
}
}
if (round_xy)
v = _cairo_fixed_from_int (_cairo_lround (glyph->x));
else
v = _cairo_fixed_from_double (glyph->x);
box.p1.x = v + scaled_glyph->bbox.p1.x;
box.p2.x = v + scaled_glyph->bbox.p2.x;
if (round_xy)
v = _cairo_fixed_from_int (_cairo_lround (glyph->y));
else
v = _cairo_fixed_from_double (glyph->y);
box.p1.y = v + scaled_glyph->bbox.p1.y;
box.p2.y = v + scaled_glyph->bbox.p2.y;
/*
* Compute a device-space bounding box for the glyphs.
*/
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_int_t *extents,
cairo_bool_t *overlap_out)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_box_t box = { { INT_MAX, INT_MAX }, { INT_MIN, INT_MIN }};
cairo_scaled_glyph_t *glyph_cache[64];
cairo_bool_t overlap = overlap_out ? FALSE : TRUE;
cairo_round_glyph_positions_t round_glyph_positions = _cairo_font_options_get_round_glyph_positions (&scaled_font->options);
int i;
if (unlikely (scaled_font->status))
return scaled_font->status;
if (num_glyphs == 1) {
if (overlap_out)
*overlap_out = FALSE;
return _cairo_scaled_font_single_glyph_device_extents (scaled_font,
glyphs,
extents);
}
_cairo_scaled_font_freeze_cache (scaled_font);
memset (glyph_cache, 0, sizeof (glyph_cache));
for (i = 0; i < num_glyphs; i++) {
cairo_scaled_glyph_t *scaled_glyph;
cairo_fixed_t x, y, x1, y1, x2, y2;
int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache);
scaled_glyph = glyph_cache[cache_index];
if (scaled_glyph == NULL ||
_cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index)
{
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[i].index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
NULL, /* foreground color */
&scaled_glyph);
if (unlikely (status))
break;
glyph_cache[cache_index] = scaled_glyph;
}
if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON)
x = _cairo_fixed_from_int (_cairo_lround (glyphs[i].x));
else
x = _cairo_fixed_from_double (glyphs[i].x);
x1 = x + scaled_glyph->bbox.p1.x;
x2 = x + scaled_glyph->bbox.p2.x;
if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON)
y = _cairo_fixed_from_int (_cairo_lround (glyphs[i].y));
else
y = _cairo_fixed_from_double (glyphs[i].y);
y1 = y + scaled_glyph->bbox.p1.y;
y2 = y + scaled_glyph->bbox.p2.y;
cairo_bool_t
_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font,
const cairo_glyph_t *glyphs,
int num_glyphs,
cairo_rectangle_int_t *extents)
{
double x0, x1, y0, y1, pad;
int i;
/* If any of the factors are suspect (i.e. the font is broken), bail */
if (scaled_font->fs_extents.max_x_advance == 0 ||
scaled_font->fs_extents.height == 0 ||
scaled_font->max_scale == 0)
{
return FALSE;
}
assert (num_glyphs);
x0 = x1 = glyphs[0].x;
y0 = y1 = glyphs[0].y;
for (i = 1; i < num_glyphs; i++) {
double g;
g = glyphs[i].x;
if (g < x0) x0 = g;
if (g > x1) x1 = g;
g = glyphs[i].y;
if (g < y0) y0 = g;
if (g > y1) y1 = g;
}
pad = MAX(scaled_font->fs_extents.max_x_advance,
scaled_font->fs_extents.height);
pad *= scaled_font->max_scale;
/* Add a single-device-unit rectangle to a path. */
static cairo_status_t
_add_unit_rectangle_to_path (cairo_path_fixed_t *path,
cairo_fixed_t x,
cairo_fixed_t y)
{
cairo_status_t status;
status = _cairo_path_fixed_move_to (path, x, y);
if (unlikely (status))
return status;
status = _cairo_path_fixed_rel_line_to (path,
_cairo_fixed_from_int (1),
_cairo_fixed_from_int (0));
if (unlikely (status))
return status;
status = _cairo_path_fixed_rel_line_to (path,
_cairo_fixed_from_int (0),
_cairo_fixed_from_int (1));
if (unlikely (status))
return status;
status = _cairo_path_fixed_rel_line_to (path,
_cairo_fixed_from_int (-1),
_cairo_fixed_from_int (0));
if (unlikely (status))
return status;
return _cairo_path_fixed_close_path (path);
}
/**
* _trace_mask_to_path:
* @bitmap: An alpha mask (either %CAIRO_FORMAT_A1 or %CAIRO_FORMAT_A8)
* @path: An initialized path to hold the result
*
* Given a mask surface, (an alpha image), fill out the provided path
* so that when filled it would result in something that approximates
* the mask.
*
* Note: The current tracing code here is extremely primitive. It
* operates only on an A1 surface, (converting an A8 surface to A1 if
* necessary), and performs the tracing by drawing a little square
* around each pixel that is on in the mask. We do not pretend that
* this is a high-quality result. But we are leaving it up to someone
* who cares enough about getting a better result to implement
* something more sophisticated.
**/
static cairo_status_t
_trace_mask_to_path (cairo_image_surface_t *mask,
cairo_path_fixed_t *path,
double tx, double ty)
{
const uint8_t *row;
int rows, cols, bytes_per_row;
int x, y, bit;
double xoff, yoff;
cairo_fixed_t x0, y0;
cairo_fixed_t px, py;
cairo_status_t status;
mask = _cairo_image_surface_coerce_to_format (mask, CAIRO_FORMAT_A1);
status = mask->base.status;
if (unlikely (status))
return status;
byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte);
for (bit = 1 << 7; bit && x < mask->width; bit >>= 1, x++) {
if (byte & bit) {
px = _cairo_fixed_from_int (x);
status = _add_unit_rectangle_to_path (path,
px + x0,
py + y0);
if (unlikely (status))
goto BAIL;
}
}
}
}
BAIL:
cairo_surface_destroy (&mask->base);
return status;
}
cairo_status_t
_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
const cairo_glyph_t *glyphs,
int num_glyphs,
cairo_path_fixed_t *path)
{
cairo_int_status_t status;
int i;
status = scaled_font->status;
if (unlikely (status))
return status;
_cairo_scaled_font_freeze_cache (scaled_font);
for (i = 0; i < num_glyphs; i++) {
cairo_scaled_glyph_t *scaled_glyph;
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[i].index,
CAIRO_SCALED_GLYPH_INFO_PATH,
NULL, /* foreground color */
&scaled_glyph);
if (status == CAIRO_INT_STATUS_SUCCESS) {
status = _cairo_path_fixed_append (path,
scaled_glyph->path,
_cairo_fixed_from_double (glyphs[i].x),
_cairo_fixed_from_double (glyphs[i].y));
} else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
/* If the font is incapable of providing a path, then we'll
* have to trace our own from a surface.
*/
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[i].index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
NULL, /* foreground color */
&scaled_glyph);
if (unlikely (status))
goto BAIL;
status = _trace_mask_to_path (scaled_glyph->surface, path,
glyphs[i].x, glyphs[i].y);
}
if (unlikely (status))
goto BAIL;
}
BAIL:
_cairo_scaled_font_thaw_cache (scaled_font);
/**
* _cairo_scaled_glyph_set_metrics:
* @scaled_glyph: a #cairo_scaled_glyph_t
* @scaled_font: a #cairo_scaled_font_t
* @fs_metrics: a #cairo_text_extents_t in font space
*
* _cairo_scaled_glyph_set_metrics() stores user space metrics
* for the specified glyph given font space metrics. It is
* called by the font backend when initializing a glyph with
* %CAIRO_SCALED_GLYPH_INFO_METRICS.
**/
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_bool_t first = TRUE;
double hm, wm;
double min_user_x = 0.0, max_user_x = 0.0, min_user_y = 0.0, max_user_y = 0.0;
double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0;
double device_x_advance, device_y_advance;
/* Transform this corner to user space */
x = fs_metrics->x_bearing + fs_metrics->width * wm;
y = fs_metrics->y_bearing + fs_metrics->height * hm;
cairo_matrix_transform_point (&scaled_font->font_matrix,
&x, &y);
if (first) {
min_user_x = max_user_x = x;
min_user_y = max_user_y = y;
} else {
if (x < min_user_x) min_user_x = x;
if (x > max_user_x) max_user_x = x;
if (y < min_user_y) min_user_y = y;
if (y > max_user_y) max_user_y = y;
}
/* Transform this corner to device space from glyph origin */
x = fs_metrics->x_bearing + fs_metrics->width * wm;
y = fs_metrics->y_bearing + fs_metrics->height * hm;
cairo_matrix_transform_distance (&scaled_font->scale,
&x, &y);
/**
* _cairo_scaled_glyph_set_recording_surface:
* @scaled_glyph: a #cairo_scaled_glyph_t
* @scaled_font: a #cairo_scaled_font_t
* @recording_surface: The recording surface
* @foreground_color: The foreground color that was used to record the
* glyph, or NULL if foreground color not required.
*
* Sets the surface that was used to record the glyph.
**/
void
_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_font_t *scaled_font,
cairo_surface_t *recording_surface,
const cairo_color_t * foreground_color)
{
if (scaled_glyph->recording_surface != NULL) {
cairo_surface_finish (scaled_glyph->recording_surface);
cairo_surface_destroy (scaled_glyph->recording_surface);
}
/**
* _cairo_scaled_glyph_set_color_surface:
* @scaled_glyph: a #cairo_scaled_glyph_t
* @scaled_font: a #cairo_scaled_font_t
* @surface: The image surface
* @foreground_marker_color: The foreground color that was used to
* substitute the foreground_marker, or NULL if foreground_marker not
* used when rendering the surface color.
*
* Sets the color surface of the glyph.
**/
void
_cairo_scaled_glyph_set_color_surface (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_font_t *scaled_font,
cairo_image_surface_t *surface,
const cairo_color_t *foreground_marker_color)
{
if (scaled_glyph->color_surface != NULL)
cairo_surface_destroy (&scaled_glyph->color_surface->base);
/* _cairo_hash_table_random_entry () predicate. To avoid race conditions,
* the font is locked when tested. The font is unlocked in
* _cairo_scaled_glyph_page_pluck. */
static cairo_bool_t
_cairo_scaled_glyph_page_can_remove (const void *closure)
{
const cairo_scaled_glyph_page_t *page = closure;
cairo_scaled_font_t *scaled_font;
scaled_font = page->scaled_font;
if (!CAIRO_MUTEX_TRY_LOCK (scaled_font->mutex))
return FALSE;
if (scaled_font->cache_frozen != 0) {
CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
return FALSE;
}
/* only the first page in the list may contain available slots */
if (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
page = cairo_list_last_entry (&scaled_font->glyph_pages,
cairo_scaled_glyph_page_t,
link);
if (page->num_glyphs < CAIRO_SCALED_GLYPH_PAGE_SIZE) {
*scaled_glyph = &page->glyphs[page->num_glyphs++];
return CAIRO_STATUS_SUCCESS;
}
}
/**
* _cairo_scaled_glyph_lookup:
* @scaled_font: a #cairo_scaled_font_t
* @index: the glyph to create
* @info: a #cairo_scaled_glyph_info_t marking which portions of
* the glyph should be filled in.
* @foreground_color - foreground color to use when rendering color
* fonts. Use NULL if not requesting
* CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE or
* CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE, or foreground color is
* unknown.
* @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph
* is returned.
*
* If the desired info is not available, (for example, when trying to
* get INFO_PATH with a bitmapped font), this function will return
* %CAIRO_INT_STATUS_UNSUPPORTED.
*
* Note: This function must be called with the scaled font frozen, and it must
* remain frozen for as long as the @scaled_glyph_ret is alive. (If the scaled
* font was not frozen, then there is no guarantee that the glyph would not be
* evicted before you tried to access it.) See
* _cairo_scaled_font_freeze_cache() and _cairo_scaled_font_thaw_cache().
*
* Returns: a glyph with the requested portions filled in. Glyph
* lookup is cached and glyph will be automatically freed along
* with the scaled_font so no explicit free is required.
* @info can be one or more of:
* %CAIRO_SCALED_GLYPH_INFO_METRICS - glyph metrics and bounding box
* %CAIRO_SCALED_GLYPH_INFO_SURFACE - surface holding glyph image
* %CAIRO_SCALED_GLYPH_INFO_PATH - path holding glyph outline in device space
* %CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE - surface holding recording of glyph
* %CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE - surface holding color glyph image
**/
cairo_int_status_t
_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
unsigned long index,
cairo_scaled_glyph_info_t info,
const cairo_color_t *foreground_color,
cairo_scaled_glyph_t **scaled_glyph_ret)
{
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
cairo_scaled_glyph_t *scaled_glyph;
cairo_scaled_glyph_info_t need_info;
cairo_hash_entry_t key;
*scaled_glyph_ret = NULL;
if (unlikely (scaled_font->status))
return scaled_font->status;
/* ask backend to initialize metrics and shape fields */
status =
scaled_font->backend->scaled_glyph_init (scaled_font,
scaled_glyph,
info | CAIRO_SCALED_GLYPH_INFO_METRICS,
foreground_color);
if (unlikely (status)) {
_cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph);
goto err;
}
status = _cairo_hash_table_insert (scaled_font->glyphs,
&scaled_glyph->hash_entry);
if (unlikely (status)) {
_cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph);
goto err;
}
}
/*
* Check and see if the glyph, as provided,
* already has the requested data and amend it if not
*/
need_info = info & ~scaled_glyph->has_info;
/* If this is not a color glyph, don't try loading the color surface again. */
if ((need_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) &&
scaled_glyph->color_glyph_set && !scaled_glyph->color_glyph)
return CAIRO_INT_STATUS_UNSUPPORTED;
/* If requesting a color surface or recording for a glyph that has
* used the foreground color to render the recording, and the
* foreground color has changed, request a new recording. */
if ((info & (CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE | CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) &&
scaled_glyph->recording_uses_foreground_color &&
!_cairo_color_equal (foreground_color, &scaled_glyph->foreground_color))
{
need_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE;
}
/* If requesting a color surface for a glyph that has
* used the foreground color to render the color_surface, and the
* foreground color has changed, request a new image. */
if (info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE &&
(scaled_glyph->recording_uses_foreground_marker || scaled_glyph->recording_uses_foreground_color) &&
!_cairo_color_equal (foreground_color, &scaled_glyph->foreground_color))
{
need_info |= CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE;
}
if (need_info) {
status = scaled_font->backend->scaled_glyph_init (scaled_font,
scaled_glyph,
need_info,
foreground_color);
if (unlikely (status))
goto err;
/* Don't trust the scaled_glyph_init() return value, the font
* backend may not even know about some of the info. For example,
* no backend other than the user-fonts knows about recording-surface
* glyph info. */
if (info & ~scaled_glyph->has_info)
return CAIRO_INT_STATUS_UNSUPPORTED;
}
err:
/* It's not an error for the backend to not support the info we want. */
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
status = _cairo_scaled_font_set_error (scaled_font, status);
return status;
}
/**
* cairo_scaled_font_get_font_face:
* @scaled_font: a #cairo_scaled_font_t
*
* Gets the font face that this scaled font uses. This might be the
* font face passed to cairo_scaled_font_create(), but this does not
* hold true for all possible cases.
*
* Return value: The #cairo_font_face_t with which @scaled_font was
* created. This object is owned by cairo. To keep a reference to it,
* you must call cairo_scaled_font_reference().
*
* Since: 1.2
**/
cairo_font_face_t *
cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font)
{
if (scaled_font->status)
return (cairo_font_face_t*) &_cairo_font_face_nil;
if (scaled_font->original_font_face != NULL)
return scaled_font->original_font_face;
return scaled_font->font_face;
}
/**
* cairo_scaled_font_get_font_matrix:
* @scaled_font: a #cairo_scaled_font_t
* @font_matrix: return value for the matrix
*
* Stores the font matrix with which @scaled_font was created into
* @matrix.
*
* Since: 1.2
**/
void
cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font,
cairo_matrix_t *font_matrix)
{
if (scaled_font->status) {
cairo_matrix_init_identity (font_matrix);
return;
}
*font_matrix = scaled_font->font_matrix;
}
/**
* cairo_scaled_font_get_ctm:
* @scaled_font: a #cairo_scaled_font_t
* @ctm: return value for the CTM
*
* Stores the CTM with which @scaled_font was created into @ctm.
* Note that the translation offsets (x0, y0) of the CTM are ignored
* by cairo_scaled_font_create(). So, the matrix this
* function returns always has 0,0 as x0,y0.
*
* Since: 1.2
**/
void
cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font,
cairo_matrix_t *ctm)
{
if (scaled_font->status) {
cairo_matrix_init_identity (ctm);
return;
}
*ctm = scaled_font->ctm;
}
/**
* cairo_scaled_font_get_scale_matrix:
* @scaled_font: a #cairo_scaled_font_t
* @scale_matrix: return value for the matrix
*
* Stores the scale matrix of @scaled_font into @matrix.
* The scale matrix is product of the font matrix and the ctm
* associated with the scaled font, and hence is the matrix mapping from
* font space to device space.
*
* Since: 1.8
**/
void
cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font,
cairo_matrix_t *scale_matrix)
{
if (scaled_font->status) {
cairo_matrix_init_identity (scale_matrix);
return;
}
*scale_matrix = scaled_font->scale;
}
/**
* cairo_scaled_font_get_font_options:
* @scaled_font: a #cairo_scaled_font_t
* @options: return value for the font options
*
* Stores the font options with which @scaled_font was created into
* @options.
*
* Since: 1.2
**/
void
cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font,
cairo_font_options_t *options)
{
if (cairo_font_options_status (options))
return;
if (scaled_font->status) {
_cairo_font_options_init_default (options);
return;
}
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.