[vte] draw: Move FontInfo to its own file
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vte] draw: Move FontInfo to its own file
- Date: Mon, 1 Jun 2020 20:50:57 +0000 (UTC)
commit 42ea29e93c08266c3b3fd30cc4a838f3970f9892
Author: Christian Persch <chpe src gnome org>
Date: Mon Jun 1 22:48:43 2020 +0200
draw: Move FontInfo to its own file
src/fonts-pangocairo.cc | 474 ++++++++++++++++++++++++++++++++
src/fonts-pangocairo.hh | 286 ++++++++++++++++++++
src/fwd.hh | 7 +
src/meson.build | 2 +
src/vtedraw.cc | 700 +-----------------------------------------------
5 files changed, 770 insertions(+), 699 deletions(-)
---
diff --git a/src/fonts-pangocairo.cc b/src/fonts-pangocairo.cc
new file mode 100644
index 00000000..fabecf66
--- /dev/null
+++ b/src/fonts-pangocairo.cc
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2003,2008 Red Hat, Inc.
+ * Copyright © 2019, 2020 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "fonts-pangocairo.hh"
+
+#include "debug.h"
+#include "vtedefines.hh"
+
+/* Have a space between letters to make sure ligatures aren't used when caching the glyphs: bug 793391. */
+#define VTE_DRAW_SINGLE_WIDE_CHARACTERS \
+ " ! \" # $ % & ' ( ) * + , - . / " \
+ "0 1 2 3 4 5 6 7 8 9 " \
+ ": ; < = > ? @ " \
+ "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z " \
+ "[ \\ ] ^ _ ` " \
+ "a b c d e f g h i j k l m n o p q r s t u v w x y z " \
+ "{ | } ~ " \
+ ""
+
+static inline bool
+_vte_double_equal(double a,
+ double b)
+{
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+ return a == b;
+#pragma GCC diagnostic pop
+}
+
+#define FONT_CACHE_TIMEOUT (30) /* seconds */
+
+namespace vte {
+namespace view {
+
+FontInfo::UnistrInfo*
+FontInfo::find_unistr_info(vteunistr c)
+{
+ if (G_LIKELY (c < G_N_ELEMENTS(m_ascii_unistr_info)))
+ return &m_ascii_unistr_info[c];
+
+ if (G_UNLIKELY (m_other_unistr_info == nullptr))
+ m_other_unistr_info = g_hash_table_new_full(nullptr, nullptr, nullptr,
(GDestroyNotify)unistr_info_destroy);
+
+ auto uinfo = reinterpret_cast<UnistrInfo*>(g_hash_table_lookup(m_other_unistr_info,
GINT_TO_POINTER(c)));
+ if (G_LIKELY (uinfo))
+ return uinfo;
+
+ uinfo = new UnistrInfo{};
+ g_hash_table_insert(m_other_unistr_info, GINT_TO_POINTER (c), uinfo);
+ return uinfo;
+}
+
+void
+FontInfo::cache_ascii()
+{
+ PangoLayoutLine *line;
+ PangoGlyphItemIter iter;
+ PangoGlyphItem *glyph_item;
+ PangoGlyphString *glyph_string;
+ PangoFont *pango_font;
+ cairo_scaled_font_t *scaled_font;
+ const char *text;
+ gboolean more;
+ PangoLanguage *language;
+ gboolean latin_uses_default_language;
+
+ /* We have m_layout holding most ASCII characters. We want to
+ * cache as much info as we can about the ASCII letters so we don't
+ * have to look them up again later */
+
+ /* Don't cache if unknown glyphs found in layout */
+ if (pango_layout_get_unknown_glyphs_count(m_layout.get()) != 0)
+ return;
+
+ language = pango_context_get_language(pango_layout_get_context(m_layout.get()));
+ if (language == nullptr)
+ language = pango_language_get_default ();
+ latin_uses_default_language = pango_language_includes_script (language, PANGO_SCRIPT_LATIN);
+
+ text = pango_layout_get_text(m_layout.get());
+
+ line = pango_layout_get_line_readonly(m_layout.get(), 0);
+
+ /* Don't cache if more than one font used for the line */
+ if (G_UNLIKELY (!line || !line->runs || line->runs->next))
+ return;
+
+ glyph_item = (PangoGlyphItem *)line->runs->data;
+ glyph_string = glyph_item->glyphs;
+ pango_font = glyph_item->item->analysis.font;
+ if (!pango_font)
+ return;
+ scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *) pango_font);
+ if (!scaled_font)
+ return;
+
+ for (more = pango_glyph_item_iter_init_start (&iter, glyph_item, text);
+ more;
+ more = pango_glyph_item_iter_next_cluster (&iter))
+ {
+ PangoGlyphGeometry *geometry;
+ PangoGlyph glyph;
+ vteunistr c;
+
+ /* Only cache simple clusters */
+ if (iter.start_char +1 != iter.end_char ||
+ iter.start_index+1 != iter.end_index ||
+ iter.start_glyph+1 != iter.end_glyph)
+ continue;
+
+ c = text[iter.start_index];
+ glyph = glyph_string->glyphs[iter.start_glyph].glyph;
+ geometry = &glyph_string->glyphs[iter.start_glyph].geometry;
+
+ /* If not using the default locale language, only cache non-common
+ * characters as common characters get their font from their neighbors
+ * and we don't want to force Latin on them. */
+ if (!latin_uses_default_language &&
+ g_unichar_get_script (c) <= G_UNICODE_SCRIPT_INHERITED)
+ continue;
+
+ /* Only cache simple glyphs */
+ if (!(glyph <= 0xFFFF) || (geometry->x_offset | geometry->y_offset) != 0)
+ continue;
+
+ auto uinfo = find_unistr_info(c);
+ if (G_UNLIKELY (uinfo->coverage() != UnistrInfo::Coverage::UNKNOWN))
+ continue;
+
+ auto ufi = &uinfo->m_ufi;
+
+ uinfo->width = PANGO_PIXELS_CEIL (geometry->width);
+ uinfo->has_unknown_chars = false;
+
+ uinfo->set_coverage(UnistrInfo::Coverage::USE_CAIRO_GLYPH);
+
+ ufi->using_cairo_glyph.scaled_font = cairo_scaled_font_reference (scaled_font);
+ ufi->using_cairo_glyph.glyph_index = glyph;
+
+#ifdef VTE_DEBUG
+ m_coverage_count[0]++;
+ m_coverage_count[(unsigned)uinfo->coverage()]++;
+#endif
+ }
+
+#ifdef VTE_DEBUG
+ _vte_debug_print (VTE_DEBUG_PANGOCAIRO,
+ "vtepangocairo: %p cached %d ASCII letters\n",
+ (void*)this, m_coverage_count[0]);
+#endif
+}
+
+void
+FontInfo::measure_font()
+{
+ PangoRectangle logical;
+
+ /* Measure U+0021..U+007E individually instead of all together and then
+ * averaging. For monospace fonts, the results should be the same, but
+ * if the user (by design, or trough mis-configuration) uses a proportional
+ * font, the latter method will greatly underestimate the required width,
+ * leading to unreadable, overlapping characters.
+ * https://gitlab.gnome.org/GNOME/vte/issues/138
+ */
+ int max_width{1};
+ int max_height{1};
+ for (char c = 0x21; c < 0x7f; ++c) {
+ pango_layout_set_text(m_layout.get(), &c, 1);
+ pango_layout_get_extents(m_layout.get(), nullptr, &logical);
+ max_width = std::max(max_width, PANGO_PIXELS_CEIL(logical.width));
+ max_height = std::max(max_height, PANGO_PIXELS_CEIL(logical.height));
+ }
+
+ /* Use the sample text to get the baseline */
+ pango_layout_set_text(m_layout.get(), VTE_DRAW_SINGLE_WIDE_CHARACTERS, -1);
+ pango_layout_get_extents(m_layout.get(), nullptr, &logical);
+ /* We don't do CEIL for width since we are averaging;
+ * rounding is more accurate */
+ m_ascent = PANGO_PIXELS_CEIL(pango_layout_get_baseline(m_layout.get()));
+
+ m_height = max_height;
+ m_width = max_width;
+
+ /* Now that we shaped the entire ASCII character string, cache glyph
+ * info for them */
+ cache_ascii();
+
+ if (m_height == 0) {
+ m_height = PANGO_PIXELS_CEIL (logical.height);
+ }
+ if (m_ascent == 0) {
+ m_ascent = PANGO_PIXELS_CEIL(pango_layout_get_baseline(m_layout.get()));
+ }
+
+ _vte_debug_print (VTE_DEBUG_MISC,
+ "vtepangocairo: %p font metrics = %dx%d (%d)\n",
+ (void*)this, m_width, m_height, m_ascent);
+}
+
+FontInfo::FontInfo(PangoContext *context)
+{
+ _vte_debug_print (VTE_DEBUG_PANGOCAIRO,
+ "vtepangocairo: %p allocating FontInfo\n",
+ (void*)this);
+
+ // FIXME: placement new
+ memset(m_ascii_unistr_info, 0, sizeof(m_ascii_unistr_info));
+
+ m_layout = vte::glib::take_ref(pango_layout_new(context));
+
+ auto tabs = pango_tab_array_new_with_positions(1, FALSE, PANGO_TAB_LEFT, 1);
+ pango_layout_set_tabs(m_layout.get(), tabs);
+ pango_tab_array_free(tabs);
+
+ // FIXME!!!
+ m_string = g_string_sized_new(VTE_UTF8_BPC+1);
+
+ measure_font();
+
+ g_hash_table_insert(s_font_info_for_context,
+ pango_layout_get_context(m_layout.get()),
+ this);
+
+}
+
+FontInfo::~FontInfo()
+{
+ g_hash_table_remove(s_font_info_for_context,
+ pango_layout_get_context(m_layout.get()));
+
+ vteunistr i;
+
+#ifdef VTE_DEBUG
+ _vte_debug_print (VTE_DEBUG_PANGOCAIRO,
+ "vtepangocairo: %p freeing font_info. coverages %d = %d + %d + %d\n",
+ (void*)this,
+ m_coverage_count[0],
+ m_coverage_count[1],
+ m_coverage_count[2],
+ m_coverage_count[3]);
+#endif
+
+ g_string_free(m_string, true);
+
+ for (i = 0; i < G_N_ELEMENTS(m_ascii_unistr_info); i++)
+ m_ascii_unistr_info[i].~UnistrInfo();
+
+ if (m_other_unistr_info) {
+ g_hash_table_destroy(m_other_unistr_info);
+ }
+}
+
+static GQuark
+fontconfig_timestamp_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("vte-fontconfig-timestamp");
+
+ return quark;
+}
+
+static void
+vte_pango_context_set_fontconfig_timestamp (PangoContext *context,
+ guint fontconfig_timestamp)
+{
+ g_object_set_qdata ((GObject *) context,
+ fontconfig_timestamp_quark (),
+ GUINT_TO_POINTER (fontconfig_timestamp));
+}
+
+static guint
+vte_pango_context_get_fontconfig_timestamp (PangoContext *context)
+{
+ return GPOINTER_TO_UINT (g_object_get_qdata ((GObject *) context,
+ fontconfig_timestamp_quark ()));
+}
+
+static guint
+context_hash (PangoContext *context)
+{
+ return pango_units_from_double (pango_cairo_context_get_resolution (context))
+ ^ pango_font_description_hash (pango_context_get_font_description (context))
+ ^ cairo_font_options_hash (pango_cairo_context_get_font_options (context))
+ ^ GPOINTER_TO_UINT (pango_context_get_language (context))
+ ^ vte_pango_context_get_fontconfig_timestamp (context);
+}
+
+static gboolean
+context_equal (PangoContext *a,
+ PangoContext *b)
+{
+ return _vte_double_equal(pango_cairo_context_get_resolution(a), pango_cairo_context_get_resolution
(b))
+ && pango_font_description_equal (pango_context_get_font_description (a),
pango_context_get_font_description (b))
+ && cairo_font_options_equal (pango_cairo_context_get_font_options (a),
pango_cairo_context_get_font_options (b))
+ && pango_context_get_language (a) == pango_context_get_language (b)
+ && vte_pango_context_get_fontconfig_timestamp (a) == vte_pango_context_get_fontconfig_timestamp
(b);
+}
+
+// FIXMEchpe return vte::base::RefPtr<FontInfo>
+/* assumes ownership/reference of context */
+FontInfo*
+FontInfo::find_for_context(vte::glib::RefPtr<PangoContext>& context)
+{
+ if (G_UNLIKELY (s_font_info_for_context == nullptr))
+ s_font_info_for_context = g_hash_table_new((GHashFunc) context_hash, (GEqualFunc)
context_equal);
+
+ auto info = reinterpret_cast<FontInfo*>(g_hash_table_lookup(s_font_info_for_context, context.get()));
+ if (G_LIKELY(info)) {
+ _vte_debug_print (VTE_DEBUG_PANGOCAIRO,
+ "vtepangocairo: %p found font_info in cache\n",
+ info);
+ info = info->ref();
+ } else {
+ info = new FontInfo{context.release()};
+ }
+
+ return info;
+}
+
+/* assumes ownership/reference of context */
+FontInfo*
+FontInfo::create_for_context(vte::glib::RefPtr<PangoContext> context,
+ PangoFontDescription const* desc,
+ PangoLanguage* language,
+ guint fontconfig_timestamp)
+{
+ if (!PANGO_IS_CAIRO_FONT_MAP(pango_context_get_font_map(context.get()))) {
+ /* Ouch, Gtk+ switched over to some drawing system?
+ * Lets just create one from the default font map.
+ */
+ context =
vte::glib::take_ref(pango_font_map_create_context(pango_cairo_font_map_get_default()));
+ }
+
+ vte_pango_context_set_fontconfig_timestamp(context.get(), fontconfig_timestamp);
+
+ pango_context_set_base_dir(context.get(), PANGO_DIRECTION_LTR);
+
+ if (desc)
+ pango_context_set_font_description(context.get(), desc);
+
+ pango_context_set_language(context.get(), language);
+
+ /* Make sure our contexts have a font_options set. We use
+ * this invariant in our context hash and equal functions.
+ */
+ if (!pango_cairo_context_get_font_options(context.get())) {
+ cairo_font_options_t *font_options;
+
+ font_options = cairo_font_options_create ();
+ pango_cairo_context_set_font_options(context.get(), font_options);
+ cairo_font_options_destroy (font_options);
+ }
+
+ return find_for_context(context);
+}
+
+FontInfo*
+FontInfo::create_for_screen(GdkScreen* screen,
+ PangoFontDescription const* desc,
+ PangoLanguage* language)
+{
+ auto settings = gtk_settings_get_for_screen(screen);
+ auto fontconfig_timestamp = guint{};
+ g_object_get (settings, "gtk-fontconfig-timestamp", &fontconfig_timestamp, nullptr);
+ return create_for_context(vte::glib::take_ref(gdk_pango_context_get_for_screen(screen)),
+ desc, language, fontconfig_timestamp);
+}
+
+FontInfo*
+FontInfo::create_for_widget(GtkWidget* widget,
+ PangoFontDescription const* desc)
+{
+ auto screen = gtk_widget_get_screen(widget);
+ auto language = pango_context_get_language(gtk_widget_get_pango_context(widget));
+
+ return create_for_screen(screen, desc, language);
+}
+
+FontInfo::UnistrInfo*
+FontInfo::get_unistr_info(vteunistr c)
+{
+ PangoRectangle logical;
+ PangoLayoutLine *line;
+
+ auto uinfo = find_unistr_info(c);
+ if (G_LIKELY (uinfo->coverage() != UnistrInfo::Coverage::UNKNOWN))
+ return uinfo;
+
+ auto ufi = &uinfo->m_ufi;
+
+ g_string_set_size(m_string, 0);
+ _vte_unistr_append_to_string(c, m_string);
+ pango_layout_set_text(m_layout.get(), m_string->str, m_string->len);
+ pango_layout_get_extents(m_layout.get(), NULL, &logical);
+
+ uinfo->width = PANGO_PIXELS_CEIL (logical.width);
+
+ line = pango_layout_get_line_readonly(m_layout.get(), 0);
+
+ uinfo->has_unknown_chars = pango_layout_get_unknown_glyphs_count(m_layout.get()) != 0;
+ /* we use PangoLayoutRun rendering unless there is exactly one run in the line. */
+ if (G_UNLIKELY (!line || !line->runs || line->runs->next))
+ {
+ uinfo->set_coverage(UnistrInfo::Coverage::USE_PANGO_LAYOUT_LINE);
+
+ ufi->using_pango_layout_line.line = pango_layout_line_ref (line);
+ /* we hold a manual reference on layout. pango currently
+ * doesn't work if line->layout is NULL. ugh! */
+ pango_layout_set_text(m_layout.get(), "", -1); /* make layout disassociate from the line */
+ ufi->using_pango_layout_line.line->layout = (PangoLayout *)g_object_ref(m_layout.get());
+
+ } else {
+ PangoGlyphItem *glyph_item = (PangoGlyphItem *)line->runs->data;
+ PangoFont *pango_font = glyph_item->item->analysis.font;
+ PangoGlyphString *glyph_string = glyph_item->glyphs;
+
+ /* we use fast cairo path if glyph string has only one real
+ * glyph and at origin */
+ if (!uinfo->has_unknown_chars &&
+ glyph_string->num_glyphs == 1 && glyph_string->glyphs[0].glyph <= 0xFFFF &&
+ (glyph_string->glyphs[0].geometry.x_offset |
+ glyph_string->glyphs[0].geometry.y_offset) == 0)
+ {
+ cairo_scaled_font_t *scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont
*) pango_font);
+
+ if (scaled_font) {
+ uinfo->set_coverage(UnistrInfo::Coverage::USE_CAIRO_GLYPH);
+
+ ufi->using_cairo_glyph.scaled_font = cairo_scaled_font_reference
(scaled_font);
+ ufi->using_cairo_glyph.glyph_index = glyph_string->glyphs[0].glyph;
+ }
+ }
+
+ /* use pango fast path otherwise */
+ if (G_UNLIKELY (uinfo->coverage() == UnistrInfo::Coverage::UNKNOWN)) {
+ uinfo->set_coverage(UnistrInfo::Coverage::USE_PANGO_GLYPH_STRING);
+
+ ufi->using_pango_glyph_string.font = pango_font ? (PangoFont *)g_object_ref
(pango_font) : NULL;
+ ufi->using_pango_glyph_string.glyph_string = pango_glyph_string_copy (glyph_string);
+ }
+ }
+
+ /* release internal layout resources */
+ pango_layout_set_text(m_layout.get(), "", -1);
+
+#ifdef VTE_DEBUG
+ m_coverage_count[0]++;
+ m_coverage_count[uinfo->m_coverage]++;
+#endif
+
+ return uinfo;
+}
+
+} // namespace view
+} // namespace vte
diff --git a/src/fonts-pangocairo.hh b/src/fonts-pangocairo.hh
new file mode 100644
index 00000000..3a30845c
--- /dev/null
+++ b/src/fonts-pangocairo.hh
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2003,2008 Red Hat, Inc.
+ * Copyright © 2019, 2020 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <cassert>
+
+#include <glib.h>
+#include <pango/pangocairo.h>
+#include <gtk/gtk.h>
+
+#include "refptr.hh"
+#include "vteunistr.h"
+
+/* Overview:
+ *
+ *
+ * This file implements vte rendering using pangocairo. Note that this does
+ * NOT implement any kind of complex text rendering. That's not currently a
+ * goal.
+ *
+ * The aim is to be super-fast and avoid unneeded work as much as possible.
+ * Here is an overview of how that is accomplished:
+ *
+ * - We attach a font_info to the draw. A font_info has all the information
+ * to quickly draw text.
+ *
+ * - A font_info keeps uses unistr_font_info structs that represent all
+ * information needed to quickly draw a single vteunistr. The font_info
+ * creates those unistr_font_info structs on demand and caches them
+ * indefinitely. It uses a direct array for the ASCII range and a hash
+ * table for the rest.
+ *
+ *
+ * Fast rendering of unistrs:
+ *
+ * A unistr_font_info (uinfo) calls Pango to set text for the unistr upon
+ * initialization and then caches information needed to draw the results
+ * later. It uses three different internal representations and respectively
+ * three drawing paths:
+ *
+ * - Coverage::USE_CAIRO_GLYPH:
+ * Keeping a single glyph index and a cairo scaled-font. This is the
+ * fastest way to draw text as it bypasses Pango completely and allows
+ * for stuffing multiple glyphs into a single cairo_show_glyphs() request
+ * (if scaled-fonts match). This method is used if the glyphs used for
+ * the vteunistr as determined by Pango consists of a single regular glyph
+ * positioned at 0,0 using a regular font. This method is used for more
+ * than 99% of the cases. Only exceptional cases fall through to the
+ * other two methods.
+ *
+ * - Coverage::USE_PANGO_GLYPH_STRING:
+ * Keeping a pango glyphstring and a pango font. This is slightly slower
+ * than the previous case as drawing each glyph goes through pango
+ * separately and causes a separate cairo_show_glyphs() call. This method
+ * is used when the previous method cannot be used but the glyphs for the
+ * character all use a single font. This is the method used for hexboxes
+ * and "empty" characters like U+200C ZERO WIDTH NON-JOINER for example.
+ *
+ * - Coverage::USE_PANGO_LAYOUT_LINE:
+ * Keeping a pango layout line. This method is used only in the very
+ * weird and exceptional case that a single vteunistr uses more than one
+ * font to be drawn. This happens for example if some diacretics is not
+ * available in the font chosen for the base character.
+ *
+ *
+ * Caching of font infos:
+ *
+ * To avoid recreating font info structs for the same font again and again we
+ * do the following:
+ *
+ * - Use a global cache to share font info structs across different widgets.
+ * We use pango language, cairo font options, resolution, and font description
+ * as the key for our hash table.
+ *
+ * - When a font info struct is no longer used by any widget, we delay
+ * destroying it for a while (FONT_CACHE_TIMEOUT seconds). This is
+ * supposed to serve two purposes:
+ *
+ * * Destroying a terminal widget and creating it again right after will
+ * reuse the font info struct from the previous widget.
+ *
+ * * Zooming in and out a terminal reuses the font info structs.
+ *
+ *
+ * Pre-caching ASCII letters:
+ *
+ * When initializing a font info struct we measure a string consisting of all
+ * ASCII letters and some other ASCII characters. Since we have a shaped pango
+ * layout at hand, we walk over it and cache unistr font info for the ASCII
+ * letters if we can do that easily using Coverage::USE_CAIRO_GLYPH. This
+ * means that we precache all ASCII letters without any extra pango shaping
+ * involved.
+ */
+
+namespace vte {
+namespace view {
+
+class DrawingContext;
+
+class FontInfo {
+ friend class DrawingContext;
+
+ int const font_cache_timeout = 30; // seconds
+
+public:
+ FontInfo(PangoContext* context);
+ ~FontInfo();
+
+ FontInfo* ref()
+ {
+ // refcount is 0 when unused but still in cache
+ assert(m_ref_count >= 0);
+
+ ++m_ref_count;
+
+ if (m_destroy_timeout != 0) {
+ g_source_remove (m_destroy_timeout);
+ m_destroy_timeout = 0;
+ }
+
+ return this;
+ }
+
+ void unref()
+ {
+ assert(m_ref_count > 0);
+ if (--m_ref_count > 0)
+ return;
+
+ /* Delay destruction by a few seconds, in case we need it again */
+ m_destroy_timeout = gdk_threads_add_timeout_seconds(font_cache_timeout,
+ (GSourceFunc)destroy_delayed_cb,
+ this);
+ }
+
+ struct UnistrInfo {
+ enum class Coverage : uint8_t {
+ /* in increasing order of speed */
+ UNKNOWN = 0u, /* we don't know about the character yet */
+ USE_PANGO_LAYOUT_LINE, /* use a PangoLayoutLine for the character */
+ USE_PANGO_GLYPH_STRING, /* use a PangoGlyphString for the character */
+ USE_CAIRO_GLYPH /* use a cairo_glyph_t for the character */
+ };
+
+ uint8_t m_coverage{uint8_t(Coverage::UNKNOWN)};
+ uint8_t has_unknown_chars;
+ uint16_t width;
+
+ inline constexpr Coverage coverage() const noexcept { return Coverage{m_coverage}; }
+ inline constexpr void set_coverage(Coverage coverage) { m_coverage = uint8_t(coverage); }
+
+ // FIXME: use std::variant<std::monostate, RefPtr<PangoLayoutLine>, ...> ?
+ union unistr_font_info {
+ /* Coverage::USE_PANGO_LAYOUT_LINE */
+ struct {
+ PangoLayoutLine *line;
+ } using_pango_layout_line;
+ /* Coverage::USE_PANGO_GLYPH_STRING */
+ struct {
+ PangoFont *font;
+ PangoGlyphString *glyph_string;
+ } using_pango_glyph_string;
+ /* Coverage::USE_CAIRO_GLYPH */
+ struct {
+ cairo_scaled_font_t *scaled_font;
+ unsigned int glyph_index;
+ } using_cairo_glyph;
+ } m_ufi;
+
+ UnistrInfo() noexcept = default;
+
+ ~UnistrInfo() noexcept
+ {
+ switch (coverage()) {
+ default:
+ case Coverage::UNKNOWN:
+ break;
+ case Coverage::USE_PANGO_LAYOUT_LINE:
+ /* we hold a manual reference on layout */
+ g_object_unref (m_ufi.using_pango_layout_line.line->layout);
+ m_ufi.using_pango_layout_line.line->layout = NULL;
+ pango_layout_line_unref (m_ufi.using_pango_layout_line.line);
+ m_ufi.using_pango_layout_line.line = NULL;
+ break;
+ case Coverage::USE_PANGO_GLYPH_STRING:
+ if (m_ufi.using_pango_glyph_string.font)
+ g_object_unref (m_ufi.using_pango_glyph_string.font);
+ m_ufi.using_pango_glyph_string.font = NULL;
+ pango_glyph_string_free (m_ufi.using_pango_glyph_string.glyph_string);
+ m_ufi.using_pango_glyph_string.glyph_string = NULL;
+ break;
+ case Coverage::USE_CAIRO_GLYPH:
+ cairo_scaled_font_destroy (m_ufi.using_cairo_glyph.scaled_font);
+ m_ufi.using_cairo_glyph.scaled_font = NULL;
+ break;
+ }
+ }
+
+ }; // struct UnistrInfo
+
+ UnistrInfo *get_unistr_info(vteunistr c);
+ inline constexpr int width() const { return m_width; }
+ inline constexpr int height() const { return m_height; }
+ inline constexpr int ascent() const { return m_ascent; }
+
+private:
+
+ static void unistr_info_destroy(UnistrInfo* uinfo)
+ {
+ delete uinfo;
+ }
+
+ static gboolean destroy_delayed_cb(void* that)
+ {
+ auto info = reinterpret_cast<FontInfo*>(that);
+ info->m_destroy_timeout = 0;
+ delete info;
+ return false;
+ }
+
+ mutable int m_ref_count{1};
+
+ UnistrInfo* find_unistr_info(vteunistr c);
+ void cache_ascii();
+ void measure_font();
+ guint m_destroy_timeout{0}; /* only used when ref_count == 0 */
+
+ /* reusable layout set with font and everything set */
+ vte::glib::RefPtr<PangoLayout> m_layout{};
+
+ /* cache of character info */
+ // FIXME: use std::array<UnistrInfo, 128>
+ UnistrInfo m_ascii_unistr_info[128];
+ // FIXME: use std::unordered_map<vteunistr, UnistrInfo>
+ GHashTable* m_other_unistr_info{nullptr};
+
+ /* cell metrics as taken from the font, not yet scaled by cell_{width,height}_scale */
+ int m_width{1};
+ int m_height{1};
+ int m_ascent{0};
+
+ /* reusable string for UTF-8 conversion */
+ // FIXME: use std::string
+ GString* m_string{nullptr};
+
+#ifdef VTE_DEBUG
+ /* profiling info */
+ int m_coverage_count[4]{0, 0, 0, 0};
+#endif
+
+ static FontInfo* find_for_context(vte::glib::RefPtr<PangoContext>& context);
+ static FontInfo* create_for_context(vte::glib::RefPtr<PangoContext> context,
+ PangoFontDescription const* desc,
+ PangoLanguage* language,
+ guint fontconfig_timestamp);
+ static FontInfo *create_for_screen(GdkScreen* screen,
+ PangoFontDescription const* desc,
+ PangoLanguage* language);
+public:
+
+ static FontInfo *create_for_widget(GtkWidget* widget,
+ PangoFontDescription const* desc);
+
+private:
+ static inline GHashTable* s_font_info_for_context{nullptr};
+
+}; // class FontInfo
+
+} // namespace view
+} // namespace vte
diff --git a/src/fwd.hh b/src/fwd.hh
index 7cdfb741..24d7eea7 100644
--- a/src/fwd.hh
+++ b/src/fwd.hh
@@ -31,4 +31,11 @@ class Widget;
} // namespace platform
+namespace view {
+
+class FontInfo;
+class DrawingContext;
+
+} // namespace view
+
} // namespace vte
diff --git a/src/meson.build b/src/meson.build
index 1fe64810..6cd1b67a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -104,6 +104,8 @@ libvte_common_sources = debug_sources + glib_glue_sources + libc_glue_sources +
'chunk.hh',
'color-triple.hh',
'cxx-utils.hh',
+ 'fonts-pangocairo.cc',
+ 'fonts-pangocairo.hh',
'gobject-glue.hh',
'keymap.cc',
'keymap.h',
diff --git a/src/vtedraw.cc b/src/vtedraw.cc
index 06c8626d..b5af3d37 100644
--- a/src/vtedraw.cc
+++ b/src/vtedraw.cc
@@ -36,119 +36,9 @@
#include <pango/pangocairo.h>
+#include "fonts-pangocairo.hh"
#include "refptr.hh"
-/* Have a space between letters to make sure ligatures aren't used when caching the glyphs: bug 793391. */
-#define VTE_DRAW_SINGLE_WIDE_CHARACTERS \
- " ! \" # $ % & ' ( ) * + , - . / " \
- "0 1 2 3 4 5 6 7 8 9 " \
- ": ; < = > ? @ " \
- "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z " \
- "[ \\ ] ^ _ ` " \
- "a b c d e f g h i j k l m n o p q r s t u v w x y z " \
- "{ | } ~ " \
- ""
-
-static inline bool
-_vte_double_equal(double a,
- double b)
-{
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
- return a == b;
-#pragma GCC diagnostic pop
-}
-
-/* Overview:
- *
- *
- * This file implements vte rendering using pangocairo. Note that this does
- * NOT implement any kind of complex text rendering. That's not currently a
- * goal.
- *
- * The aim is to be super-fast and avoid unneeded work as much as possible.
- * Here is an overview of how that is accomplished:
- *
- * - We attach a font_info to the draw. A font_info has all the information
- * to quickly draw text.
- *
- * - A font_info keeps uses unistr_font_info structs that represent all
- * information needed to quickly draw a single vteunistr. The font_info
- * creates those unistr_font_info structs on demand and caches them
- * indefinitely. It uses a direct array for the ASCII range and a hash
- * table for the rest.
- *
- *
- * Fast rendering of unistrs:
- *
- * A unistr_font_info (uinfo) calls Pango to set text for the unistr upon
- * initialization and then caches information needed to draw the results
- * later. It uses three different internal representations and respectively
- * three drawing paths:
- *
- * - Coverage::USE_CAIRO_GLYPH:
- * Keeping a single glyph index and a cairo scaled-font. This is the
- * fastest way to draw text as it bypasses Pango completely and allows
- * for stuffing multiple glyphs into a single cairo_show_glyphs() request
- * (if scaled-fonts match). This method is used if the glyphs used for
- * the vteunistr as determined by Pango consists of a single regular glyph
- * positioned at 0,0 using a regular font. This method is used for more
- * than 99% of the cases. Only exceptional cases fall through to the
- * other two methods.
- *
- * - Coverage::USE_PANGO_GLYPH_STRING:
- * Keeping a pango glyphstring and a pango font. This is slightly slower
- * than the previous case as drawing each glyph goes through pango
- * separately and causes a separate cairo_show_glyphs() call. This method
- * is used when the previous method cannot be used but the glyphs for the
- * character all use a single font. This is the method used for hexboxes
- * and "empty" characters like U+200C ZERO WIDTH NON-JOINER for example.
- *
- * - Coverage::USE_PANGO_LAYOUT_LINE:
- * Keeping a pango layout line. This method is used only in the very
- * weird and exceptional case that a single vteunistr uses more than one
- * font to be drawn. This happens for example if some diacretics is not
- * available in the font chosen for the base character.
- *
- *
- * Caching of font infos:
- *
- * To avoid recreating font info structs for the same font again and again we
- * do the following:
- *
- * - Use a global cache to share font info structs across different widgets.
- * We use pango language, cairo font options, resolution, and font description
- * as the key for our hash table.
- *
- * - When a font info struct is no longer used by any widget, we delay
- * destroying it for a while (FONT_CACHE_TIMEOUT seconds). This is
- * supposed to serve two purposes:
- *
- * * Destroying a terminal widget and creating it again right after will
- * reuse the font info struct from the previous widget.
- *
- * * Zooming in and out a terminal reuses the font info structs.
- *
- *
- * Pre-caching ASCII letters:
- *
- * When initializing a font info struct we measure a string consisting of all
- * ASCII letters and some other ASCII characters. Since we have a shaped pango
- * layout at hand, we walk over it and cache unistr font info for the ASCII
- * letters if we can do that easily using Coverage::USE_CAIRO_GLYPH. This
- * means that we precache all ASCII letters without any extra pango shaping
- * involved.
- */
-
-
-
-#define FONT_CACHE_TIMEOUT (30) /* seconds */
-
-
-/* All shared data structures are implicitly protected by GDK mutex, because
- * that's how vte.c works and we only get called from there. */
-
-
/* cairo_show_glyphs accepts runs up to 102 glyphs before it allocates a
* temporary array.
*
@@ -187,594 +77,6 @@ _vte_draw_get_undercurl_height(gint width, double line_width)
namespace vte {
namespace view {
-class FontInfo {
- friend class DrawingContext;
-
-public:
- FontInfo(PangoContext* context);
- ~FontInfo();
-
- FontInfo* ref()
- {
- // refcount is 0 when unused but still in cache
- assert(m_ref_count >= 0);
-
- ++m_ref_count;
-
- if (m_destroy_timeout != 0) {
- g_source_remove (m_destroy_timeout);
- m_destroy_timeout = 0;
- }
-
- return this;
- }
-
- void unref()
- {
- assert(m_ref_count > 0);
- if (--m_ref_count > 0)
- return;
-
- /* Delay destruction by a few seconds, in case we need it again */
- m_destroy_timeout = gdk_threads_add_timeout_seconds(FONT_CACHE_TIMEOUT,
- (GSourceFunc)destroy_delayed_cb,
- this);
- }
-
- struct UnistrInfo {
- enum class Coverage : uint8_t {
- /* in increasing order of speed */
- UNKNOWN = 0u, /* we don't know about the character yet */
- USE_PANGO_LAYOUT_LINE, /* use a PangoLayoutLine for the character */
- USE_PANGO_GLYPH_STRING, /* use a PangoGlyphString for the character */
- USE_CAIRO_GLYPH /* use a cairo_glyph_t for the character */
- };
-
- uint8_t m_coverage{uint8_t(Coverage::UNKNOWN)};
- uint8_t has_unknown_chars;
- uint16_t width;
-
- inline constexpr Coverage coverage() const noexcept { return Coverage{m_coverage}; }
- inline constexpr void set_coverage(Coverage coverage) { m_coverage = uint8_t(coverage); }
-
- // FIXME: use std::variant<std::monostate, RefPtr<PangoLayoutLine>, ...> ?
- union unistr_font_info {
- /* Coverage::USE_PANGO_LAYOUT_LINE */
- struct {
- PangoLayoutLine *line;
- } using_pango_layout_line;
- /* Coverage::USE_PANGO_GLYPH_STRING */
- struct {
- PangoFont *font;
- PangoGlyphString *glyph_string;
- } using_pango_glyph_string;
- /* Coverage::USE_CAIRO_GLYPH */
- struct {
- cairo_scaled_font_t *scaled_font;
- unsigned int glyph_index;
- } using_cairo_glyph;
- } m_ufi;
-
- UnistrInfo() noexcept = default;
-
- ~UnistrInfo() noexcept
- {
- switch (coverage()) {
- default:
- case Coverage::UNKNOWN:
- break;
- case Coverage::USE_PANGO_LAYOUT_LINE:
- /* we hold a manual reference on layout */
- g_object_unref (m_ufi.using_pango_layout_line.line->layout);
- m_ufi.using_pango_layout_line.line->layout = NULL;
- pango_layout_line_unref (m_ufi.using_pango_layout_line.line);
- m_ufi.using_pango_layout_line.line = NULL;
- break;
- case Coverage::USE_PANGO_GLYPH_STRING:
- if (m_ufi.using_pango_glyph_string.font)
- g_object_unref (m_ufi.using_pango_glyph_string.font);
- m_ufi.using_pango_glyph_string.font = NULL;
- pango_glyph_string_free (m_ufi.using_pango_glyph_string.glyph_string);
- m_ufi.using_pango_glyph_string.glyph_string = NULL;
- break;
- case Coverage::USE_CAIRO_GLYPH:
- cairo_scaled_font_destroy (m_ufi.using_cairo_glyph.scaled_font);
- m_ufi.using_cairo_glyph.scaled_font = NULL;
- break;
- }
- }
-
- }; // struct UnistrInfo
-
- UnistrInfo *get_unistr_info(vteunistr c);
- inline constexpr int width() const { return m_width; }
- inline constexpr int height() const { return m_height; }
- inline constexpr int ascent() const { return m_ascent; }
-
-private:
-
- static void unistr_info_destroy(UnistrInfo* uinfo)
- {
- delete uinfo;
- }
-
- static gboolean destroy_delayed_cb(void* that)
- {
- auto info = reinterpret_cast<FontInfo*>(that);
- info->m_destroy_timeout = 0;
- delete info;
- return false;
- }
-
- mutable int m_ref_count{1};
-
- UnistrInfo* find_unistr_info(vteunistr c);
- void cache_ascii();
- void measure_font();
- guint m_destroy_timeout{0}; /* only used when ref_count == 0 */
-
- /* reusable layout set with font and everything set */
- vte::glib::RefPtr<PangoLayout> m_layout{};
-
- /* cache of character info */
- // FIXME: use std::array<UnistrInfo, 128>
- UnistrInfo m_ascii_unistr_info[128];
- // FIXME: use std::unordered_map<vteunistr, UnistrInfo>
- GHashTable* m_other_unistr_info{nullptr};
-
- /* cell metrics as taken from the font, not yet scaled by cell_{width,height}_scale */
- int m_width{1};
- int m_height{1};
- int m_ascent{0};
-
- /* reusable string for UTF-8 conversion */
- // FIXME: use std::string
- GString* m_string{nullptr};
-
-#ifdef VTE_DEBUG
- /* profiling info */
- int m_coverage_count[4]{0, 0, 0, 0};
-#endif
-
- static FontInfo* find_for_context(vte::glib::RefPtr<PangoContext>& context);
- static FontInfo* create_for_context(vte::glib::RefPtr<PangoContext> context,
- PangoFontDescription const* desc,
- PangoLanguage* language,
- guint fontconfig_timestamp);
- static FontInfo *create_for_screen(GdkScreen* screen,
- PangoFontDescription const* desc,
- PangoLanguage* language);
-public:
-
- static FontInfo *create_for_widget(GtkWidget* widget,
- PangoFontDescription const* desc);
-
-private:
- static inline GHashTable* s_font_info_for_context{nullptr};
-
-}; // class FontInfo
-
-FontInfo::UnistrInfo*
-FontInfo::find_unistr_info(vteunistr c)
-{
- if (G_LIKELY (c < G_N_ELEMENTS(m_ascii_unistr_info)))
- return &m_ascii_unistr_info[c];
-
- if (G_UNLIKELY (m_other_unistr_info == nullptr))
- m_other_unistr_info = g_hash_table_new_full(nullptr, nullptr, nullptr,
(GDestroyNotify)unistr_info_destroy);
-
- auto uinfo = reinterpret_cast<UnistrInfo*>(g_hash_table_lookup(m_other_unistr_info,
GINT_TO_POINTER(c)));
- if (G_LIKELY (uinfo))
- return uinfo;
-
- uinfo = new UnistrInfo{};
- g_hash_table_insert(m_other_unistr_info, GINT_TO_POINTER (c), uinfo);
- return uinfo;
-}
-
-void
-FontInfo::cache_ascii()
-{
- PangoLayoutLine *line;
- PangoGlyphItemIter iter;
- PangoGlyphItem *glyph_item;
- PangoGlyphString *glyph_string;
- PangoFont *pango_font;
- cairo_scaled_font_t *scaled_font;
- const char *text;
- gboolean more;
- PangoLanguage *language;
- gboolean latin_uses_default_language;
-
- /* We have m_layout holding most ASCII characters. We want to
- * cache as much info as we can about the ASCII letters so we don't
- * have to look them up again later */
-
- /* Don't cache if unknown glyphs found in layout */
- if (pango_layout_get_unknown_glyphs_count(m_layout.get()) != 0)
- return;
-
- language = pango_context_get_language(pango_layout_get_context(m_layout.get()));
- if (language == nullptr)
- language = pango_language_get_default ();
- latin_uses_default_language = pango_language_includes_script (language, PANGO_SCRIPT_LATIN);
-
- text = pango_layout_get_text(m_layout.get());
-
- line = pango_layout_get_line_readonly(m_layout.get(), 0);
-
- /* Don't cache if more than one font used for the line */
- if (G_UNLIKELY (!line || !line->runs || line->runs->next))
- return;
-
- glyph_item = (PangoGlyphItem *)line->runs->data;
- glyph_string = glyph_item->glyphs;
- pango_font = glyph_item->item->analysis.font;
- if (!pango_font)
- return;
- scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *) pango_font);
- if (!scaled_font)
- return;
-
- for (more = pango_glyph_item_iter_init_start (&iter, glyph_item, text);
- more;
- more = pango_glyph_item_iter_next_cluster (&iter))
- {
- PangoGlyphGeometry *geometry;
- PangoGlyph glyph;
- vteunistr c;
-
- /* Only cache simple clusters */
- if (iter.start_char +1 != iter.end_char ||
- iter.start_index+1 != iter.end_index ||
- iter.start_glyph+1 != iter.end_glyph)
- continue;
-
- c = text[iter.start_index];
- glyph = glyph_string->glyphs[iter.start_glyph].glyph;
- geometry = &glyph_string->glyphs[iter.start_glyph].geometry;
-
- /* If not using the default locale language, only cache non-common
- * characters as common characters get their font from their neighbors
- * and we don't want to force Latin on them. */
- if (!latin_uses_default_language &&
- g_unichar_get_script (c) <= G_UNICODE_SCRIPT_INHERITED)
- continue;
-
- /* Only cache simple glyphs */
- if (!(glyph <= 0xFFFF) || (geometry->x_offset | geometry->y_offset) != 0)
- continue;
-
- auto uinfo = find_unistr_info(c);
- if (G_UNLIKELY (uinfo->coverage() != UnistrInfo::Coverage::UNKNOWN))
- continue;
-
- auto ufi = &uinfo->m_ufi;
-
- uinfo->width = PANGO_PIXELS_CEIL (geometry->width);
- uinfo->has_unknown_chars = false;
-
- uinfo->set_coverage(UnistrInfo::Coverage::USE_CAIRO_GLYPH);
-
- ufi->using_cairo_glyph.scaled_font = cairo_scaled_font_reference (scaled_font);
- ufi->using_cairo_glyph.glyph_index = glyph;
-
-#ifdef VTE_DEBUG
- m_coverage_count[0]++;
- m_coverage_count[(unsigned)uinfo->coverage()]++;
-#endif
- }
-
-#ifdef VTE_DEBUG
- _vte_debug_print (VTE_DEBUG_PANGOCAIRO,
- "vtepangocairo: %p cached %d ASCII letters\n",
- (void*)this, m_coverage_count[0]);
-#endif
-}
-
-void
-FontInfo::measure_font()
-{
- PangoRectangle logical;
-
- /* Measure U+0021..U+007E individually instead of all together and then
- * averaging. For monospace fonts, the results should be the same, but
- * if the user (by design, or trough mis-configuration) uses a proportional
- * font, the latter method will greatly underestimate the required width,
- * leading to unreadable, overlapping characters.
- * https://gitlab.gnome.org/GNOME/vte/issues/138
- */
- int max_width{1};
- int max_height{1};
- for (char c = 0x21; c < 0x7f; ++c) {
- pango_layout_set_text(m_layout.get(), &c, 1);
- pango_layout_get_extents(m_layout.get(), nullptr, &logical);
- max_width = std::max(max_width, PANGO_PIXELS_CEIL(logical.width));
- max_height = std::max(max_height, PANGO_PIXELS_CEIL(logical.height));
- }
-
- /* Use the sample text to get the baseline */
- pango_layout_set_text(m_layout.get(), VTE_DRAW_SINGLE_WIDE_CHARACTERS, -1);
- pango_layout_get_extents(m_layout.get(), nullptr, &logical);
- /* We don't do CEIL for width since we are averaging;
- * rounding is more accurate */
- m_ascent = PANGO_PIXELS_CEIL(pango_layout_get_baseline(m_layout.get()));
-
- m_height = max_height;
- m_width = max_width;
-
- /* Now that we shaped the entire ASCII character string, cache glyph
- * info for them */
- cache_ascii();
-
- if (m_height == 0) {
- m_height = PANGO_PIXELS_CEIL (logical.height);
- }
- if (m_ascent == 0) {
- m_ascent = PANGO_PIXELS_CEIL(pango_layout_get_baseline(m_layout.get()));
- }
-
- _vte_debug_print (VTE_DEBUG_MISC,
- "vtepangocairo: %p font metrics = %dx%d (%d)\n",
- (void*)this, m_width, m_height, m_ascent);
-}
-
-FontInfo::FontInfo(PangoContext *context)
-{
- _vte_debug_print (VTE_DEBUG_PANGOCAIRO,
- "vtepangocairo: %p allocating FontInfo\n",
- (void*)this);
-
- // FIXME: placement new
- memset(m_ascii_unistr_info, 0, sizeof(m_ascii_unistr_info));
-
- m_layout = vte::glib::take_ref(pango_layout_new(context));
-
- auto tabs = pango_tab_array_new_with_positions(1, FALSE, PANGO_TAB_LEFT, 1);
- pango_layout_set_tabs(m_layout.get(), tabs);
- pango_tab_array_free(tabs);
-
- // FIXME!!!
- m_string = g_string_sized_new(VTE_UTF8_BPC+1);
-
- measure_font();
-
- g_hash_table_insert(s_font_info_for_context,
- pango_layout_get_context(m_layout.get()),
- this);
-
-}
-
-FontInfo::~FontInfo()
-{
- g_hash_table_remove(s_font_info_for_context,
- pango_layout_get_context(m_layout.get()));
-
- vteunistr i;
-
-#ifdef VTE_DEBUG
- _vte_debug_print (VTE_DEBUG_PANGOCAIRO,
- "vtepangocairo: %p freeing font_info. coverages %d = %d + %d + %d\n",
- (void*)this,
- m_coverage_count[0],
- m_coverage_count[1],
- m_coverage_count[2],
- m_coverage_count[3]);
-#endif
-
- g_string_free(m_string, true);
-
- for (i = 0; i < G_N_ELEMENTS(m_ascii_unistr_info); i++)
- m_ascii_unistr_info[i].~UnistrInfo();
-
- if (m_other_unistr_info) {
- g_hash_table_destroy(m_other_unistr_info);
- }
-}
-
-static GQuark
-fontconfig_timestamp_quark (void)
-{
- static GQuark quark;
-
- if (G_UNLIKELY (!quark))
- quark = g_quark_from_static_string ("vte-fontconfig-timestamp");
-
- return quark;
-}
-
-static void
-vte_pango_context_set_fontconfig_timestamp (PangoContext *context,
- guint fontconfig_timestamp)
-{
- g_object_set_qdata ((GObject *) context,
- fontconfig_timestamp_quark (),
- GUINT_TO_POINTER (fontconfig_timestamp));
-}
-
-static guint
-vte_pango_context_get_fontconfig_timestamp (PangoContext *context)
-{
- return GPOINTER_TO_UINT (g_object_get_qdata ((GObject *) context,
- fontconfig_timestamp_quark ()));
-}
-
-static guint
-context_hash (PangoContext *context)
-{
- return pango_units_from_double (pango_cairo_context_get_resolution (context))
- ^ pango_font_description_hash (pango_context_get_font_description (context))
- ^ cairo_font_options_hash (pango_cairo_context_get_font_options (context))
- ^ GPOINTER_TO_UINT (pango_context_get_language (context))
- ^ vte_pango_context_get_fontconfig_timestamp (context);
-}
-
-static gboolean
-context_equal (PangoContext *a,
- PangoContext *b)
-{
- return _vte_double_equal(pango_cairo_context_get_resolution(a), pango_cairo_context_get_resolution
(b))
- && pango_font_description_equal (pango_context_get_font_description (a),
pango_context_get_font_description (b))
- && cairo_font_options_equal (pango_cairo_context_get_font_options (a),
pango_cairo_context_get_font_options (b))
- && pango_context_get_language (a) == pango_context_get_language (b)
- && vte_pango_context_get_fontconfig_timestamp (a) == vte_pango_context_get_fontconfig_timestamp
(b);
-}
-
-// FIXMEchpe return vte::base::RefPtr<FontInfo>
-/* assumes ownership/reference of context */
-FontInfo*
-FontInfo::find_for_context(vte::glib::RefPtr<PangoContext>& context)
-{
- if (G_UNLIKELY (s_font_info_for_context == nullptr))
- s_font_info_for_context = g_hash_table_new((GHashFunc) context_hash, (GEqualFunc)
context_equal);
-
- auto info = reinterpret_cast<FontInfo*>(g_hash_table_lookup(s_font_info_for_context, context.get()));
- if (G_LIKELY(info)) {
- _vte_debug_print (VTE_DEBUG_PANGOCAIRO,
- "vtepangocairo: %p found font_info in cache\n",
- info);
- info = info->ref();
- } else {
- info = new FontInfo{context.release()};
- }
-
- return info;
-}
-
-/* assumes ownership/reference of context */
-FontInfo*
-FontInfo::create_for_context(vte::glib::RefPtr<PangoContext> context,
- PangoFontDescription const* desc,
- PangoLanguage* language,
- guint fontconfig_timestamp)
-{
- if (!PANGO_IS_CAIRO_FONT_MAP(pango_context_get_font_map(context.get()))) {
- /* Ouch, Gtk+ switched over to some drawing system?
- * Lets just create one from the default font map.
- */
- context =
vte::glib::take_ref(pango_font_map_create_context(pango_cairo_font_map_get_default()));
- }
-
- vte_pango_context_set_fontconfig_timestamp(context.get(), fontconfig_timestamp);
-
- pango_context_set_base_dir(context.get(), PANGO_DIRECTION_LTR);
-
- if (desc)
- pango_context_set_font_description(context.get(), desc);
-
- pango_context_set_language(context.get(), language);
-
- /* Make sure our contexts have a font_options set. We use
- * this invariant in our context hash and equal functions.
- */
- if (!pango_cairo_context_get_font_options(context.get())) {
- cairo_font_options_t *font_options;
-
- font_options = cairo_font_options_create ();
- pango_cairo_context_set_font_options(context.get(), font_options);
- cairo_font_options_destroy (font_options);
- }
-
- return find_for_context(context);
-}
-
-FontInfo*
-FontInfo::create_for_screen(GdkScreen* screen,
- PangoFontDescription const* desc,
- PangoLanguage* language)
-{
- auto settings = gtk_settings_get_for_screen(screen);
- auto fontconfig_timestamp = guint{};
- g_object_get (settings, "gtk-fontconfig-timestamp", &fontconfig_timestamp, nullptr);
- return create_for_context(vte::glib::take_ref(gdk_pango_context_get_for_screen(screen)),
- desc, language, fontconfig_timestamp);
-}
-
-FontInfo*
-FontInfo::create_for_widget(GtkWidget* widget,
- PangoFontDescription const* desc)
-{
- auto screen = gtk_widget_get_screen(widget);
- auto language = pango_context_get_language(gtk_widget_get_pango_context(widget));
-
- return create_for_screen(screen, desc, language);
-}
-
-FontInfo::UnistrInfo*
-FontInfo::get_unistr_info(vteunistr c)
-{
- PangoRectangle logical;
- PangoLayoutLine *line;
-
- auto uinfo = find_unistr_info(c);
- if (G_LIKELY (uinfo->coverage() != UnistrInfo::Coverage::UNKNOWN))
- return uinfo;
-
- auto ufi = &uinfo->m_ufi;
-
- g_string_set_size(m_string, 0);
- _vte_unistr_append_to_string(c, m_string);
- pango_layout_set_text(m_layout.get(), m_string->str, m_string->len);
- pango_layout_get_extents(m_layout.get(), NULL, &logical);
-
- uinfo->width = PANGO_PIXELS_CEIL (logical.width);
-
- line = pango_layout_get_line_readonly(m_layout.get(), 0);
-
- uinfo->has_unknown_chars = pango_layout_get_unknown_glyphs_count(m_layout.get()) != 0;
- /* we use PangoLayoutRun rendering unless there is exactly one run in the line. */
- if (G_UNLIKELY (!line || !line->runs || line->runs->next))
- {
- uinfo->set_coverage(UnistrInfo::Coverage::USE_PANGO_LAYOUT_LINE);
-
- ufi->using_pango_layout_line.line = pango_layout_line_ref (line);
- /* we hold a manual reference on layout. pango currently
- * doesn't work if line->layout is NULL. ugh! */
- pango_layout_set_text(m_layout.get(), "", -1); /* make layout disassociate from the line */
- ufi->using_pango_layout_line.line->layout = (PangoLayout *)g_object_ref(m_layout.get());
-
- } else {
- PangoGlyphItem *glyph_item = (PangoGlyphItem *)line->runs->data;
- PangoFont *pango_font = glyph_item->item->analysis.font;
- PangoGlyphString *glyph_string = glyph_item->glyphs;
-
- /* we use fast cairo path if glyph string has only one real
- * glyph and at origin */
- if (!uinfo->has_unknown_chars &&
- glyph_string->num_glyphs == 1 && glyph_string->glyphs[0].glyph <= 0xFFFF &&
- (glyph_string->glyphs[0].geometry.x_offset |
- glyph_string->glyphs[0].geometry.y_offset) == 0)
- {
- cairo_scaled_font_t *scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont
*) pango_font);
-
- if (scaled_font) {
- uinfo->set_coverage(UnistrInfo::Coverage::USE_CAIRO_GLYPH);
-
- ufi->using_cairo_glyph.scaled_font = cairo_scaled_font_reference
(scaled_font);
- ufi->using_cairo_glyph.glyph_index = glyph_string->glyphs[0].glyph;
- }
- }
-
- /* use pango fast path otherwise */
- if (G_UNLIKELY (uinfo->coverage() == UnistrInfo::Coverage::UNKNOWN)) {
- uinfo->set_coverage(UnistrInfo::Coverage::USE_PANGO_GLYPH_STRING);
-
- ufi->using_pango_glyph_string.font = pango_font ? (PangoFont *)g_object_ref
(pango_font) : NULL;
- ufi->using_pango_glyph_string.glyph_string = pango_glyph_string_copy (glyph_string);
- }
- }
-
- /* release internal layout resources */
- pango_layout_set_text(m_layout.get(), "", -1);
-
-#ifdef VTE_DEBUG
- m_coverage_count[0]++;
- m_coverage_count[uinfo->m_coverage]++;
-#endif
-
- return uinfo;
-}
-
DrawingContext::~DrawingContext()
{
clear_font_cache();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]