mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-14 20:51:07 +00:00
7d43e4e56a
This is very rudimentary, but it's a step in the direction of getting text going. There's lots of things missing still.
377 lines
11 KiB
C
377 lines
11 KiB
C
#include "config.h"
|
|
|
|
#include "gskgpudeviceprivate.h"
|
|
|
|
#include "gskgpuframeprivate.h"
|
|
#include "gskgpuuploadopprivate.h"
|
|
|
|
#include "gdk/gdkdisplayprivate.h"
|
|
#include "gdk/gdktextureprivate.h"
|
|
|
|
typedef enum
|
|
{
|
|
GSK_GPU_CACHE_TEXTURE,
|
|
GSK_GPU_CACHE_GLYPH
|
|
} GskGpuCacheType;
|
|
|
|
typedef struct _GskGpuCachedTexture GskGpuCachedTexture;
|
|
typedef struct _GskGpuCachedGlyph GskGpuCachedGlyph;
|
|
typedef struct _GskGpuCacheEntry GskGpuCacheEntry;
|
|
|
|
struct _GskGpuCacheEntry
|
|
{
|
|
GskGpuCacheType type;
|
|
guint64 last_use_timestamp;
|
|
};
|
|
|
|
struct _GskGpuCachedTexture
|
|
{
|
|
GskGpuCacheEntry entry;
|
|
/* atomic */ GdkTexture *texture;
|
|
GskGpuImage *image;
|
|
};
|
|
|
|
struct _GskGpuCachedGlyph
|
|
{
|
|
GskGpuCacheEntry entry;
|
|
PangoFont *font;
|
|
PangoGlyph glyph;
|
|
GskGpuGlyphLookupFlags flags;
|
|
float scale;
|
|
|
|
GskGpuImage *image;
|
|
graphene_rect_t bounds;
|
|
graphene_point_t origin;
|
|
};
|
|
|
|
#define GDK_ARRAY_NAME gsk_gpu_cache_entries
|
|
#define GDK_ARRAY_TYPE_NAME GskGpuCacheEntries
|
|
#define GDK_ARRAY_ELEMENT_TYPE GskGpuCacheEntry *
|
|
#define GDK_ARRAY_PREALLOC 32
|
|
#define GDK_ARRAY_NO_MEMSET 1
|
|
#include "gdk/gdkarrayimpl.c"
|
|
|
|
typedef struct _GskGpuDevicePrivate GskGpuDevicePrivate;
|
|
|
|
struct _GskGpuDevicePrivate
|
|
{
|
|
GdkDisplay *display;
|
|
|
|
GskGpuCacheEntries cache;
|
|
guint cache_gc_source;
|
|
GHashTable *glyph_cache;
|
|
};
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GskGpuDevice, gsk_gpu_device, G_TYPE_OBJECT)
|
|
|
|
static guint
|
|
gsk_gpu_cached_glyph_hash (gconstpointer data)
|
|
{
|
|
const GskGpuCachedGlyph *glyph = data;
|
|
|
|
return GPOINTER_TO_UINT (glyph->font) ^
|
|
glyph->glyph ^
|
|
(glyph->flags << 24) ^
|
|
((guint) glyph->scale * PANGO_SCALE);
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_cached_glyph_equal (gconstpointer v1,
|
|
gconstpointer v2)
|
|
{
|
|
const GskGpuCachedGlyph *glyph1 = v1;
|
|
const GskGpuCachedGlyph *glyph2 = v2;
|
|
|
|
return glyph1->font == glyph2->font
|
|
&& glyph1->glyph == glyph2->glyph
|
|
&& glyph1->flags == glyph2->flags
|
|
&& glyph1->scale == glyph2->scale;
|
|
}
|
|
|
|
void
|
|
gsk_gpu_device_gc (GskGpuDevice *self,
|
|
gint64 timestamp)
|
|
{
|
|
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
|
|
gsize i;
|
|
|
|
for (i = 0; i < gsk_gpu_cache_entries_get_size (&priv->cache); i++)
|
|
{
|
|
GskGpuCacheEntry *entry = gsk_gpu_cache_entries_get (&priv->cache, i);
|
|
|
|
switch (entry->type)
|
|
{
|
|
case GSK_GPU_CACHE_TEXTURE:
|
|
{
|
|
GskGpuCachedTexture *texture = (GskGpuCachedTexture *) entry;
|
|
if (texture->texture != NULL)
|
|
continue;
|
|
|
|
gdk_texture_clear_render_data (texture->texture);
|
|
g_object_unref (texture->image);
|
|
}
|
|
break;
|
|
|
|
case GSK_GPU_CACHE_GLYPH:
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
if (i + 1 != gsk_gpu_cache_entries_get_size (&priv->cache))
|
|
{
|
|
*gsk_gpu_cache_entries_index (&priv->cache, i) = *gsk_gpu_cache_entries_index (&priv->cache, gsk_gpu_cache_entries_get_size (&priv->cache) - 1);
|
|
gsk_gpu_cache_entries_set_size (&priv->cache, gsk_gpu_cache_entries_get_size (&priv->cache) - 1);
|
|
}
|
|
i--;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_device_clear_cache (GskGpuDevice *self)
|
|
{
|
|
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
|
|
gsize i;
|
|
|
|
for (i = 0; i < gsk_gpu_cache_entries_get_size (&priv->cache); i++)
|
|
{
|
|
GskGpuCacheEntry *entry = gsk_gpu_cache_entries_get (&priv->cache, i);
|
|
|
|
switch (entry->type)
|
|
{
|
|
case GSK_GPU_CACHE_TEXTURE:
|
|
{
|
|
GskGpuCachedTexture *texture = (GskGpuCachedTexture *) entry;
|
|
|
|
if (texture->texture)
|
|
gdk_texture_clear_render_data (texture->texture);
|
|
g_object_unref (texture->image);
|
|
}
|
|
break;
|
|
|
|
case GSK_GPU_CACHE_GLYPH:
|
|
{
|
|
GskGpuCachedGlyph *glyph = (GskGpuCachedGlyph *) entry;
|
|
|
|
g_object_unref (glyph->font);
|
|
g_object_unref (glyph->image);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
g_free (entry);
|
|
}
|
|
|
|
gsk_gpu_cache_entries_set_size (&priv->cache, 0);
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_device_dispose (GObject *object)
|
|
{
|
|
GskGpuDevice *self = GSK_GPU_DEVICE (object);
|
|
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
|
|
|
|
g_hash_table_unref (priv->glyph_cache);
|
|
gsk_gpu_device_clear_cache (self);
|
|
gsk_gpu_cache_entries_clear (&priv->cache);
|
|
|
|
G_OBJECT_CLASS (gsk_gpu_device_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_device_finalize (GObject *object)
|
|
{
|
|
GskGpuDevice *self = GSK_GPU_DEVICE (object);
|
|
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
|
|
|
|
g_object_unref (priv->display);
|
|
|
|
G_OBJECT_CLASS (gsk_gpu_device_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_device_class_init (GskGpuDeviceClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = gsk_gpu_device_dispose;
|
|
object_class->finalize = gsk_gpu_device_finalize;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_device_init (GskGpuDevice *self)
|
|
{
|
|
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
|
|
|
|
gsk_gpu_cache_entries_init (&priv->cache);
|
|
priv->glyph_cache = g_hash_table_new (gsk_gpu_cached_glyph_hash,
|
|
gsk_gpu_cached_glyph_equal);
|
|
}
|
|
|
|
void
|
|
gsk_gpu_device_setup (GskGpuDevice *self,
|
|
GdkDisplay *display)
|
|
{
|
|
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
|
|
|
|
priv->display = g_object_ref (display);
|
|
}
|
|
|
|
GdkDisplay *
|
|
gsk_gpu_device_get_display (GskGpuDevice *self)
|
|
{
|
|
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
|
|
|
|
return priv->display;
|
|
}
|
|
|
|
GskGpuImage *
|
|
gsk_gpu_device_create_offscreen_image (GskGpuDevice *self,
|
|
GdkMemoryDepth depth,
|
|
gsize width,
|
|
gsize height)
|
|
{
|
|
return GSK_GPU_DEVICE_GET_CLASS (self)->create_offscreen_image (self, depth, width, height);
|
|
}
|
|
|
|
GskGpuImage *
|
|
gsk_gpu_device_create_upload_image (GskGpuDevice *self,
|
|
GdkMemoryFormat format,
|
|
gsize width,
|
|
gsize height)
|
|
{
|
|
return GSK_GPU_DEVICE_GET_CLASS (self)->create_upload_image (self, format, width, height);
|
|
}
|
|
|
|
GskGpuImage *
|
|
gsk_gpu_device_lookup_texture_image (GskGpuDevice *self,
|
|
GdkTexture *texture,
|
|
gint64 timestamp)
|
|
{
|
|
GskGpuCachedTexture *cache;
|
|
|
|
cache = gdk_texture_get_render_data (texture, self);
|
|
if (cache)
|
|
{
|
|
cache->entry.last_use_timestamp = timestamp;
|
|
return g_object_ref (cache->image);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_device_cache_texture_destroy_cb (gpointer data)
|
|
{
|
|
GskGpuCachedTexture *cache = data;
|
|
|
|
g_atomic_pointer_set (&cache->texture, NULL);
|
|
}
|
|
|
|
void
|
|
gsk_gpu_device_cache_texture_image (GskGpuDevice *self,
|
|
GdkTexture *texture,
|
|
gint64 timestamp,
|
|
GskGpuImage *image)
|
|
{
|
|
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
|
|
GskGpuCachedTexture *cache;
|
|
|
|
cache = g_new (GskGpuCachedTexture, 1);
|
|
|
|
*cache = (GskGpuCachedTexture) {
|
|
.entry = {
|
|
.type = GSK_GPU_CACHE_TEXTURE,
|
|
.last_use_timestamp = timestamp,
|
|
},
|
|
.texture = texture,
|
|
.image = g_object_ref (image)
|
|
};
|
|
|
|
if (gdk_texture_get_render_data (texture, self))
|
|
gdk_texture_clear_render_data (texture);
|
|
gdk_texture_set_render_data (texture, self, cache, gsk_gpu_device_cache_texture_destroy_cb);
|
|
gsk_gpu_cache_entries_append (&priv->cache, (GskGpuCacheEntry *) cache);
|
|
}
|
|
|
|
GskGpuImage *
|
|
gsk_gpu_device_lookup_glyph_image (GskGpuDevice *self,
|
|
GskGpuFrame *frame,
|
|
PangoFont *font,
|
|
PangoGlyph glyph,
|
|
GskGpuGlyphLookupFlags flags,
|
|
float scale,
|
|
graphene_rect_t *out_bounds,
|
|
graphene_point_t *out_origin)
|
|
{
|
|
GskGpuDevicePrivate *priv = gsk_gpu_device_get_instance_private (self);
|
|
GskGpuCachedGlyph lookup = {
|
|
.font = font,
|
|
.glyph = glyph,
|
|
.flags = flags,
|
|
.scale = scale
|
|
};
|
|
GskGpuCachedGlyph *cache;
|
|
PangoRectangle ink_rect;
|
|
graphene_rect_t rect;
|
|
|
|
cache = g_hash_table_lookup (priv->glyph_cache, &lookup);
|
|
if (cache)
|
|
{
|
|
cache->entry.last_use_timestamp = gsk_gpu_frame_get_timestamp (frame);
|
|
*out_bounds = cache->bounds;
|
|
*out_origin = cache->origin;
|
|
return cache->image;
|
|
}
|
|
|
|
cache = g_new (GskGpuCachedGlyph, 1);
|
|
pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
|
|
rect.origin.x = floor (ink_rect.x * scale / PANGO_SCALE) - 1;
|
|
rect.origin.y = floor (ink_rect.y * scale / PANGO_SCALE) - 1;
|
|
rect.size.width = ceil ((ink_rect.x + ink_rect.width) * scale / PANGO_SCALE) - rect.origin.x + 2;
|
|
rect.size.height = ceil ((ink_rect.y + ink_rect.height) * scale / PANGO_SCALE) - rect.origin.y + 2;
|
|
|
|
*cache = (GskGpuCachedGlyph) {
|
|
.entry = {
|
|
.type = GSK_GPU_CACHE_GLYPH,
|
|
.last_use_timestamp = gsk_gpu_frame_get_timestamp (frame),
|
|
},
|
|
.font = g_object_ref (font),
|
|
.glyph = glyph,
|
|
.flags = flags,
|
|
.scale = scale,
|
|
.image = gsk_gpu_device_create_upload_image (self, GDK_MEMORY_DEFAULT, rect.size.width, rect.size.height),
|
|
.bounds = GRAPHENE_RECT_INIT (0, 0, rect.size.width, rect.size.height),
|
|
.origin = GRAPHENE_POINT_INIT (-rect.origin.x + (flags & 3) / 4.f,
|
|
-rect.origin.y + ((flags >> 2) & 3) / 4.f)
|
|
};
|
|
|
|
gsk_gpu_upload_glyph_op (frame,
|
|
cache->image,
|
|
font,
|
|
glyph,
|
|
&(cairo_rectangle_int_t) {
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = rect.size.width,
|
|
.height = rect.size.height
|
|
},
|
|
scale,
|
|
&cache->origin);
|
|
|
|
g_hash_table_insert (priv->glyph_cache, cache, cache);
|
|
gsk_gpu_cache_entries_append (&priv->cache, (GskGpuCacheEntry *) cache);
|
|
|
|
*out_bounds = cache->bounds;
|
|
*out_origin = cache->origin;
|
|
return cache->image;
|
|
}
|
|
|