summaryrefslogtreecommitdiff
path: root/gsk/gl/gskglglyphlibrary.c
diff options
context:
space:
mode:
Diffstat (limited to 'gsk/gl/gskglglyphlibrary.c')
-rw-r--r--gsk/gl/gskglglyphlibrary.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/gsk/gl/gskglglyphlibrary.c b/gsk/gl/gskglglyphlibrary.c
new file mode 100644
index 0000000000..fd4518d341
--- /dev/null
+++ b/gsk/gl/gskglglyphlibrary.c
@@ -0,0 +1,326 @@
+/* gskglglyphlibrary.c
+ *
+ * Copyright 2020 Christian Hergert <chergert@redhat.com>
+ *
+ * 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 2.1 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 Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <gdk/gdkglcontextprivate.h>
+#include <gdk/gdkmemoryformatprivate.h>
+#include <gdk/gdkprofilerprivate.h>
+
+#include "gskglcommandqueueprivate.h"
+#include "gskgldriverprivate.h"
+#include "gskglglyphlibraryprivate.h"
+
+#define MAX_GLYPH_SIZE 128
+
+G_DEFINE_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
+
+GskGLGlyphLibrary *
+gsk_gl_glyph_library_new (GskGLDriver *driver)
+{
+ g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL);
+
+ return g_object_new (GSK_TYPE_GL_GLYPH_LIBRARY,
+ "driver", driver,
+ NULL);
+}
+
+static guint
+gsk_gl_glyph_key_hash (gconstpointer data)
+{
+ const GskGLGlyphKey *key = data;
+
+ /* We do not store the hash within the key because GHashTable will already
+ * store the hash value for us and so this is called only a single time per
+ * cached item. This saves an extra 4 bytes per GskGLGlyphKey which means on
+ * 64-bit, we fit nicely within 2 pointers (the smallest allocation size
+ * for GSlice).
+ */
+
+ return GPOINTER_TO_UINT (key->font) ^
+ key->glyph ^
+ (key->xshift << 24) ^
+ (key->yshift << 26) ^
+ key->scale;
+}
+
+static gboolean
+gsk_gl_glyph_key_equal (gconstpointer v1,
+ gconstpointer v2)
+{
+ return memcmp (v1, v2, sizeof (GskGLGlyphKey)) == 0;
+}
+
+static void
+gsk_gl_glyph_key_free (gpointer data)
+{
+ GskGLGlyphKey *key = data;
+
+ g_clear_object (&key->font);
+ g_slice_free (GskGLGlyphKey, key);
+}
+
+static void
+gsk_gl_glyph_value_free (gpointer data)
+{
+ g_slice_free (GskGLGlyphValue, data);
+}
+
+static void
+gsk_gl_glyph_library_begin_frame (GskGLTextureLibrary *library,
+ gint64 frame_id,
+ GPtrArray *removed_atlases)
+{
+ GskGLGlyphLibrary *self = (GskGLGlyphLibrary *)library;
+
+ memset (self->front, 0, sizeof self->front);
+}
+
+static void
+gsk_gl_glyph_library_finalize (GObject *object)
+{
+ GskGLGlyphLibrary *self = (GskGLGlyphLibrary *)object;
+
+ g_clear_pointer (&self->surface_data, g_free);
+
+ G_OBJECT_CLASS (gsk_gl_glyph_library_parent_class)->finalize (object);
+}
+
+static void
+gsk_gl_glyph_library_class_init (GskGLGlyphLibraryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GskGLTextureLibraryClass *library_class = GSK_GL_TEXTURE_LIBRARY_CLASS (klass);
+
+ object_class->finalize = gsk_gl_glyph_library_finalize;
+
+ library_class->begin_frame = gsk_gl_glyph_library_begin_frame;
+}
+
+static void
+gsk_gl_glyph_library_init (GskGLGlyphLibrary *self)
+{
+ GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
+
+ tl->max_entry_size = MAX_GLYPH_SIZE;
+ gsk_gl_texture_library_set_funcs (tl,
+ gsk_gl_glyph_key_hash,
+ gsk_gl_glyph_key_equal,
+ gsk_gl_glyph_key_free,
+ gsk_gl_glyph_value_free);
+}
+
+static cairo_surface_t *
+gsk_gl_glyph_library_create_surface (GskGLGlyphLibrary *self,
+ int stride,
+ int width,
+ int height,
+ int uwidth,
+ int uheight)
+{
+ cairo_surface_t *surface;
+ gsize n_bytes;
+
+ g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
+ g_assert (width > 0);
+ g_assert (height > 0);
+
+ n_bytes = stride * height;
+
+ if G_LIKELY (n_bytes > self->surface_data_len)
+ {
+ self->surface_data = g_realloc (self->surface_data, n_bytes);
+ self->surface_data_len = n_bytes;
+ }
+
+ memset (self->surface_data, 0, n_bytes);
+ surface = cairo_image_surface_create_for_data (self->surface_data,
+ CAIRO_FORMAT_ARGB32,
+ width, height, stride);
+ cairo_surface_set_device_scale (surface, width / (double)uwidth, height / (double)uheight);
+
+ return surface;
+}
+
+static void
+render_glyph (cairo_surface_t *surface,
+ const cairo_scaled_font_t *scaled_font,
+ const GskGLGlyphKey *key,
+ const GskGLGlyphValue *value)
+{
+ cairo_t *cr;
+ cairo_glyph_t glyph;
+
+ g_assert (surface != NULL);
+ g_assert (scaled_font != NULL);
+
+ cr = cairo_create (surface);
+ cairo_set_scaled_font (cr, scaled_font);
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+
+ glyph.index = key->glyph;
+ glyph.x = 0.25 * key->xshift - value->ink_rect.x;
+ glyph.y = 0.25 * key->yshift - value->ink_rect.y;
+
+ cairo_show_glyphs (cr, &glyph, 1);
+ cairo_destroy (cr);
+
+ cairo_surface_flush (surface);
+}
+
+static void
+gsk_gl_glyph_library_upload_glyph (GskGLGlyphLibrary *self,
+ const GskGLGlyphKey *key,
+ const GskGLGlyphValue *value,
+ int x,
+ int y,
+ int width,
+ int height,
+ int uwidth,
+ int uheight)
+{
+ GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
+ G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
+ cairo_scaled_font_t *scaled_font;
+ cairo_surface_t *surface;
+ guchar *pixel_data;
+ guchar *free_data = NULL;
+ guint gl_format;
+ guint gl_type;
+ guint texture_id;
+ gsize stride;
+
+ g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
+ g_assert (key != NULL);
+ g_assert (value != NULL);
+
+ scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)key->font);
+ if G_UNLIKELY (scaled_font == NULL ||
+ cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS)
+ return;
+
+ stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width);
+
+ gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
+ "Uploading glyph %d",
+ key->glyph);
+
+ surface = gsk_gl_glyph_library_create_surface (self, stride, width, height, uwidth, uheight);
+ render_glyph (surface, scaled_font, key, value);
+
+ texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (value);
+
+ g_assert (texture_id > 0);
+
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / 4);
+ glBindTexture (GL_TEXTURE_2D, texture_id);
+
+ if G_UNLIKELY (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
+ {
+ pixel_data = free_data = g_malloc (width * height * 4);
+ gdk_memory_convert (pixel_data,
+ width * 4,
+ GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
+ cairo_image_surface_get_data (surface),
+ width * 4,
+ GDK_MEMORY_DEFAULT,
+ width, height);
+ gl_format = GL_RGBA;
+ gl_type = GL_UNSIGNED_BYTE;
+ }
+ else
+ {
+ pixel_data = cairo_image_surface_get_data (surface);
+ gl_format = GL_BGRA;
+ gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ }
+
+ glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, width, height,
+ gl_format, gl_type, pixel_data);
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+
+ cairo_surface_destroy (surface);
+ g_free (free_data);
+
+ gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
+
+ tl->driver->command_queue->n_uploads++;
+
+ if (gdk_profiler_is_running ())
+ {
+ char message[64];
+ g_snprintf (message, sizeof message, "Size %dx%d", width, height);
+ gdk_profiler_add_mark (start_time, GDK_PROFILER_CURRENT_TIME-start_time, "Upload Glyph", message);
+ }
+}
+
+gboolean
+gsk_gl_glyph_library_add (GskGLGlyphLibrary *self,
+ GskGLGlyphKey *key,
+ const GskGLGlyphValue **out_value)
+{
+ GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
+ PangoRectangle ink_rect;
+ GskGLGlyphValue *value;
+ int width;
+ int height;
+ guint packed_x;
+ guint packed_y;
+
+ g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
+ g_assert (key != NULL);
+ g_assert (out_value != NULL);
+
+ pango_font_get_glyph_extents (key->font, key->glyph, &ink_rect, NULL);
+ pango_extents_to_pixels (&ink_rect, NULL);
+
+ ink_rect.x -= 1;
+ ink_rect.width += 2;
+ ink_rect.y -= 1;
+ ink_rect.height += 2;
+
+ width = (int) ceil (ink_rect.width * key->scale / 1024.0);
+ height = (int) ceil (ink_rect.height * key->scale / 1024.0);
+
+ value = gsk_gl_texture_library_pack (tl,
+ key,
+ sizeof *value,
+ width,
+ height,
+ 1,
+ &packed_x, &packed_y);
+
+ memcpy (&value->ink_rect, &ink_rect, sizeof ink_rect);
+
+ if (key->scale > 0 && width > 0 && height > 0)
+ gsk_gl_glyph_library_upload_glyph (self,
+ key,
+ value,
+ packed_x + 1,
+ packed_y + 1,
+ width,
+ height,
+ ink_rect.width,
+ ink_rect.height);
+
+ *out_value = value;
+
+ return GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (value) != 0;
+}