diff options
Diffstat (limited to 'pango/pangocairo-font.c')
-rw-r--r-- | pango/pangocairo-font.c | 506 |
1 files changed, 439 insertions, 67 deletions
diff --git a/pango/pangocairo-font.c b/pango/pangocairo-font.c index 2595c0ef..5f439998 100644 --- a/pango/pangocairo-font.c +++ b/pango/pangocairo-font.c @@ -29,6 +29,12 @@ PangoCairoWarningHistory _pango_cairo_warning_history = { FALSE }; +#define PANGO_CAIRO_FONT_PRIVATE(font) \ + ((PangoCairoFontPrivate *) \ + (font == NULL ? NULL : \ + G_STRUCT_MEMBER_P (font, \ + PANGO_CAIRO_FONT_GET_IFACE(PANGO_CAIRO_FONT(font))->cf_priv_offset))) + GType pango_cairo_font_get_type (void) { @@ -60,45 +66,86 @@ pango_cairo_font_get_type (void) return cairo_font_type; } -/** - * _pango_cairo_font_install: - * @font: a #PangoCairoFont - * @cr: a #cairo_t - * - * Makes @font the current font for rendering in the specified - * Cairo context. - **/ -gboolean -_pango_cairo_font_install (PangoCairoFont *font, - cairo_t *cr) +static PangoCairoFontPrivateScaledFontData * +_pango_cairo_font_private_scaled_font_data_create (void) +{ + return g_slice_new (PangoCairoFontPrivateScaledFontData); +} + +static void +_pango_cairo_font_private_scaled_font_data_destroy (PangoCairoFontPrivateScaledFontData *data) { - if (G_UNLIKELY (!PANGO_IS_CAIRO_FONT (font))) + cairo_font_options_destroy (data->options); + g_slice_free (PangoCairoFontPrivateScaledFontData, data); +} + +cairo_scaled_font_t * +_pango_cairo_font_private_get_scaled_font (PangoCairoFontPrivate *cf_priv) +{ + cairo_font_face_t *font_face; + + if (G_LIKELY (cf_priv->scaled_font)) + return cf_priv->scaled_font; + + /* need to create it */ + + if (G_UNLIKELY (cf_priv->data == NULL)) + { + /* we have tried to create and failed before */ + return NULL; + } + + font_face = (* PANGO_CAIRO_FONT_GET_IFACE (cf_priv->cfont)->create_font_face) (cf_priv->cfont); + if (G_UNLIKELY (font_face == NULL)) + goto done; + + cf_priv->scaled_font = cairo_scaled_font_create (font_face, + &cf_priv->data->font_matrix, + &cf_priv->data->ctm, + cf_priv->data->options); + + cairo_font_face_destroy (font_face); + +done: + _pango_cairo_font_private_scaled_font_data_destroy (cf_priv->data); + cf_priv->data = NULL; + + if (G_UNLIKELY (cf_priv->scaled_font == NULL || cairo_scaled_font_status (cf_priv->scaled_font) != CAIRO_STATUS_SUCCESS)) { - if (!_pango_cairo_warning_history.font_install) + PangoFont *font = PANGO_FONT (cf_priv->cfont); + static GQuark warned_quark = 0; + if (!warned_quark) + warned_quark = g_quark_from_static_string ("pangocairo-scaledfont-warned"); + + if (!g_object_get_qdata (G_OBJECT (font), warned_quark)) { - _pango_cairo_warning_history.font_install = TRUE; - g_warning ("_pango_cairo_font_install called with bad font, expect ugly output"); - cairo_set_font_face (cr, NULL); + PangoFontDescription *desc; + char *s; + + desc = pango_font_describe (font); + s = pango_font_description_to_string (desc); + pango_font_description_free (desc); + + g_warning ("failed to create cairo %s, expect ugly output. the offending font is '%s'", + font_face ? "scaled font" : "font face", + s); + + g_free (s); + + g_object_set_qdata_full (G_OBJECT (font), warned_quark, + GINT_TO_POINTER (1), NULL); } - return FALSE; } - return (* PANGO_CAIRO_FONT_GET_IFACE (font)->install) (font, cr); + return cf_priv->scaled_font; } -cairo_font_face_t * -_pango_cairo_font_get_font_face (PangoCairoFont *font) +static cairo_scaled_font_t * +_pango_cairo_font_get_scaled_font (PangoFont *cfont) { - g_return_val_if_fail (PANGO_IS_CAIRO_FONT (font), NULL); - /* this function will be removed. don't bother with warning history here */ - - return (* PANGO_CAIRO_FONT_GET_IFACE (font)->get_font_face) (font); -} + PangoCairoFontPrivate *cf_priv; -cairo_scaled_font_t * -_pango_cairo_font_get_scaled_font (PangoCairoFont *font) -{ - if (G_UNLIKELY (!PANGO_IS_CAIRO_FONT (font))) + if (G_UNLIKELY (!PANGO_IS_CAIRO_FONT (cfont))) { if (!_pango_cairo_warning_history.font_get_scaled_font) { @@ -108,27 +155,126 @@ _pango_cairo_font_get_scaled_font (PangoCairoFont *font) return NULL; } - return (* PANGO_CAIRO_FONT_GET_IFACE (font)->get_scaled_font) (font); + cf_priv = PANGO_CAIRO_FONT_PRIVATE (cfont); + + return _pango_cairo_font_private_get_scaled_font (cf_priv); } -static void -_pango_cairo_hex_box_info_destroy (PangoCairoHexBoxInfo *hbi) +/** + * _pango_cairo_font_install: + * @font: a #PangoCairoFont + * @cr: a #cairo_t + * + * Makes @font the current font for rendering in the specified + * Cairo context. + * + * Return value: %TRUE if font was installed successfully, %FALSE otherwise. + **/ +gboolean +_pango_cairo_font_install (PangoFont *font, + cairo_t *cr) { - if (hbi) + cairo_scaled_font_t *scaled_font = _pango_cairo_font_get_scaled_font (font); + + if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS)) + return FALSE; + + cairo_set_scaled_font (cr, scaled_font); + + return TRUE; +} + + +typedef struct _PangoCairoFontMetricsInfo +{ + const char *sample_str; + PangoFontMetrics *metrics; +} PangoCairoFontMetricsInfo; + +PangoFontMetrics * +_pango_cairo_font_get_metrics (PangoFont *font, + PangoLanguage *language) +{ + PangoCairoFont *cfont = (PangoCairoFont *) font; + PangoCairoFontPrivate *cf_priv = PANGO_CAIRO_FONT_PRIVATE (font); + PangoCairoFontMetricsInfo *info = NULL; /* Quiet gcc */ + GSList *tmp_list; + + const char *sample_str = pango_language_get_sample_string (language); + + tmp_list = cf_priv->metrics_by_lang; + while (tmp_list) { - g_object_unref (hbi->font); - g_slice_free (PangoCairoHexBoxInfo, hbi); + info = tmp_list->data; + + if (info->sample_str == sample_str) /* We _don't_ need strcmp */ + break; + + tmp_list = tmp_list->next; } + + if (!tmp_list) + { + PangoContext *context; + cairo_font_options_t *font_options; + int height, shift; + + info = g_slice_new0 (PangoCairoFontMetricsInfo); + + cf_priv->metrics_by_lang = g_slist_prepend (cf_priv->metrics_by_lang, info); + + info->sample_str = sample_str; + + context = pango_cairo_font_map_create_context ((PangoCairoFontMap *) pango_font_get_font_map (font)); + pango_context_set_language (context, language); + font_options = cairo_font_options_create (); + cairo_scaled_font_get_font_options (_pango_cairo_font_private_get_scaled_font (cf_priv), font_options); + pango_cairo_context_set_font_options (context, font_options); + cairo_font_options_destroy (font_options); + + info->metrics = (* PANGO_CAIRO_FONT_GET_IFACE (font)->create_metrics_for_context) (cfont, context); + + /* We may actually reuse ascent/descent we got from cairo here. that's + * in cf_priv->font_extents. + */ + height = info->metrics->ascent + info->metrics->descent; + switch (cf_priv->gravity) + { + default: + case PANGO_GRAVITY_AUTO: + case PANGO_GRAVITY_SOUTH: + break; + case PANGO_GRAVITY_NORTH: + info->metrics->ascent = info->metrics->descent; + break; + case PANGO_GRAVITY_EAST: + case PANGO_GRAVITY_WEST: + { + int ascent = height / 2; + if (cf_priv->is_hinted) + ascent = PANGO_UNITS_ROUND (ascent); + info->metrics->ascent = ascent; + } + } + shift = (height - info->metrics->ascent) - info->metrics->descent; + info->metrics->descent += shift; + info->metrics->underline_position -= shift; + info->metrics->strikethrough_position -= shift; + info->metrics->ascent = height - info->metrics->descent; + + g_object_unref (context); + } + + return pango_font_metrics_ref (info->metrics); } -PangoCairoHexBoxInfo * -_pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont) +static PangoCairoFontHexBoxInfo * +_pango_cairo_font_private_get_hex_box_info (PangoCairoFontPrivate *cf_priv) { static const char hexdigits[] = "0123456789ABCDEF"; char c[2] = {0, 0}; PangoFont *mini_font; - PangoCairoFont *mini_cfont; - PangoCairoHexBoxInfo *hbi; + PangoCairoFontHexBoxInfo *hbi; /* for metrics hinting */ double scale_x = 1., scale_x_inv = 1., scale_y = 1., scale_y_inv = 1.; @@ -148,24 +294,20 @@ _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont) cairo_matrix_t cairo_ctm; PangoGravity gravity; - if (!cfont) + if (!cf_priv) return NULL; - hbi = (PangoCairoHexBoxInfo *) g_object_get_data (G_OBJECT (cfont), "hex_box_info"); - if (hbi) - return hbi; + if (cf_priv->hbi) + return cf_priv->hbi; - scaled_font = _pango_cairo_font_get_scaled_font (cfont); - if (!scaled_font) - { - g_object_set_data_full (G_OBJECT (cfont), "hex_box_info", NULL, NULL); - return NULL; - } + scaled_font = _pango_cairo_font_private_get_scaled_font (cf_priv); + if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS)) + return NULL; + + is_hinted = cf_priv->is_hinted; font_options = cairo_font_options_create (); - cairo_scaled_font_get_font_options (scaled_font, font_options); - is_hinted = (cairo_font_options_get_hint_metrics(font_options) != CAIRO_HINT_METRICS_OFF); - desc = pango_font_describe_with_absolute_size ((PangoFont *)cfont); + desc = pango_font_describe_with_absolute_size ((PangoFont *)cf_priv->cfont); size = pango_font_description_get_size (desc) / (1.*PANGO_SCALE); gravity = pango_font_description_get_gravity (desc); @@ -208,7 +350,7 @@ _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont) PangoContext *context; PangoFontMap *fontmap; - fontmap = pango_font_get_font_map ((PangoFont *)cfont); + fontmap = pango_font_get_font_map ((PangoFont *)cf_priv->cfont); /* we inherit most font properties for the mini font. just * change family and size. means, you get bold hex digits @@ -233,8 +375,7 @@ _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont) } } - pango_font_description_set_absolute_size (desc, mini_size * PANGO_SCALE); - + pango_font_description_set_absolute_size (desc, pango_units_from_double (mini_size)); /* load mini_font */ @@ -251,8 +392,7 @@ _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont) cairo_font_options_destroy (font_options); - mini_cfont = (PangoCairoFont *) mini_font; - scaled_mini_font = _pango_cairo_font_get_scaled_font (mini_cfont); + scaled_mini_font = _pango_cairo_font_get_scaled_font (mini_font); for (i = 0 ; i < 16 ; i++) { @@ -274,8 +414,8 @@ _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont) pad = (font_extents.ascent + font_extents.descent) / 43; pad = MIN (pad, mini_size); - hbi = g_slice_new (PangoCairoHexBoxInfo); - hbi->font = mini_font; + hbi = g_slice_new (PangoCairoFontHexBoxInfo); + hbi->font = (PangoCairoFont *) mini_font; hbi->rows = rows; hbi->digit_width = width; @@ -314,21 +454,114 @@ _pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont) hbi->box_descent = HINT_Y (hbi->box_descent); } - g_object_set_data_full (G_OBJECT (cfont), "hex_box_info", hbi, (GDestroyNotify)_pango_cairo_hex_box_info_destroy); - return hbi; } +static void +_pango_cairo_font_hex_box_info_destroy (PangoCairoFontHexBoxInfo *hbi) +{ + if (hbi) + { + g_object_unref (hbi->font); + g_slice_free (PangoCairoFontHexBoxInfo, hbi); + } +} + +PangoCairoFontHexBoxInfo * +_pango_cairo_font_get_hex_box_info (PangoCairoFont *cfont) +{ + PangoCairoFontPrivate *cf_priv = PANGO_CAIRO_FONT_PRIVATE (cfont); + + return _pango_cairo_font_private_get_hex_box_info (cf_priv); +} + +void +_pango_cairo_font_private_initialize (PangoCairoFontPrivate *cf_priv, + PangoCairoFont *cfont, + PangoContext *context, + const PangoFontDescription *desc, + const cairo_matrix_t *font_matrix) +{ + const cairo_font_options_t *font_options; + cairo_matrix_t gravity_matrix; + const PangoMatrix *pango_ctm; + + cf_priv->cfont = cfont; + cf_priv->gravity = pango_font_description_get_gravity (desc); + + cf_priv->data = _pango_cairo_font_private_scaled_font_data_create (); + + /* first apply gravity rotation, then font_matrix, such that + * vertical italic text comes out "correct". we don't do anything + * like baseline adjustment etc though. should be specially + * handled when we support italic correction. */ + cairo_matrix_init_rotate(&gravity_matrix, + pango_gravity_to_rotation (cf_priv->gravity)); + cairo_matrix_multiply (&cf_priv->data->font_matrix, + font_matrix, + &gravity_matrix); + + pango_ctm = pango_context_get_matrix (context); + if (pango_ctm) + cairo_matrix_init (&cf_priv->data->ctm, + pango_ctm->xx, + pango_ctm->yx, + pango_ctm->xy, + pango_ctm->yy, + 0., 0.); + else + cairo_matrix_init_identity (&cf_priv->data->ctm); + + font_options = _pango_cairo_context_get_merged_font_options (context); + cf_priv->data->options = cairo_font_options_copy (font_options); + cf_priv->is_hinted = cairo_font_options_get_hint_metrics (font_options) != CAIRO_HINT_METRICS_OFF; + + cf_priv->scaled_font = NULL; + cf_priv->hbi = NULL; + cf_priv->glyph_extents_cache = NULL; + cf_priv->metrics_by_lang = NULL; +} + +static void +free_metrics_info (PangoCairoFontMetricsInfo *info) +{ + pango_font_metrics_unref (info->metrics); + g_slice_free (PangoCairoFontMetricsInfo, info); +} + void -_pango_cairo_get_glyph_extents_missing (PangoCairoFont *cfont, - PangoGlyph glyph, - PangoRectangle *ink_rect, - PangoRectangle *logical_rect) +_pango_cairo_font_private_finalize (PangoCairoFontPrivate *cf_priv) +{ + _pango_cairo_font_private_scaled_font_data_destroy (cf_priv->data); + + if (cf_priv->scaled_font) + cairo_scaled_font_destroy (cf_priv->scaled_font); + + _pango_cairo_font_hex_box_info_destroy (cf_priv->hbi); + + if (cf_priv->glyph_extents_cache) + g_free (cf_priv->glyph_extents_cache); + + g_slist_foreach (cf_priv->metrics_by_lang, (GFunc)free_metrics_info, NULL); + g_slist_free (cf_priv->metrics_by_lang); +} + +gboolean +_pango_cairo_font_private_is_metrics_hinted (PangoCairoFontPrivate *cf_priv) +{ + return cf_priv->is_hinted; +} + +static void +_pango_cairo_font_private_get_glyph_extents_missing (PangoCairoFontPrivate *cf_priv, + PangoGlyph glyph, + PangoRectangle *ink_rect, + PangoRectangle *logical_rect) { - PangoCairoHexBoxInfo *hbi; + PangoCairoFontHexBoxInfo *hbi; gint rows, cols; - hbi = _pango_cairo_font_get_hex_box_info (cfont); + hbi = _pango_cairo_font_private_get_hex_box_info (cf_priv); if (!hbi) { pango_font_get_glyph_extents (NULL, glyph, ink_rect, logical_rect); @@ -355,3 +588,142 @@ _pango_cairo_get_glyph_extents_missing (PangoCairoFont *cfont, } } +#define GLYPH_CACHE_NUM_ENTRIES 256 /* should be power of two */ +#define GLYPH_CACHE_MASK (GLYPH_CACHE_NUM_ENTRIES - 1) +/* An entry in the fixed-size cache for the glyph->extents mapping. + * The cache is indexed by the lower N bits of the glyph (see + * GLYPH_CACHE_NUM_ENTRIES). For scripts with few glyphs, + * this should provide pretty much instant lookups. + */ +struct _PangoCairoFontGlyphExtentsCacheEntry +{ + PangoGlyph glyph; + int width; + PangoRectangle ink_rect; +}; + +static gboolean +_pango_cairo_font_private_glyph_extents_cache_init (PangoCairoFontPrivate *cf_priv) +{ + cairo_scaled_font_t *scaled_font = _pango_cairo_font_private_get_scaled_font (cf_priv); + cairo_font_extents_t font_extents; + + if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS)) + return FALSE; + + cairo_scaled_font_extents (scaled_font, &font_extents); + + cf_priv->font_extents.x = 0; + cf_priv->font_extents.width = 0; + cf_priv->font_extents.height = pango_units_from_double (font_extents.ascent + font_extents.descent); + switch (cf_priv->gravity) + { + default: + case PANGO_GRAVITY_AUTO: + case PANGO_GRAVITY_SOUTH: + cf_priv->font_extents.y = - pango_units_from_double (font_extents.ascent); + break; + case PANGO_GRAVITY_NORTH: + cf_priv->font_extents.y = - pango_units_from_double (font_extents.descent); + break; + case PANGO_GRAVITY_EAST: + case PANGO_GRAVITY_WEST: + { + int ascent = pango_units_from_double (font_extents.ascent + font_extents.descent) / 2; + if (cf_priv->is_hinted) + ascent = PANGO_UNITS_ROUND (ascent); + cf_priv->font_extents.y = - ascent; + } + } + + cf_priv->glyph_extents_cache = g_new0 (PangoCairoFontGlyphExtentsCacheEntry, GLYPH_CACHE_NUM_ENTRIES); + /* Make sure all cache entries are invalid initially */ + cf_priv->glyph_extents_cache[0].glyph = 1; /* glyph 1 cannot happen in bucket 0 */ + + return TRUE; +} + +/* Fills in the glyph extents cache entry + */ +static void +compute_glyph_extents (PangoCairoFontPrivate *cf_priv, + PangoGlyph glyph, + PangoCairoFontGlyphExtentsCacheEntry *entry) +{ + cairo_text_extents_t extents; + cairo_glyph_t cairo_glyph; + + cairo_glyph.index = glyph; + cairo_glyph.x = 0; + cairo_glyph.y = 0; + + cairo_scaled_font_glyph_extents (_pango_cairo_font_private_get_scaled_font (cf_priv), + &cairo_glyph, 1, &extents); + + entry->glyph = glyph; + entry->width = pango_units_from_double (extents.x_advance); + entry->ink_rect.x = pango_units_from_double (extents.x_bearing); + entry->ink_rect.y = pango_units_from_double (extents.y_bearing); + entry->ink_rect.width = pango_units_from_double (extents.width); + entry->ink_rect.height = pango_units_from_double (extents.height); +} + +static PangoCairoFontGlyphExtentsCacheEntry * +_pango_cairo_font_private_get_glyph_extents_cache_entry (PangoCairoFontPrivate *cf_priv, + PangoGlyph glyph) +{ + PangoCairoFontGlyphExtentsCacheEntry *entry; + guint idx; + + idx = glyph & GLYPH_CACHE_MASK; + entry = cf_priv->glyph_extents_cache + idx; + + if (entry->glyph != glyph) + { + compute_glyph_extents (cf_priv, glyph, entry); + } + + return entry; +} + +void +_pango_cairo_font_private_get_glyph_extents (PangoCairoFontPrivate *cf_priv, + PangoGlyph glyph, + PangoRectangle *ink_rect, + PangoRectangle *logical_rect) +{ + PangoCairoFontGlyphExtentsCacheEntry *entry; + + if (!cf_priv || + (cf_priv->glyph_extents_cache == NULL && + !_pango_cairo_font_private_glyph_extents_cache_init (cf_priv))) + { + /* Get generic unknown-glyph extents. */ + pango_font_get_glyph_extents (NULL, glyph, ink_rect, logical_rect); + return; + } + + if (glyph == PANGO_GLYPH_EMPTY) + { + if (ink_rect) + ink_rect->x = ink_rect->y = ink_rect->width = ink_rect->height = 0; + if (logical_rect) + *logical_rect = cf_priv->font_extents; + return; + } + else if (glyph & PANGO_GLYPH_UNKNOWN_FLAG) + { + _pango_cairo_font_private_get_glyph_extents_missing(cf_priv, glyph, ink_rect, logical_rect); + return; + } + + entry = _pango_cairo_font_private_get_glyph_extents_cache_entry (cf_priv, glyph); + + if (ink_rect) + *ink_rect = entry->ink_rect; + if (logical_rect) + { + *logical_rect = cf_priv->font_extents; + logical_rect->width = entry->width; + } +} |