Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1675bfbc8 | ||
|
|
6d606b19be | ||
|
|
07019887be | ||
|
|
0d52772f68 | ||
|
|
269918b0f6 | ||
|
|
1bedfe3dcd | ||
|
|
1854f4d21a | ||
|
|
09d0f4289e | ||
|
|
a227b4f69d | ||
|
|
e881918fea | ||
|
|
06adb709a1 | ||
|
|
1a8db5f112 | ||
|
|
526f460d6b | ||
|
|
80b6a89a32 | ||
|
|
0c192288cd | ||
|
|
d34035c22c | ||
|
|
1f7588087a | ||
|
|
bef8d09040 | ||
|
|
6f1f1a7c89 | ||
|
|
4f5a2d7490 | ||
|
|
8a8e165560 | ||
|
|
0ec623da09 | ||
|
|
3b147ad630 | ||
|
|
615cf2a782 | ||
|
|
f9725cc536 | ||
|
|
468863a8b9 |
@@ -512,7 +512,7 @@ load_file (const char *demoname,
|
||||
|
||||
info_buffer = gtk_text_buffer_new (NULL);
|
||||
gtk_text_buffer_create_tag (info_buffer, "title",
|
||||
"font", "Sans 18",
|
||||
"size", 18 * 1024,
|
||||
"pixels-below-lines", 10,
|
||||
NULL);
|
||||
|
||||
|
||||
@@ -282,7 +282,7 @@ snapshot_uniforms (GskGLUniformState *state,
|
||||
{
|
||||
const GskGLUniformMapping *mapping = &program->mappings[i];
|
||||
|
||||
if (!mapping->info.initial && mapping->location > -1)
|
||||
if (!mapping->info.initial && mapping->info.format && mapping->location > -1)
|
||||
{
|
||||
uniform[count].location = mapping->location;
|
||||
uniform[count].info = mapping->info;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "gskglcommandqueueprivate.h"
|
||||
#include "gskglcompilerprivate.h"
|
||||
#include "gskglglyphlibraryprivate.h"
|
||||
#include "gskglglyphylibraryprivate.h"
|
||||
#include "gskgliconlibraryprivate.h"
|
||||
#include "gskglprogramprivate.h"
|
||||
#include "gskglshadowlibraryprivate.h"
|
||||
@@ -44,9 +45,6 @@
|
||||
#include <gdk/gdkprofilerprivate.h>
|
||||
#include <gdk/gdktextureprivate.h>
|
||||
|
||||
#define ATLAS_SIZE 512
|
||||
#define MAX_OLD_RATIO 0.5
|
||||
|
||||
G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
|
||||
|
||||
static guint
|
||||
@@ -156,50 +154,6 @@ gsk_gl_driver_collect_unused_textures (GskGLDriver *self,
|
||||
return collected;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_texture_atlas_free (GskGLTextureAtlas *atlas)
|
||||
{
|
||||
if (atlas->texture_id != 0)
|
||||
{
|
||||
glDeleteTextures (1, &atlas->texture_id);
|
||||
atlas->texture_id = 0;
|
||||
}
|
||||
|
||||
g_clear_pointer (&atlas->nodes, g_free);
|
||||
g_slice_free (GskGLTextureAtlas, atlas);
|
||||
}
|
||||
|
||||
GskGLTextureAtlas *
|
||||
gsk_gl_driver_create_atlas (GskGLDriver *self)
|
||||
{
|
||||
GskGLTextureAtlas *atlas;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL);
|
||||
|
||||
atlas = g_slice_new0 (GskGLTextureAtlas);
|
||||
atlas->width = ATLAS_SIZE;
|
||||
atlas->height = ATLAS_SIZE;
|
||||
/* TODO: We might want to change the strategy about the amount of
|
||||
* nodes here? stb_rect_pack.h says width is optimal. */
|
||||
atlas->nodes = g_malloc0_n (atlas->width, sizeof (struct stbrp_node));
|
||||
stbrp_init_target (&atlas->context, atlas->width, atlas->height, atlas->nodes, atlas->width);
|
||||
atlas->texture_id = gsk_gl_command_queue_create_texture (self->command_queue,
|
||||
atlas->width,
|
||||
atlas->height,
|
||||
GL_RGBA8,
|
||||
GL_LINEAR,
|
||||
GL_LINEAR);
|
||||
|
||||
gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
|
||||
GL_TEXTURE, atlas->texture_id,
|
||||
"Texture atlas %d",
|
||||
atlas->texture_id);
|
||||
|
||||
g_ptr_array_add (self->atlases, atlas);
|
||||
|
||||
return atlas;
|
||||
}
|
||||
|
||||
static void
|
||||
remove_program (gpointer data)
|
||||
{
|
||||
@@ -226,6 +180,29 @@ gsk_gl_driver_shader_weak_cb (gpointer data,
|
||||
g_hash_table_remove (self->shader_cache, where_object_was);
|
||||
}
|
||||
|
||||
G_GNUC_NULL_TERMINATED static inline GBytes *
|
||||
join_sources (GBytes *first_bytes,
|
||||
...)
|
||||
{
|
||||
GByteArray *byte_array = g_byte_array_new ();
|
||||
GBytes *bytes = first_bytes;
|
||||
va_list args;
|
||||
|
||||
va_start (args, first_bytes);
|
||||
while (bytes != NULL)
|
||||
{
|
||||
gsize len;
|
||||
const guint8 *data = g_bytes_get_data (bytes, &len);
|
||||
if (len > 0)
|
||||
g_byte_array_append (byte_array, data, len);
|
||||
g_bytes_unref (bytes);
|
||||
bytes = va_arg (args, GBytes *);
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
return g_byte_array_free_to_bytes (byte_array);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_driver_dispose (GObject *object)
|
||||
{
|
||||
@@ -235,6 +212,10 @@ gsk_gl_driver_dispose (GObject *object)
|
||||
g_assert (self->in_frame == FALSE);
|
||||
|
||||
#define GSK_GL_NO_UNIFORMS
|
||||
#define GSK_GL_SHADER_RESOURCE(name)
|
||||
#define GSK_GL_SHADER_STRING(str)
|
||||
#define GSK_GL_SHADER_SINGLE(name)
|
||||
#define GSK_GL_SHADER_JOINED(kind, ...)
|
||||
#define GSK_GL_ADD_UNIFORM(pos, KEY, name)
|
||||
#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) \
|
||||
GSK_GL_DELETE_PROGRAM(name); \
|
||||
@@ -248,6 +229,10 @@ gsk_gl_driver_dispose (GObject *object)
|
||||
} G_STMT_END;
|
||||
# include "gskglprograms.defs"
|
||||
#undef GSK_GL_NO_UNIFORMS
|
||||
#undef GSK_GL_SHADER_RESOURCE
|
||||
#undef GSK_GL_SHADER_STRING
|
||||
#undef GSK_GL_SHADER_SINGLE
|
||||
#undef GSK_GL_SHADER_JOINED
|
||||
#undef GSK_GL_ADD_UNIFORM
|
||||
#undef GSK_GL_DEFINE_PROGRAM
|
||||
|
||||
@@ -289,11 +274,11 @@ gsk_gl_driver_dispose (GObject *object)
|
||||
g_assert (!self->texture_id_to_key || g_hash_table_size (self->texture_id_to_key) == 0);
|
||||
g_assert (!self->key_to_texture_id|| g_hash_table_size (self->key_to_texture_id) == 0);
|
||||
|
||||
g_clear_object (&self->glyphs);
|
||||
g_clear_object (&self->icons);
|
||||
g_clear_object (&self->shadows);
|
||||
g_clear_object (&self->glyphs_library);
|
||||
g_clear_object (&self->glyphy_library);
|
||||
g_clear_object (&self->icons_library);
|
||||
g_clear_object (&self->shadows_library);
|
||||
|
||||
g_clear_pointer (&self->atlases, g_ptr_array_unref);
|
||||
g_clear_pointer (&self->autorelease_framebuffers, g_array_unref);
|
||||
g_clear_pointer (&self->key_to_texture_id, g_hash_table_unref);
|
||||
g_clear_pointer (&self->textures, g_hash_table_unref);
|
||||
@@ -330,7 +315,6 @@ gsk_gl_driver_init (GskGLDriver *self)
|
||||
self->shader_cache = g_hash_table_new_full (NULL, NULL, NULL, remove_program);
|
||||
self->texture_pool = g_array_new (FALSE, FALSE, sizeof (guint));
|
||||
self->render_targets = g_ptr_array_new ();
|
||||
self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -348,14 +332,14 @@ gsk_gl_driver_load_programs (GskGLDriver *self,
|
||||
|
||||
/* Setup preambles that are shared by all shaders */
|
||||
gsk_gl_compiler_set_preamble_from_resource (compiler,
|
||||
GSK_GL_COMPILER_ALL,
|
||||
"/org/gtk/libgsk/gl/preamble.glsl");
|
||||
GSK_GL_COMPILER_ALL,
|
||||
"/org/gtk/libgsk/gl/preamble.glsl");
|
||||
gsk_gl_compiler_set_preamble_from_resource (compiler,
|
||||
GSK_GL_COMPILER_VERTEX,
|
||||
"/org/gtk/libgsk/gl/preamble.vs.glsl");
|
||||
GSK_GL_COMPILER_VERTEX,
|
||||
"/org/gtk/libgsk/gl/preamble.vs.glsl");
|
||||
gsk_gl_compiler_set_preamble_from_resource (compiler,
|
||||
GSK_GL_COMPILER_FRAGMENT,
|
||||
"/org/gtk/libgsk/gl/preamble.fs.glsl");
|
||||
GSK_GL_COMPILER_FRAGMENT,
|
||||
"/org/gtk/libgsk/gl/preamble.fs.glsl");
|
||||
|
||||
/* Setup attributes that are provided via VBO */
|
||||
gsk_gl_compiler_bind_attribute (compiler, "aPosition", 0);
|
||||
@@ -365,10 +349,28 @@ gsk_gl_driver_load_programs (GskGLDriver *self,
|
||||
|
||||
/* Use XMacros to register all of our programs and their uniforms */
|
||||
#define GSK_GL_NO_UNIFORMS
|
||||
#define GSK_GL_SHADER_RESOURCE(name) \
|
||||
g_bytes_ref(g_resources_lookup_data("/org/gtk/libgsk/gl/" name, 0, NULL))
|
||||
#define GSK_GL_SHADER_STRING(str) \
|
||||
g_bytes_new_static(str, strlen(str))
|
||||
#define GSK_GL_SHADER_SINGLE(bytes) \
|
||||
G_STMT_START { \
|
||||
GBytes *b = bytes; \
|
||||
gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_ALL, b); \
|
||||
g_bytes_unref (b); \
|
||||
} G_STMT_END;
|
||||
#define GSK_GL_SHADER_JOINED(kind, ...) \
|
||||
G_STMT_START { \
|
||||
GBytes *bytes = join_sources(__VA_ARGS__); \
|
||||
gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_##kind, bytes); \
|
||||
g_bytes_unref (bytes); \
|
||||
} G_STMT_END;
|
||||
#define GSK_GL_ADD_UNIFORM(pos, KEY, name) \
|
||||
gsk_gl_program_add_uniform (program, #name, UNIFORM_##KEY);
|
||||
#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) \
|
||||
gsk_gl_compiler_set_source_from_resource (compiler, GSK_GL_COMPILER_ALL, resource); \
|
||||
#define GSK_GL_DEFINE_PROGRAM(name, sources, uniforms) \
|
||||
gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_VERTEX, NULL); \
|
||||
gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_FRAGMENT, NULL); \
|
||||
sources \
|
||||
GSK_GL_COMPILE_PROGRAM(name ## _no_clip, uniforms, "#define NO_CLIP 1\n"); \
|
||||
GSK_GL_COMPILE_PROGRAM(name ## _rect_clip, uniforms, "#define RECT_CLIP 1\n"); \
|
||||
GSK_GL_COMPILE_PROGRAM(name, uniforms, "");
|
||||
@@ -401,6 +403,11 @@ gsk_gl_driver_load_programs (GskGLDriver *self,
|
||||
#undef GSK_GL_DEFINE_PROGRAM_CLIP
|
||||
#undef GSK_GL_DEFINE_PROGRAM
|
||||
#undef GSK_GL_ADD_UNIFORM
|
||||
#undef GSK_GL_SHADER_SINGLE
|
||||
#undef GSK_GL_SHADER_JOINED
|
||||
#undef GSK_GL_SHADER_RESOURCE
|
||||
#undef GSK_GL_SHADER_STRING
|
||||
#undef GSK_GL_NO_UNIFORMS
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
@@ -456,9 +463,10 @@ gsk_gl_driver_new (GskGLCommandQueue *command_queue,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->glyphs = gsk_gl_glyph_library_new (self);
|
||||
self->icons = gsk_gl_icon_library_new (self);
|
||||
self->shadows = gsk_gl_shadow_library_new (self);
|
||||
self->glyphs_library = gsk_gl_glyph_library_new (self);
|
||||
self->glyphy_library = gsk_gl_glyphy_library_new (self);
|
||||
self->icons_library = gsk_gl_icon_library_new (self);
|
||||
self->shadows_library = gsk_gl_shadow_library_new (self);
|
||||
|
||||
gdk_profiler_end_mark (before, "create GskGLDriver", NULL);
|
||||
|
||||
@@ -518,37 +526,6 @@ failure:
|
||||
return g_steal_pointer (&driver);
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
gsk_gl_driver_compact_atlases (GskGLDriver *self)
|
||||
{
|
||||
GPtrArray *removed = NULL;
|
||||
|
||||
g_assert (GSK_IS_GL_DRIVER (self));
|
||||
|
||||
for (guint i = self->atlases->len; i > 0; i--)
|
||||
{
|
||||
GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i - 1);
|
||||
|
||||
if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO)
|
||||
{
|
||||
GSK_NOTE (GLYPH_CACHE,
|
||||
g_message ("Dropping atlas %d (%g.2%% old)", i,
|
||||
100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas)));
|
||||
if (removed == NULL)
|
||||
removed = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
|
||||
g_ptr_array_add (removed, g_ptr_array_steal_index (self->atlases, i - 1));
|
||||
}
|
||||
}
|
||||
|
||||
GSK_NOTE (GLYPH_CACHE, {
|
||||
static guint timestamp;
|
||||
if (timestamp++ % 60 == 0)
|
||||
g_message ("%d atlases", self->atlases->len);
|
||||
});
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_gl_driver_begin_frame:
|
||||
* @self: a `GskGLDriver`
|
||||
@@ -565,7 +542,6 @@ gsk_gl_driver_begin_frame (GskGLDriver *self,
|
||||
GskGLCommandQueue *command_queue)
|
||||
{
|
||||
gint64 last_frame_id;
|
||||
GPtrArray *removed;
|
||||
|
||||
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
||||
g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (command_queue));
|
||||
@@ -580,19 +556,16 @@ gsk_gl_driver_begin_frame (GskGLDriver *self,
|
||||
|
||||
gsk_gl_command_queue_begin_frame (self->command_queue);
|
||||
|
||||
/* Compact atlases with too many freed pixels */
|
||||
removed = gsk_gl_driver_compact_atlases (self);
|
||||
|
||||
/* Mark unused pixel regions of the atlases */
|
||||
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->icons),
|
||||
self->current_frame_id,
|
||||
removed);
|
||||
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphs),
|
||||
self->current_frame_id,
|
||||
removed);
|
||||
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->icons_library),
|
||||
self->current_frame_id);
|
||||
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphs_library),
|
||||
self->current_frame_id);
|
||||
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphy_library),
|
||||
self->current_frame_id);
|
||||
|
||||
/* Cleanup old shadows */
|
||||
gsk_gl_shadow_library_begin_frame (self->shadows);
|
||||
gsk_gl_shadow_library_begin_frame (self->shadows_library);
|
||||
|
||||
/* Remove all textures that are from a previous frame or are no
|
||||
* longer used by linked GdkTexture. We do this at the beginning
|
||||
@@ -600,9 +573,6 @@ gsk_gl_driver_begin_frame (GskGLDriver *self,
|
||||
* we block on any resources while delivering our frames.
|
||||
*/
|
||||
gsk_gl_driver_collect_unused_textures (self, last_frame_id - 1);
|
||||
|
||||
/* Now free atlas textures */
|
||||
g_clear_pointer (&removed, g_ptr_array_unref);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1182,14 +1152,23 @@ void
|
||||
gsk_gl_driver_save_atlases_to_png (GskGLDriver *self,
|
||||
const char *directory)
|
||||
{
|
||||
GPtrArray *atlases;
|
||||
|
||||
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
||||
|
||||
if (directory == NULL)
|
||||
directory = ".";
|
||||
|
||||
for (guint i = 0; i < self->atlases->len; i++)
|
||||
#define copy_atlases(dst, library) \
|
||||
g_ptr_array_extend(dst, GSK_GL_TEXTURE_LIBRARY(library)->atlases, NULL, NULL)
|
||||
atlases = g_ptr_array_new ();
|
||||
copy_atlases (atlases, self->glyphs_library);
|
||||
copy_atlases (atlases, self->icons_library);
|
||||
#undef copy_atlases
|
||||
|
||||
for (guint i = 0; i < atlases->len; i++)
|
||||
{
|
||||
GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i);
|
||||
GskGLTextureAtlas *atlas = g_ptr_array_index (atlases, i);
|
||||
char *filename = g_strdup_printf ("%s%sframe-%d-atlas-%d.png",
|
||||
directory,
|
||||
G_DIR_SEPARATOR_S,
|
||||
@@ -1198,6 +1177,8 @@ gsk_gl_driver_save_atlases_to_png (GskGLDriver *self,
|
||||
write_atlas_to_png (self, atlas, filename);
|
||||
g_free (filename);
|
||||
}
|
||||
|
||||
g_ptr_array_unref (atlases);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -100,17 +100,16 @@ struct _GskGLDriver
|
||||
GskGLCommandQueue *shared_command_queue;
|
||||
GskGLCommandQueue *command_queue;
|
||||
|
||||
GskGLGlyphLibrary *glyphs;
|
||||
GskGLIconLibrary *icons;
|
||||
GskGLShadowLibrary *shadows;
|
||||
GskGLGlyphLibrary *glyphs_library;
|
||||
GskGLGlyphyLibrary *glyphy_library;
|
||||
GskGLIconLibrary *icons_library;
|
||||
GskGLShadowLibrary *shadows_library;
|
||||
|
||||
GArray *texture_pool;
|
||||
GHashTable *textures;
|
||||
GHashTable *key_to_texture_id;
|
||||
GHashTable *texture_id_to_key;
|
||||
|
||||
GPtrArray *atlases;
|
||||
|
||||
GHashTable *shader_cache;
|
||||
|
||||
GArray *autorelease_framebuffers;
|
||||
@@ -184,7 +183,6 @@ void gsk_gl_driver_add_texture_slices (GskGLDriver *s
|
||||
GskGLProgram * gsk_gl_driver_lookup_shader (GskGLDriver *self,
|
||||
GskGLShader *shader,
|
||||
GError **error);
|
||||
GskGLTextureAtlas * gsk_gl_driver_create_atlas (GskGLDriver *self);
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
void gsk_gl_driver_save_atlases_to_png (GskGLDriver *self,
|
||||
|
||||
@@ -84,15 +84,64 @@ gsk_gl_glyph_value_free (gpointer data)
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyph_library_begin_frame (GskGLTextureLibrary *library,
|
||||
gint64 frame_id,
|
||||
GPtrArray *removed_atlases)
|
||||
gsk_gl_glyph_library_clear_cache (GskGLTextureLibrary *library)
|
||||
{
|
||||
GskGLGlyphLibrary *self = (GskGLGlyphLibrary *)library;
|
||||
|
||||
g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
|
||||
|
||||
memset (self->front, 0, sizeof self->front);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyph_library_init_atlas (GskGLTextureLibrary *self,
|
||||
GskGLTextureAtlas *atlas)
|
||||
{
|
||||
gboolean packed G_GNUC_UNUSED;
|
||||
int x, y;
|
||||
guint gl_format;
|
||||
guint gl_type;
|
||||
guint8 pixel_data[4 * 3 * 3];
|
||||
|
||||
g_assert (GSK_IS_GL_GLYPH_LIBRARY (self));
|
||||
g_assert (atlas != NULL);
|
||||
|
||||
/* Insert a single pixel at 0,0 for use in coloring */
|
||||
|
||||
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
|
||||
"Initializing Atlas");
|
||||
|
||||
packed = gsk_gl_texture_library_allocate (self, atlas, 3, 3, &x, &y);
|
||||
g_assert (packed);
|
||||
g_assert (x == 0 && y == 0);
|
||||
|
||||
memset (pixel_data, 255, sizeof pixel_data);
|
||||
|
||||
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
|
||||
{
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gl_format = GL_BGRA;
|
||||
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
}
|
||||
|
||||
glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
|
||||
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
0, 0,
|
||||
3, 3,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
|
||||
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
|
||||
|
||||
self->driver->command_queue->n_uploads++;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gsk_gl_glyph_library_finalize (GObject *object)
|
||||
{
|
||||
@@ -111,7 +160,8 @@ gsk_gl_glyph_library_class_init (GskGLGlyphLibraryClass *klass)
|
||||
|
||||
object_class->finalize = gsk_gl_glyph_library_finalize;
|
||||
|
||||
library_class->begin_frame = gsk_gl_glyph_library_begin_frame;
|
||||
library_class->clear_cache = gsk_gl_glyph_library_clear_cache;
|
||||
library_class->init_atlas = gsk_gl_glyph_library_init_atlas;
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
523
gsk/gl/gskglglyphylibrary.c
Normal file
523
gsk/gl/gskglglyphylibrary.c
Normal file
@@ -0,0 +1,523 @@
|
||||
/* gskglglyphylibrary.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
|
||||
*/
|
||||
|
||||
/* Some of the glyphy cache is based upon the original glyphy code.
|
||||
* It's license is provided below.
|
||||
*/
|
||||
/*
|
||||
* Copyright 2012 Google, Inc. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Google Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gdk/gdkglcontextprivate.h>
|
||||
#include <gdk/gdkmemoryformatprivate.h>
|
||||
#include <gdk/gdkprofilerprivate.h>
|
||||
|
||||
#include "gskglcommandqueueprivate.h"
|
||||
#include "gskgldriverprivate.h"
|
||||
#include "gskglglyphylibraryprivate.h"
|
||||
|
||||
#include "gskpath.h"
|
||||
|
||||
#include <glyphy.h>
|
||||
|
||||
#define TOLERANCE (1/2048.)
|
||||
#define MIN_FONT_SIZE 14
|
||||
#define GRID_SIZE 20 /* Per EM */
|
||||
#define ENLIGHTEN_MAX .01 /* Per EM */
|
||||
#define EMBOLDEN_MAX .024 /* Per EM */
|
||||
#define ITEM_W 64
|
||||
#define ITEM_H_QUANTUM 8
|
||||
|
||||
G_DEFINE_TYPE (GskGLGlyphyLibrary, gsk_gl_glyphy_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
|
||||
|
||||
GskGLGlyphyLibrary *
|
||||
gsk_gl_glyphy_library_new (GskGLDriver *driver)
|
||||
{
|
||||
g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL);
|
||||
|
||||
return g_object_new (GSK_TYPE_GL_GLYPHY_LIBRARY,
|
||||
"driver", driver,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static guint
|
||||
gsk_gl_glyphy_key_hash (gconstpointer data)
|
||||
{
|
||||
const GskGLGlyphyKey *key = data;
|
||||
|
||||
/* malloc()'d pointers already guarantee 3 bits from the LSB on 64-bit and
|
||||
* 2 bits from the LSB on 32-bit. Shift by enough to give us 256 entries
|
||||
* in our front cache for the glyph since languages will naturally cluster
|
||||
* for us.
|
||||
*/
|
||||
|
||||
return (key->font << 8) ^ key->glyph;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_gl_glyphy_key_equal (gconstpointer v1,
|
||||
gconstpointer v2)
|
||||
{
|
||||
return memcmp (v1, v2, sizeof (GskGLGlyphyKey)) == 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_key_free (gpointer data)
|
||||
{
|
||||
GskGLGlyphyKey *key = data;
|
||||
|
||||
g_slice_free (GskGLGlyphyKey, key);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_value_free (gpointer data)
|
||||
{
|
||||
g_slice_free (GskGLGlyphyValue, data);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_library_clear_cache (GskGLTextureLibrary *library)
|
||||
{
|
||||
GskGLGlyphyLibrary *self = (GskGLGlyphyLibrary *)library;
|
||||
|
||||
g_assert (GSK_IS_GL_GLYPHY_LIBRARY (self));
|
||||
|
||||
memset (self->front, 0, sizeof self->front);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_library_init_atlas (GskGLTextureLibrary *library,
|
||||
GskGLTextureAtlas *atlas)
|
||||
{
|
||||
g_assert (GSK_IS_GL_GLYPHY_LIBRARY (library));
|
||||
g_assert (atlas != NULL);
|
||||
|
||||
atlas->cursor_x = 0;
|
||||
atlas->cursor_y = 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_gl_glyphy_library_allocate (GskGLTextureLibrary *library,
|
||||
GskGLTextureAtlas *atlas,
|
||||
int width,
|
||||
int height,
|
||||
int *out_x,
|
||||
int *out_y)
|
||||
{
|
||||
GskGLGlyphyLibrary *self = (GskGLGlyphyLibrary *)library;
|
||||
int cursor_save_x;
|
||||
int cursor_save_y;
|
||||
|
||||
g_assert (GSK_IS_GL_GLYPHY_LIBRARY (self));
|
||||
g_assert (atlas != NULL);
|
||||
|
||||
cursor_save_x = atlas->cursor_x;
|
||||
cursor_save_y = atlas->cursor_y;
|
||||
|
||||
if ((height & (self->item_h_q-1)) != 0)
|
||||
height = (height + self->item_h_q - 1) & ~(self->item_h_q - 1);
|
||||
|
||||
/* Require allocations in columns of 64 and rows of 8 */
|
||||
g_assert (width == self->item_w);
|
||||
g_assert ((height % self->item_h_q) == 0);
|
||||
|
||||
if (atlas->cursor_y + height > atlas->height)
|
||||
{
|
||||
/* Go to next column */
|
||||
atlas->cursor_x += self->item_w;
|
||||
atlas->cursor_y = 0;
|
||||
}
|
||||
|
||||
if (atlas->cursor_x + width <= atlas->width &&
|
||||
atlas->cursor_y + height <= atlas->height)
|
||||
{
|
||||
*out_x = atlas->cursor_x;
|
||||
*out_y = atlas->cursor_y;
|
||||
atlas->cursor_y += height;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
atlas->cursor_x = cursor_save_x;
|
||||
atlas->cursor_y = cursor_save_y;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_library_finalize (GObject *object)
|
||||
{
|
||||
GskGLGlyphyLibrary *self = (GskGLGlyphyLibrary *)object;
|
||||
|
||||
g_clear_pointer (&self->acc, glyphy_arc_accumulator_destroy);
|
||||
g_clear_pointer (&self->acc_endpoints, g_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (gsk_gl_glyphy_library_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
GQuark quark_glyphy_font_key;
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_library_class_init (GskGLGlyphyLibraryClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GskGLTextureLibraryClass *library_class = GSK_GL_TEXTURE_LIBRARY_CLASS (klass);
|
||||
|
||||
quark_glyphy_font_key = g_quark_from_static_string ("glyphy-font-key");
|
||||
|
||||
object_class->finalize = gsk_gl_glyphy_library_finalize;
|
||||
|
||||
library_class->allocate = gsk_gl_glyphy_library_allocate;
|
||||
library_class->clear_cache = gsk_gl_glyphy_library_clear_cache;
|
||||
library_class->init_atlas = gsk_gl_glyphy_library_init_atlas;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_library_init (GskGLGlyphyLibrary *self)
|
||||
{
|
||||
GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
|
||||
|
||||
tl->max_entry_size = 0;
|
||||
tl->max_frame_age = 512;
|
||||
tl->atlas_width = 2048;
|
||||
tl->atlas_height = 1024;
|
||||
gsk_gl_texture_library_set_funcs (tl,
|
||||
gsk_gl_glyphy_key_hash,
|
||||
gsk_gl_glyphy_key_equal,
|
||||
gsk_gl_glyphy_key_free,
|
||||
gsk_gl_glyphy_value_free);
|
||||
|
||||
self->acc = glyphy_arc_accumulator_create ();
|
||||
self->acc_endpoints = g_array_new (FALSE, FALSE, sizeof (glyphy_arc_endpoint_t));
|
||||
self->item_w = ITEM_W;
|
||||
self->item_h_q = ITEM_H_QUANTUM;
|
||||
}
|
||||
|
||||
static glyphy_bool_t
|
||||
accumulate_endpoint (glyphy_arc_endpoint_t *endpoint,
|
||||
GArray *endpoints)
|
||||
{
|
||||
g_array_append_vals (endpoints, endpoint, 1);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
move_to (hb_draw_funcs_t *dfuncs,
|
||||
GskPathBuilder *builder,
|
||||
hb_draw_state_t *st,
|
||||
float x,
|
||||
float y,
|
||||
void *data)
|
||||
{
|
||||
gsk_path_builder_move_to (builder, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
line_to (hb_draw_funcs_t *dfuncs,
|
||||
GskPathBuilder *builder,
|
||||
hb_draw_state_t *st,
|
||||
float x,
|
||||
float y,
|
||||
void *data)
|
||||
{
|
||||
gsk_path_builder_line_to (builder, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
cubic_to (hb_draw_funcs_t *dfuncs,
|
||||
GskPathBuilder *builder,
|
||||
hb_draw_state_t *st,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float x3,
|
||||
float y3,
|
||||
void *data)
|
||||
{
|
||||
gsk_path_builder_curve_to (builder, x1, y1, x2, y2, x3, y3);
|
||||
}
|
||||
|
||||
static void
|
||||
close_path (hb_draw_funcs_t *dfuncs,
|
||||
GskPathBuilder *builder,
|
||||
hb_draw_state_t *st,
|
||||
void *data)
|
||||
{
|
||||
gsk_path_builder_close (builder);
|
||||
}
|
||||
|
||||
static hb_draw_funcs_t *
|
||||
gsk_path_get_draw_funcs (void)
|
||||
{
|
||||
static hb_draw_funcs_t *funcs = NULL;
|
||||
|
||||
if (!funcs)
|
||||
{
|
||||
funcs = hb_draw_funcs_create ();
|
||||
|
||||
hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) move_to, NULL, NULL);
|
||||
hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) line_to, NULL, NULL);
|
||||
hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) cubic_to, NULL, NULL);
|
||||
hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) close_path, NULL, NULL);
|
||||
|
||||
hb_draw_funcs_make_immutable (funcs);
|
||||
}
|
||||
|
||||
return funcs;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
acc_callback (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
glyphy_arc_accumulator_t *acc = user_data;
|
||||
glyphy_point_t p0, p1, p2, p3;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
p0.x = pts[0].x; p0.y = pts[0].y;
|
||||
glyphy_arc_accumulator_move_to (acc, &p0);
|
||||
break;
|
||||
case GSK_PATH_CLOSE:
|
||||
glyphy_arc_accumulator_close_path (acc);
|
||||
break;
|
||||
case GSK_PATH_LINE:
|
||||
p1.x = pts[1].x; p1.y = pts[1].y;
|
||||
glyphy_arc_accumulator_line_to (acc, &p1);
|
||||
break;
|
||||
case GSK_PATH_CURVE:
|
||||
p1.x = pts[1].x; p1.y = pts[1].y;
|
||||
p2.x = pts[2].x; p2.y = pts[2].y;
|
||||
p3.x = pts[3].x; p3.y = pts[3].y;
|
||||
glyphy_arc_accumulator_cubic_to (acc, &p1, &p2, &p3);
|
||||
break;
|
||||
case GSK_PATH_CONIC:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
encode_glyph (GskGLGlyphyLibrary *self,
|
||||
hb_font_t *font,
|
||||
unsigned int glyph_index,
|
||||
double tolerance_per_em,
|
||||
glyphy_rgba_t *buffer,
|
||||
guint buffer_len,
|
||||
guint *output_len,
|
||||
guint *nominal_width,
|
||||
guint *nominal_height,
|
||||
glyphy_extents_t *extents)
|
||||
{
|
||||
hb_face_t *face = hb_font_get_face (font);
|
||||
guint upem = hb_face_get_upem (face);
|
||||
double tolerance = upem * tolerance_per_em;
|
||||
double faraway = (double)upem / (MIN_FONT_SIZE * M_SQRT2);
|
||||
double unit_size = (double) upem / GRID_SIZE;
|
||||
double enlighten_max = (double) upem * ENLIGHTEN_MAX;
|
||||
double embolden_max = (double) upem * EMBOLDEN_MAX;
|
||||
double avg_fetch_achieved;
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path, *simplified;
|
||||
|
||||
self->acc_endpoints->len = 0;
|
||||
|
||||
glyphy_arc_accumulator_reset (self->acc);
|
||||
glyphy_arc_accumulator_set_tolerance (self->acc, tolerance);
|
||||
glyphy_arc_accumulator_set_callback (self->acc,
|
||||
(glyphy_arc_endpoint_accumulator_callback_t)accumulate_endpoint,
|
||||
self->acc_endpoints);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
hb_font_get_glyph_shape (font, glyph_index, gsk_path_get_draw_funcs (), builder);
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
simplified = gsk_path_simplify (path);
|
||||
|
||||
gsk_path_foreach (simplified, GSK_PATH_FOREACH_ALLOW_CURVE, acc_callback, self->acc);
|
||||
gsk_path_unref (simplified);
|
||||
gsk_path_unref (path);
|
||||
|
||||
if (!glyphy_arc_accumulator_successful (self->acc))
|
||||
return FALSE;
|
||||
|
||||
g_assert (glyphy_arc_accumulator_get_error (self->acc) <= tolerance);
|
||||
|
||||
if (self->acc_endpoints->len > 0)
|
||||
glyphy_outline_winding_from_even_odd ((gpointer)self->acc_endpoints->data,
|
||||
self->acc_endpoints->len,
|
||||
FALSE);
|
||||
|
||||
if (!glyphy_arc_list_encode_blob2 ((gpointer)self->acc_endpoints->data,
|
||||
self->acc_endpoints->len,
|
||||
buffer,
|
||||
buffer_len,
|
||||
faraway,
|
||||
unit_size,
|
||||
enlighten_max,
|
||||
embolden_max,
|
||||
&avg_fetch_achieved,
|
||||
output_len,
|
||||
nominal_width,
|
||||
nominal_height,
|
||||
extents))
|
||||
return FALSE;
|
||||
|
||||
glyphy_extents_scale (extents, 1./upem, 1./upem);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline hb_font_t *
|
||||
get_nominal_size_hb_font (PangoFont *font)
|
||||
{
|
||||
hb_font_t *hbfont;
|
||||
const float *coords;
|
||||
unsigned int length;
|
||||
|
||||
hbfont = (hb_font_t *) g_object_get_data ((GObject *)font, "glyph-nominal-size-font");
|
||||
if (hbfont == NULL)
|
||||
{
|
||||
hbfont = hb_font_create (hb_font_get_face (pango_font_get_hb_font (font)));
|
||||
coords = hb_font_get_var_coords_design (pango_font_get_hb_font (font), &length);
|
||||
if (length > 0)
|
||||
hb_font_set_var_coords_design (hbfont, coords, length);
|
||||
|
||||
g_object_set_data_full ((GObject *)font, "glyphy-nominal-size-font",
|
||||
hbfont, (GDestroyNotify)hb_font_destroy);
|
||||
}
|
||||
|
||||
return hbfont;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_gl_glyphy_library_add (GskGLGlyphyLibrary *self,
|
||||
GskGLGlyphyKey *key,
|
||||
PangoFont *font,
|
||||
const GskGLGlyphyValue **out_value)
|
||||
{
|
||||
static glyphy_rgba_t buffer[4096 * 16];
|
||||
GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
|
||||
GskGLGlyphyValue *value;
|
||||
glyphy_extents_t extents;
|
||||
hb_font_t *hbfont;
|
||||
guint packed_x;
|
||||
guint packed_y;
|
||||
guint nominal_w, nominal_h;
|
||||
guint output_len = 0;
|
||||
guint texture_id;
|
||||
guint width, height;
|
||||
|
||||
g_assert (GSK_IS_GL_GLYPHY_LIBRARY (self));
|
||||
g_assert (key != NULL);
|
||||
g_assert (font != NULL);
|
||||
g_assert (out_value != NULL);
|
||||
|
||||
hbfont = get_nominal_size_hb_font (font);
|
||||
|
||||
/* Convert the glyph to a list of arcs */
|
||||
if (!encode_glyph (self, hbfont, key->glyph, TOLERANCE,
|
||||
buffer, sizeof buffer, &output_len,
|
||||
&nominal_w, &nominal_h, &extents))
|
||||
return FALSE;
|
||||
|
||||
/* Allocate space for list within atlas */
|
||||
width = self->item_w;
|
||||
height = (output_len + width - 1) / width;
|
||||
value = gsk_gl_texture_library_pack (tl, key, sizeof *value,
|
||||
width, height, 0,
|
||||
&packed_x, &packed_y);
|
||||
|
||||
g_assert (packed_x % ITEM_W == 0);
|
||||
g_assert (packed_y % ITEM_H_QUANTUM == 0);
|
||||
|
||||
/* Make sure we found space to pack */
|
||||
texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (value);
|
||||
if (texture_id == 0)
|
||||
return FALSE;
|
||||
|
||||
if (!glyphy_extents_is_empty (&extents))
|
||||
{
|
||||
/* Connect the texture for data upload */
|
||||
glActiveTexture (GL_TEXTURE0);
|
||||
glBindTexture (GL_TEXTURE_2D, texture_id);
|
||||
|
||||
g_assert (width > 0);
|
||||
g_assert (height > 0);
|
||||
|
||||
/* Upload the arc list */
|
||||
if (width * height == output_len)
|
||||
{
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y,
|
||||
width, height,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y,
|
||||
width, height - 1,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
buffer);
|
||||
/* Upload the last row separately */
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y + height - 1,
|
||||
output_len - (width * (height - 1)), 1,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
buffer + (width * (height - 1)));
|
||||
}
|
||||
}
|
||||
|
||||
value->extents.min_x = extents.min_x;
|
||||
value->extents.min_y = extents.min_y;
|
||||
value->extents.max_x = extents.max_x;
|
||||
value->extents.max_y = extents.max_y;
|
||||
value->nominal_w = nominal_w;
|
||||
value->nominal_h = nominal_h;
|
||||
value->atlas_x = packed_x / self->item_w;
|
||||
value->atlas_y = packed_y / self->item_h_q;
|
||||
|
||||
*out_value = value;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
142
gsk/gl/gskglglyphylibraryprivate.h
Normal file
142
gsk/gl/gskglglyphylibraryprivate.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/* gskglglyphylibraryprivate.h
|
||||
*
|
||||
* Copyright 2020-2022 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
|
||||
*/
|
||||
|
||||
#ifndef __GSK_GL_GLYPHY_LIBRARY_PRIVATE_H__
|
||||
#define __GSK_GL_GLYPHY_LIBRARY_PRIVATE_H__
|
||||
|
||||
#include <glyphy.h>
|
||||
#include <pango/pango.h>
|
||||
|
||||
#include "gskgltexturelibraryprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_GL_GLYPHY_LIBRARY (gsk_gl_glyphy_library_get_type())
|
||||
|
||||
typedef guint FontKey;
|
||||
|
||||
extern GQuark quark_glyphy_font_key;
|
||||
|
||||
static inline FontKey
|
||||
gsk_gl_glyphy_library_get_font_key (PangoFont *font)
|
||||
{
|
||||
FontKey key;
|
||||
|
||||
key = (FontKey) GPOINTER_TO_UINT (g_object_get_qdata ((GObject *)font, quark_glyphy_font_key));
|
||||
if (key == 0)
|
||||
{
|
||||
PangoFontDescription *desc = pango_font_describe (font);
|
||||
pango_font_description_set_size (desc, 10 * PANGO_SCALE);
|
||||
key = (FontKey) pango_font_description_hash (desc);
|
||||
pango_font_description_free (desc);
|
||||
g_object_set_qdata ((GObject *)font, quark_glyphy_font_key, GUINT_TO_POINTER (key));
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
static inline float
|
||||
gsk_gl_glyphy_library_get_font_scale (PangoFont *font)
|
||||
{
|
||||
hb_font_t *hbfont;
|
||||
int x_scale, y_scale;
|
||||
|
||||
hbfont = pango_font_get_hb_font (font);
|
||||
hb_font_get_scale (hbfont, &x_scale, &y_scale);
|
||||
|
||||
return MAX (x_scale, y_scale) / 1000.0;
|
||||
}
|
||||
|
||||
|
||||
typedef struct _GskGLGlyphyKey
|
||||
{
|
||||
FontKey font;
|
||||
PangoGlyph glyph;
|
||||
} GskGLGlyphyKey;
|
||||
|
||||
typedef struct _GskGLGlyphyValue
|
||||
{
|
||||
GskGLTextureAtlasEntry entry;
|
||||
struct {
|
||||
float min_x;
|
||||
float min_y;
|
||||
float max_x;
|
||||
float max_y;
|
||||
} extents;
|
||||
guint nominal_w;
|
||||
guint nominal_h;
|
||||
guint atlas_x;
|
||||
guint atlas_y;
|
||||
} GskGLGlyphyValue;
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GskGLGlyphyLibrary, gsk_gl_glyphy_library, GSK, GL_GLYPHY_LIBRARY, GskGLTextureLibrary)
|
||||
|
||||
struct _GskGLGlyphyLibrary
|
||||
{
|
||||
GskGLTextureLibrary parent_instance;
|
||||
glyphy_arc_accumulator_t *acc;
|
||||
GArray *acc_endpoints;
|
||||
guint item_w;
|
||||
guint item_h_q;
|
||||
struct {
|
||||
GskGLGlyphyKey key;
|
||||
const GskGLGlyphyValue *value;
|
||||
} front[256];
|
||||
};
|
||||
|
||||
GskGLGlyphyLibrary *gsk_gl_glyphy_library_new (GskGLDriver *driver);
|
||||
gboolean gsk_gl_glyphy_library_add (GskGLGlyphyLibrary *self,
|
||||
GskGLGlyphyKey *key,
|
||||
PangoFont *font,
|
||||
const GskGLGlyphyValue **out_value);
|
||||
|
||||
static inline guint
|
||||
gsk_gl_glyphy_library_lookup_or_add (GskGLGlyphyLibrary *self,
|
||||
const GskGLGlyphyKey *key,
|
||||
PangoFont *font,
|
||||
const GskGLGlyphyValue **out_value)
|
||||
{
|
||||
GskGLTextureAtlasEntry *entry;
|
||||
guint front_index = key->glyph & 0xFF;
|
||||
|
||||
if (memcmp (key, &self->front[front_index], sizeof *key) == 0)
|
||||
{
|
||||
*out_value = self->front[front_index].value;
|
||||
}
|
||||
else if (gsk_gl_texture_library_lookup ((GskGLTextureLibrary *)self, key, &entry))
|
||||
{
|
||||
*out_value = (GskGLGlyphyValue *)entry;
|
||||
self->front[front_index].key = *key;
|
||||
self->front[front_index].value = *out_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
GskGLGlyphyKey *k = g_slice_copy (sizeof *key, key);
|
||||
gsk_gl_glyphy_library_add (self, k, font, out_value);
|
||||
self->front[front_index].key = *key;
|
||||
self->front[front_index].value = *out_value;
|
||||
}
|
||||
|
||||
return GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (*out_value);
|
||||
}
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_GL_GLYPHY_LIBRARY_PRIVATE_H__ */
|
||||
@@ -1,71 +1,71 @@
|
||||
GSK_GL_DEFINE_PROGRAM (blend,
|
||||
"/org/gtk/libgsk/gl/blend.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("blend.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, BLEND_SOURCE2, u_source2)
|
||||
GSK_GL_ADD_UNIFORM (2, BLEND_MODE, u_mode))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (blit,
|
||||
"/org/gtk/libgsk/gl/blit.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("blit.glsl")),
|
||||
GSK_GL_NO_UNIFORMS)
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (blur,
|
||||
"/org/gtk/libgsk/gl/blur.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("blur.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, BLUR_RADIUS, u_blur_radius)
|
||||
GSK_GL_ADD_UNIFORM (2, BLUR_SIZE, u_blur_size)
|
||||
GSK_GL_ADD_UNIFORM (3, BLUR_DIR, u_blur_dir))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (border,
|
||||
"/org/gtk/libgsk/gl/border.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("border.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, BORDER_WIDTHS, u_widths)
|
||||
GSK_GL_ADD_UNIFORM (2, BORDER_OUTLINE_RECT, u_outline_rect))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (color,
|
||||
"/org/gtk/libgsk/gl/color.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("color.glsl")),
|
||||
GSK_GL_NO_UNIFORMS)
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (coloring,
|
||||
"/org/gtk/libgsk/gl/coloring.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("coloring.glsl")),
|
||||
GSK_GL_NO_UNIFORMS)
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (color_matrix,
|
||||
"/org/gtk/libgsk/gl/color_matrix.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("color_matrix.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, COLOR_MATRIX_COLOR_MATRIX, u_color_matrix)
|
||||
GSK_GL_ADD_UNIFORM (2, COLOR_MATRIX_COLOR_OFFSET, u_color_offset))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (conic_gradient,
|
||||
"/org/gtk/libgsk/gl/conic_gradient.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("conic_gradient.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, CONIC_GRADIENT_COLOR_STOPS, u_color_stops)
|
||||
GSK_GL_ADD_UNIFORM (2, CONIC_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops)
|
||||
GSK_GL_ADD_UNIFORM (3, CONIC_GRADIENT_GEOMETRY, u_geometry))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (cross_fade,
|
||||
"/org/gtk/libgsk/gl/cross_fade.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("cross_fade.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, CROSS_FADE_PROGRESS, u_progress)
|
||||
GSK_GL_ADD_UNIFORM (2, CROSS_FADE_SOURCE2, u_source2))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (filled_border,
|
||||
"/org/gtk/libgsk/gl/filled_border.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("filled_border.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, FILLED_BORDER_WIDTHS, u_widths)
|
||||
GSK_GL_ADD_UNIFORM (2, FILLED_BORDER_OUTLINE_RECT, u_outline_rect))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (inset_shadow,
|
||||
"/org/gtk/libgsk/gl/inset_shadow.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("inset_shadow.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, INSET_SHADOW_SPREAD, u_spread)
|
||||
GSK_GL_ADD_UNIFORM (2, INSET_SHADOW_OFFSET, u_offset)
|
||||
GSK_GL_ADD_UNIFORM (3, INSET_SHADOW_OUTLINE_RECT, u_outline_rect))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (linear_gradient,
|
||||
"/org/gtk/libgsk/gl/linear_gradient.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("linear_gradient.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, LINEAR_GRADIENT_COLOR_STOPS, u_color_stops)
|
||||
GSK_GL_ADD_UNIFORM (2, LINEAR_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops)
|
||||
GSK_GL_ADD_UNIFORM (3, LINEAR_GRADIENT_POINTS, u_points)
|
||||
GSK_GL_ADD_UNIFORM (4, LINEAR_GRADIENT_REPEAT, u_repeat))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (outset_shadow,
|
||||
"/org/gtk/libgsk/gl/outset_shadow.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("outset_shadow.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (radial_gradient,
|
||||
"/org/gtk/libgsk/gl/radial_gradient.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("radial_gradient.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, RADIAL_GRADIENT_COLOR_STOPS, u_color_stops)
|
||||
GSK_GL_ADD_UNIFORM (2, RADIAL_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops)
|
||||
GSK_GL_ADD_UNIFORM (3, RADIAL_GRADIENT_REPEAT, u_repeat)
|
||||
@@ -73,12 +73,30 @@ GSK_GL_DEFINE_PROGRAM (radial_gradient,
|
||||
GSK_GL_ADD_UNIFORM (5, RADIAL_GRADIENT_GEOMETRY, u_geometry))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (repeat,
|
||||
"/org/gtk/libgsk/gl/repeat.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("repeat.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, REPEAT_CHILD_BOUNDS, u_child_bounds)
|
||||
GSK_GL_ADD_UNIFORM (2, REPEAT_TEXTURE_RECT, u_texture_rect))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (unblurred_outset_shadow,
|
||||
"/org/gtk/libgsk/gl/unblurred_outset_shadow.glsl",
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("unblurred_outset_shadow.glsl")),
|
||||
GSK_GL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_SPREAD, u_spread)
|
||||
GSK_GL_ADD_UNIFORM (2, UNBLURRED_OUTSET_SHADOW_OFFSET, u_offset)
|
||||
GSK_GL_ADD_UNIFORM (3, UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (glyphy,
|
||||
GSK_GL_SHADER_JOINED (VERTEX,
|
||||
GSK_GL_SHADER_RESOURCE ("glyphy.vs.glsl"),
|
||||
NULL)
|
||||
GSK_GL_SHADER_JOINED (FRAGMENT,
|
||||
GSK_GL_SHADER_RESOURCE ("glyphy.atlas.glsl"),
|
||||
GSK_GL_SHADER_STRING (glyphy_common_shader_source ()),
|
||||
GSK_GL_SHADER_STRING ("#define GLYPHY_SDF_PSEUDO_DISTANCE 1\n"),
|
||||
GSK_GL_SHADER_STRING (glyphy_sdf_shader_source ()),
|
||||
GSK_GL_SHADER_RESOURCE ("glyphy.fs.glsl"),
|
||||
NULL),
|
||||
GSK_GL_ADD_UNIFORM (0, GLYPHY_CONTRAST, u_contrast)
|
||||
GSK_GL_ADD_UNIFORM (1, GLYPHY_GAMMA_ADJUST, u_gamma_adjust)
|
||||
GSK_GL_ADD_UNIFORM (2, GLYPHY_OUTLINE_THICKNESS, u_outline_thickness)
|
||||
GSK_GL_ADD_UNIFORM (3, GLYPHY_OUTLINE, u_outline)
|
||||
GSK_GL_ADD_UNIFORM (4, GLYPHY_BOLDNESS, u_boldness)
|
||||
GSK_GL_ADD_UNIFORM (6, GLYPHY_ATLAS_INFO, u_atlas_info))
|
||||
|
||||
@@ -33,10 +33,12 @@
|
||||
#include <gsk/gskroundedrectprivate.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <pango/pangofc-font.h>
|
||||
|
||||
#include "gskglcommandqueueprivate.h"
|
||||
#include "gskgldriverprivate.h"
|
||||
#include "gskglglyphlibraryprivate.h"
|
||||
#include "gskglglyphylibraryprivate.h"
|
||||
#include "gskgliconlibraryprivate.h"
|
||||
#include "gskglprogramprivate.h"
|
||||
#include "gskglrenderjobprivate.h"
|
||||
@@ -2455,7 +2457,9 @@ gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob *job,
|
||||
scaled_outline.corner[i].height *= scale_y;
|
||||
}
|
||||
|
||||
cached_tid = gsk_gl_shadow_library_lookup (job->driver->shadows, &scaled_outline, blur_radius);
|
||||
cached_tid = gsk_gl_shadow_library_lookup (job->driver->shadows_library,
|
||||
&scaled_outline,
|
||||
blur_radius);
|
||||
|
||||
if (cached_tid == 0)
|
||||
{
|
||||
@@ -2517,7 +2521,7 @@ gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob *job,
|
||||
blur_radius * scale_x,
|
||||
blur_radius * scale_y);
|
||||
|
||||
gsk_gl_shadow_library_insert (job->driver->shadows,
|
||||
gsk_gl_shadow_library_insert (job->driver->shadows_library,
|
||||
&scaled_outline,
|
||||
blur_radius,
|
||||
blurred_texture_id);
|
||||
@@ -2927,10 +2931,10 @@ compute_phase_and_pos (float value, float *pos)
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
|
||||
const GskRenderNode *node,
|
||||
const GdkRGBA *color,
|
||||
gboolean force_color)
|
||||
gsk_gl_render_job_visit_text_node_legacy (GskGLRenderJob *job,
|
||||
const GskRenderNode *node,
|
||||
const GdkRGBA *color,
|
||||
gboolean force_color)
|
||||
{
|
||||
const PangoFont *font = gsk_text_node_get_font (node);
|
||||
const PangoGlyphInfo *glyphs = gsk_text_node_get_glyphs (node, NULL);
|
||||
@@ -2939,7 +2943,7 @@ gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
|
||||
guint num_glyphs = gsk_text_node_get_num_glyphs (node);
|
||||
float x = offset->x + job->offset_x;
|
||||
float y = offset->y + job->offset_y;
|
||||
GskGLGlyphLibrary *library = job->driver->glyphs;
|
||||
GskGLGlyphLibrary *library = job->driver->glyphs_library;
|
||||
GskGLCommandBatch *batch;
|
||||
int x_position = 0;
|
||||
GskGLGlyphKey lookup;
|
||||
@@ -3065,6 +3069,259 @@ gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
|
||||
gsk_gl_render_job_end_draw (job);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float g16hi;
|
||||
float g16lo;
|
||||
} EncodedGlyph;
|
||||
|
||||
static inline unsigned int
|
||||
glyph_encode (guint atlas_x , /* 7 bits */
|
||||
guint atlas_y, /* 7 bits */
|
||||
guint corner_x, /* 1 bit */
|
||||
guint corner_y, /* 1 bit */
|
||||
guint nominal_w, /* 6 bits */
|
||||
guint nominal_h) /* 6 bits */
|
||||
{
|
||||
guint x, y;
|
||||
|
||||
g_assert (0 == (atlas_x & ~0x7F));
|
||||
g_assert (0 == (atlas_y & ~0x7F));
|
||||
g_assert (0 == (corner_x & ~1));
|
||||
g_assert (0 == (corner_y & ~1));
|
||||
g_assert (0 == (nominal_w & ~0x3F));
|
||||
g_assert (0 == (nominal_h & ~0x3F));
|
||||
|
||||
x = (((atlas_x << 6) | nominal_w) << 1) | corner_x;
|
||||
y = (((atlas_y << 6) | nominal_h) << 1) | corner_y;
|
||||
|
||||
return (x << 16) | y;
|
||||
}
|
||||
|
||||
static inline void
|
||||
encoded_glyph_init (EncodedGlyph *eg,
|
||||
float x,
|
||||
float y,
|
||||
guint corner_x,
|
||||
guint corner_y,
|
||||
const GskGLGlyphyValue *gi)
|
||||
{
|
||||
guint encoded = glyph_encode (gi->atlas_x, gi->atlas_y, corner_x, corner_y, gi->nominal_w, gi->nominal_h);
|
||||
|
||||
eg->x = x;
|
||||
eg->y = y;
|
||||
eg->g16hi = encoded >> 16;
|
||||
eg->g16lo = encoded & 0xFFFF;
|
||||
}
|
||||
|
||||
static inline void
|
||||
add_encoded_glyph (GskGLDrawVertex *vertices,
|
||||
const EncodedGlyph *eg,
|
||||
const guint16 c[4])
|
||||
{
|
||||
*vertices = (GskGLDrawVertex) { .position = { eg->x, eg->y}, .uv = { eg->g16hi, eg->g16lo}, .color = { c[0], c[1], c[2], c[3] } };
|
||||
}
|
||||
|
||||
static void
|
||||
get_synthetic_font_params (PangoFont *font,
|
||||
gboolean *embolden,
|
||||
PangoMatrix *matrix)
|
||||
{
|
||||
*embolden = FALSE;
|
||||
|
||||
if (PANGO_IS_FC_FONT (font))
|
||||
{
|
||||
FcPattern *pattern = pango_fc_font_get_pattern (PANGO_FC_FONT (font));
|
||||
FcBool b;
|
||||
FcMatrix mat;
|
||||
FcMatrix *m;
|
||||
|
||||
if (FcPatternGetBool (pattern, FC_EMBOLDEN, 0, &b) == FcResultMatch)
|
||||
*embolden = b;
|
||||
|
||||
FcMatrixInit (&mat);
|
||||
for (int i = 0; FcPatternGetMatrix (pattern, FC_MATRIX, i, &m) == FcResultMatch; i++)
|
||||
FcMatrixMultiply (&mat, &mat, m);
|
||||
|
||||
matrix->xx = mat.xx;
|
||||
matrix->xy = mat.xy;
|
||||
matrix->yx = mat.yx;
|
||||
matrix->yy = mat.yy;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_visit_text_node_glyphy (GskGLRenderJob *job,
|
||||
const GskRenderNode *node,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
const graphene_point_t *offset;
|
||||
const PangoGlyphInfo *glyphs;
|
||||
const PangoGlyphInfo *gi;
|
||||
GskGLGlyphyLibrary *library;
|
||||
GskGLCommandBatch *batch;
|
||||
PangoFont *font;
|
||||
GskGLDrawVertex *vertices;
|
||||
const guint16 *c;
|
||||
GskGLGlyphyKey lookup;
|
||||
guint16 cc[4];
|
||||
float x;
|
||||
float y;
|
||||
guint last_texture = 0;
|
||||
guint num_glyphs;
|
||||
guint used = 0;
|
||||
guint i;
|
||||
int x_position = 0;
|
||||
float font_scale;
|
||||
gboolean embolden;
|
||||
PangoMatrix matrix = PANGO_MATRIX_INIT;
|
||||
|
||||
#define GRID_SIZE 20
|
||||
|
||||
g_assert (!gsk_text_node_has_color_glyphs (node));
|
||||
|
||||
if (!(num_glyphs = gsk_text_node_get_num_glyphs (node)))
|
||||
return;
|
||||
|
||||
if (RGBA_IS_CLEAR (color))
|
||||
return;
|
||||
|
||||
font = (PangoFont *)gsk_text_node_get_font (node);
|
||||
get_synthetic_font_params (font, &embolden, &matrix);
|
||||
|
||||
glyphs = gsk_text_node_get_glyphs (node, NULL);
|
||||
library = job->driver->glyphy_library;
|
||||
offset = gsk_text_node_get_offset (node);
|
||||
x = offset->x + job->offset_x;
|
||||
y = offset->y + job->offset_y;
|
||||
|
||||
rgba_to_half (color, cc);
|
||||
c = cc;
|
||||
|
||||
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, glyphy));
|
||||
|
||||
batch = gsk_gl_command_queue_get_batch (job->command_queue);
|
||||
vertices = gsk_gl_command_queue_add_n_vertices (job->command_queue, num_glyphs);
|
||||
|
||||
lookup.font = gsk_gl_glyphy_library_get_font_key (font);
|
||||
font_scale = gsk_gl_glyphy_library_get_font_scale (font);
|
||||
|
||||
for (i = 0, gi = glyphs; i < num_glyphs; i++, gi++)
|
||||
{
|
||||
const GskGLGlyphyValue *glyph;
|
||||
float cx = 0, cy = 0;
|
||||
guint texture_id;
|
||||
|
||||
lookup.glyph = gi->glyph;
|
||||
texture_id = gsk_gl_glyphy_library_lookup_or_add (library, &lookup, font, &glyph);
|
||||
|
||||
if G_UNLIKELY (texture_id == 0)
|
||||
continue;
|
||||
|
||||
if G_UNLIKELY (last_texture != texture_id || batch->draw.vbo_count + GSK_GL_N_VERTICES > 0xffff)
|
||||
{
|
||||
if G_LIKELY (last_texture != 0)
|
||||
{
|
||||
guint vbo_offset = batch->draw.vbo_offset + batch->draw.vbo_count;
|
||||
|
||||
/* Since we have batched added our VBO vertices to avoid repeated
|
||||
* calls to the buffer, we need to manually tweak the vbo offset
|
||||
* of the new batch as otherwise it will point at the end of our
|
||||
* vbo array.
|
||||
*/
|
||||
gsk_gl_render_job_split_draw (job);
|
||||
batch = gsk_gl_command_queue_get_batch (job->command_queue);
|
||||
batch->draw.vbo_offset = vbo_offset;
|
||||
}
|
||||
|
||||
gsk_gl_program_set_uniform4i (job->current_program,
|
||||
UNIFORM_GLYPHY_ATLAS_INFO, 0,
|
||||
GSK_GL_TEXTURE_LIBRARY (library)->atlas_width,
|
||||
GSK_GL_TEXTURE_LIBRARY (library)->atlas_height,
|
||||
library->item_w,
|
||||
library->item_h_q);
|
||||
gsk_gl_program_set_uniform_texture (job->current_program,
|
||||
UNIFORM_SHARED_SOURCE, 0,
|
||||
GL_TEXTURE_2D,
|
||||
GL_TEXTURE0,
|
||||
texture_id);
|
||||
gsk_gl_program_set_uniform1f (job->current_program,
|
||||
UNIFORM_GLYPHY_GAMMA_ADJUST, 0,
|
||||
1.0);
|
||||
gsk_gl_program_set_uniform1f (job->current_program,
|
||||
UNIFORM_GLYPHY_CONTRAST, 0,
|
||||
1.0);
|
||||
|
||||
/* 0.0208 is the value used by freetype for synthetic emboldening */
|
||||
gsk_gl_program_set_uniform1f (job->current_program,
|
||||
UNIFORM_GLYPHY_BOLDNESS, 0,
|
||||
embolden ? 0.0208 * GRID_SIZE : 0.0);
|
||||
|
||||
#if 0
|
||||
gsk_gl_program_set_uniform1f (job->current_program,
|
||||
UNIFORM_GLYPHY_OUTLINE_THICKNESS, 0,
|
||||
1.0);
|
||||
gsk_gl_program_set_uniform1f (job->current_program,
|
||||
UNIFORM_GLYPHY_OUTLINE, 0,
|
||||
1.0);
|
||||
#endif
|
||||
|
||||
last_texture = texture_id;
|
||||
}
|
||||
|
||||
cx = (float)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
|
||||
if G_UNLIKELY (gi->geometry.y_offset != 0)
|
||||
cy = (float)(gi->geometry.y_offset) / PANGO_SCALE;
|
||||
|
||||
x_position += gi->geometry.width;
|
||||
|
||||
EncodedGlyph encoded[4];
|
||||
#define ENCODE_CORNER(_cx, _cy) \
|
||||
G_STMT_START { \
|
||||
float _dx = _cx * (glyph->extents.max_x - glyph->extents.min_x); \
|
||||
float _dy = _cy * (glyph->extents.max_y - glyph->extents.min_y); \
|
||||
float _vx = x + cx + font_scale * (glyph->extents.min_x + matrix.xx * _dx + matrix.xy * _dy); \
|
||||
float _vy = y + cy - font_scale * (glyph->extents.min_y + matrix.yx * _dx + matrix.yy * _dy); \
|
||||
encoded_glyph_init (&encoded[_cx * 2 + _cy], _vx, _vy, _cx, _cy, glyph); \
|
||||
} G_STMT_END
|
||||
ENCODE_CORNER (0, 0);
|
||||
ENCODE_CORNER (0, 1);
|
||||
ENCODE_CORNER (1, 0);
|
||||
ENCODE_CORNER (1, 1);
|
||||
#undef ENCODE_CORNER
|
||||
|
||||
add_encoded_glyph (vertices++, &encoded[0], c);
|
||||
add_encoded_glyph (vertices++, &encoded[1], c);
|
||||
add_encoded_glyph (vertices++, &encoded[2], c);
|
||||
|
||||
add_encoded_glyph (vertices++, &encoded[1], c);
|
||||
add_encoded_glyph (vertices++, &encoded[2], c);
|
||||
add_encoded_glyph (vertices++, &encoded[3], c);
|
||||
|
||||
batch->draw.vbo_count += GSK_GL_N_VERTICES;
|
||||
used++;
|
||||
}
|
||||
|
||||
if (used != num_glyphs)
|
||||
gsk_gl_command_queue_retract_n_vertices (job->command_queue, num_glyphs - used);
|
||||
|
||||
gsk_gl_render_job_end_draw (job);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
|
||||
const GskRenderNode *node,
|
||||
const GdkRGBA *color,
|
||||
gboolean force_color)
|
||||
{
|
||||
if (!gsk_text_node_has_color_glyphs (node))
|
||||
gsk_gl_render_job_visit_text_node_glyphy (job, node, color);
|
||||
else
|
||||
gsk_gl_render_job_visit_text_node_legacy (job, node, color, force_color);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_visit_shadow_node (GskGLRenderJob *job,
|
||||
const GskRenderNode *node)
|
||||
@@ -3442,14 +3699,14 @@ gsk_gl_render_job_upload_texture (GskGLRenderJob *job,
|
||||
GdkTexture *texture,
|
||||
GskGLRenderOffscreen *offscreen)
|
||||
{
|
||||
if (gsk_gl_texture_library_can_cache ((GskGLTextureLibrary *)job->driver->icons,
|
||||
if (gsk_gl_texture_library_can_cache ((GskGLTextureLibrary *)job->driver->icons_library,
|
||||
texture->width,
|
||||
texture->height) &&
|
||||
!GDK_IS_GL_TEXTURE (texture))
|
||||
{
|
||||
const GskGLIconData *icon_data;
|
||||
|
||||
gsk_gl_icon_library_lookup_or_add (job->driver->icons, texture, &icon_data);
|
||||
gsk_gl_icon_library_lookup_or_add (job->driver->icons_library, texture, &icon_data);
|
||||
offscreen->texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (icon_data);
|
||||
memcpy (&offscreen->area, &icon_data->entry.area, sizeof offscreen->area);
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
|
||||
struct _GskGLShadowLibrary
|
||||
{
|
||||
GObject parent_instance;
|
||||
GObject parent_instance;
|
||||
GskGLDriver *driver;
|
||||
GArray *shadows;
|
||||
GArray *shadows;
|
||||
};
|
||||
|
||||
typedef struct _Shadow
|
||||
|
||||
@@ -27,7 +27,10 @@
|
||||
#include "gskgldriverprivate.h"
|
||||
#include "gskgltexturelibraryprivate.h"
|
||||
|
||||
#define MAX_FRAME_AGE 60
|
||||
#define DEFAULT_MAX_FRAME_AGE 60
|
||||
#define DEFAULT_ATLAS_WIDTH 512
|
||||
#define DEFAULT_ATLAS_HEIGHT 512
|
||||
#define MAX_OLD_RATIO 0.5
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE (GskGLTextureLibrary, gsk_gl_texture_library, G_TYPE_OBJECT)
|
||||
|
||||
@@ -39,6 +42,143 @@ enum {
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
static void
|
||||
gsk_gl_texture_atlas_free (GskGLTextureAtlas *atlas)
|
||||
{
|
||||
if (atlas->texture_id != 0)
|
||||
{
|
||||
glDeleteTextures (1, &atlas->texture_id);
|
||||
atlas->texture_id = 0;
|
||||
}
|
||||
|
||||
g_clear_pointer (&atlas->nodes, g_free);
|
||||
g_slice_free (GskGLTextureAtlas, atlas);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_gl_texture_library_real_compact (GskGLTextureLibrary *self,
|
||||
gint64 frame_id)
|
||||
{
|
||||
GPtrArray *removed = NULL;
|
||||
gboolean ret = FALSE;
|
||||
gboolean periodic_scan;
|
||||
|
||||
g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
|
||||
|
||||
periodic_scan = (self->max_frame_age > 0 &&
|
||||
(frame_id % self->max_frame_age) == 0);
|
||||
|
||||
for (guint i = self->atlases->len; i > 0; i--)
|
||||
{
|
||||
GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i - 1);
|
||||
|
||||
if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO)
|
||||
{
|
||||
GSK_NOTE (GLYPH_CACHE,
|
||||
g_message ("Dropping atlas %d (%g.2%% old)", i,
|
||||
100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas)));
|
||||
if (removed == NULL)
|
||||
removed = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
|
||||
g_ptr_array_add (removed, g_ptr_array_steal_index (self->atlases, i - 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (periodic_scan || removed != NULL)
|
||||
{
|
||||
GskGLTextureAtlasEntry *entry;
|
||||
GHashTableIter iter;
|
||||
guint dropped = 0;
|
||||
guint atlased = 0;
|
||||
|
||||
g_hash_table_iter_init (&iter, self->hash_table);
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
|
||||
{
|
||||
if (entry->is_atlased)
|
||||
{
|
||||
if (removed && g_ptr_array_find (removed, entry->atlas, NULL))
|
||||
{
|
||||
g_hash_table_iter_remove (&iter);
|
||||
dropped++;
|
||||
}
|
||||
else if (periodic_scan)
|
||||
{
|
||||
gsk_gl_texture_atlas_entry_mark_unused (entry);
|
||||
entry->accessed = FALSE;
|
||||
if (entry->is_atlased)
|
||||
atlased++;
|
||||
}
|
||||
}
|
||||
else if (!entry->accessed)
|
||||
{
|
||||
gsk_gl_driver_release_texture (self->driver, entry->texture);
|
||||
g_hash_table_iter_remove (&iter);
|
||||
dropped++;
|
||||
}
|
||||
}
|
||||
|
||||
GSK_NOTE (GLYPH_CACHE, g_message ("%s: Dropped %d individual items",
|
||||
G_OBJECT_TYPE_NAME (self),
|
||||
dropped);
|
||||
g_message ("%s: %d items cached (%d atlased, %d individually)",
|
||||
G_OBJECT_TYPE_NAME (self),
|
||||
g_hash_table_size (self->hash_table),
|
||||
atlased,
|
||||
g_hash_table_size (self->hash_table) - atlased));
|
||||
|
||||
if (dropped > 0)
|
||||
gsk_gl_texture_library_clear_cache (self);
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
g_clear_pointer (&removed, g_ptr_array_unref);
|
||||
}
|
||||
|
||||
GSK_NOTE (GLYPH_CACHE, {
|
||||
static gint64 last_message;
|
||||
gint64 now = g_get_monotonic_time ();
|
||||
if (now - last_message > G_USEC_PER_SEC)
|
||||
{
|
||||
last_message = now;
|
||||
g_message ("%s contains %d atlases",
|
||||
G_OBJECT_TYPE_NAME (self),
|
||||
self->atlases->len);
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_gl_texture_library_real_allocate (GskGLTextureLibrary *self,
|
||||
GskGLTextureAtlas *atlas,
|
||||
int width,
|
||||
int height,
|
||||
int *out_x,
|
||||
int *out_y)
|
||||
{
|
||||
stbrp_rect rect;
|
||||
|
||||
g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
|
||||
g_assert (atlas != NULL);
|
||||
g_assert (width > 0);
|
||||
g_assert (height > 0);
|
||||
g_assert (out_x != NULL);
|
||||
g_assert (out_y != NULL);
|
||||
|
||||
rect.w = width;
|
||||
rect.h = height;
|
||||
|
||||
stbrp_pack_rects (&atlas->context, &rect, 1);
|
||||
|
||||
if (rect.was_packed)
|
||||
{
|
||||
*out_x = rect.x;
|
||||
*out_y = rect.y;
|
||||
}
|
||||
|
||||
return rect.was_packed;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_texture_library_constructed (GObject *object)
|
||||
{
|
||||
@@ -52,6 +192,7 @@ gsk_gl_texture_library_dispose (GObject *object)
|
||||
{
|
||||
GskGLTextureLibrary *self = (GskGLTextureLibrary *)object;
|
||||
|
||||
g_clear_pointer (&self->atlases, g_ptr_array_unref);
|
||||
g_clear_object (&self->driver);
|
||||
|
||||
G_OBJECT_CLASS (gsk_gl_texture_library_parent_class)->dispose (object);
|
||||
@@ -105,6 +246,9 @@ gsk_gl_texture_library_class_init (GskGLTextureLibraryClass *klass)
|
||||
object_class->get_property = gsk_gl_texture_library_get_property;
|
||||
object_class->set_property = gsk_gl_texture_library_set_property;
|
||||
|
||||
klass->compact = gsk_gl_texture_library_real_compact;
|
||||
klass->allocate = gsk_gl_texture_library_real_allocate;
|
||||
|
||||
properties [PROP_DRIVER] =
|
||||
g_param_spec_object ("driver",
|
||||
"Driver",
|
||||
@@ -118,6 +262,10 @@ gsk_gl_texture_library_class_init (GskGLTextureLibraryClass *klass)
|
||||
static void
|
||||
gsk_gl_texture_library_init (GskGLTextureLibrary *self)
|
||||
{
|
||||
self->max_frame_age = DEFAULT_MAX_FRAME_AGE;
|
||||
self->atlas_width = DEFAULT_ATLAS_WIDTH;
|
||||
self->atlas_height = DEFAULT_ATLAS_HEIGHT;
|
||||
self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -136,78 +284,14 @@ gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self,
|
||||
|
||||
void
|
||||
gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self,
|
||||
gint64 frame_id,
|
||||
GPtrArray *removed_atlases)
|
||||
gint64 frame_id)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
|
||||
g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
|
||||
|
||||
gsk_gl_texture_library_compact (self, frame_id);
|
||||
|
||||
if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame)
|
||||
GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame (self, frame_id, removed_atlases);
|
||||
|
||||
if (removed_atlases != NULL)
|
||||
{
|
||||
GskGLTextureAtlasEntry *entry;
|
||||
guint dropped = 0;
|
||||
|
||||
g_hash_table_iter_init (&iter, self->hash_table);
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
|
||||
{
|
||||
if (entry->is_atlased)
|
||||
{
|
||||
for (guint i = 0; i < removed_atlases->len; i++)
|
||||
{
|
||||
GskGLTextureAtlas *atlas = g_ptr_array_index (removed_atlases, i);
|
||||
|
||||
if (atlas == entry->atlas)
|
||||
{
|
||||
g_hash_table_iter_remove (&iter);
|
||||
dropped++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GSK_NOTE (GLYPH_CACHE,
|
||||
if (dropped > 0)
|
||||
g_message ("%s: Dropped %d items",
|
||||
G_OBJECT_TYPE_NAME (self), dropped));
|
||||
}
|
||||
|
||||
if (frame_id % MAX_FRAME_AGE == 0)
|
||||
{
|
||||
GskGLTextureAtlasEntry *entry;
|
||||
int atlased = 0;
|
||||
int dropped = 0;
|
||||
|
||||
g_hash_table_iter_init (&iter, self->hash_table);
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
|
||||
{
|
||||
if (!entry->is_atlased && !entry->accessed)
|
||||
{
|
||||
gsk_gl_driver_release_texture (self->driver, entry->texture);
|
||||
g_hash_table_iter_remove (&iter);
|
||||
dropped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
gsk_gl_texture_atlas_entry_mark_unused (entry);
|
||||
entry->accessed = FALSE;
|
||||
if (entry->is_atlased)
|
||||
atlased++;
|
||||
}
|
||||
|
||||
GSK_NOTE (GLYPH_CACHE, g_message ("%s: Dropped %d individual items",
|
||||
G_OBJECT_TYPE_NAME (self),
|
||||
dropped);
|
||||
g_message ("%s: %d items cached (%d atlased, %d individually)",
|
||||
G_OBJECT_TYPE_NAME (self),
|
||||
g_hash_table_size (self->hash_table),
|
||||
atlased,
|
||||
g_hash_table_size (self->hash_table) - atlased));
|
||||
}
|
||||
GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame (self, frame_id);
|
||||
}
|
||||
|
||||
static GskGLTexture *
|
||||
@@ -234,90 +318,29 @@ gsk_gl_texture_library_pack_one (GskGLTextureLibrary *self,
|
||||
return texture;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
gsk_gl_texture_atlas_pack (GskGLTextureAtlas *self,
|
||||
int width,
|
||||
int height,
|
||||
int *out_x,
|
||||
int *out_y)
|
||||
{
|
||||
stbrp_rect rect;
|
||||
|
||||
rect.w = width;
|
||||
rect.h = height;
|
||||
|
||||
stbrp_pack_rects (&self->context, &rect, 1);
|
||||
|
||||
if (rect.was_packed)
|
||||
{
|
||||
*out_x = rect.x;
|
||||
*out_y = rect.y;
|
||||
}
|
||||
|
||||
return rect.was_packed;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_texture_atlas_initialize (GskGLDriver *driver,
|
||||
GskGLTextureAtlas *atlas)
|
||||
{
|
||||
/* Insert a single pixel at 0,0 for use in coloring */
|
||||
|
||||
gboolean packed G_GNUC_UNUSED;
|
||||
int x, y;
|
||||
guint gl_format;
|
||||
guint gl_type;
|
||||
guint8 pixel_data[4 * 3 * 3];
|
||||
|
||||
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
|
||||
"Initializing Atlas");
|
||||
|
||||
packed = gsk_gl_texture_atlas_pack (atlas, 3, 3, &x, &y);
|
||||
g_assert (packed);
|
||||
g_assert (x == 0 && y == 0);
|
||||
|
||||
memset (pixel_data, 255, sizeof pixel_data);
|
||||
|
||||
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
|
||||
{
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gl_format = GL_BGRA;
|
||||
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
}
|
||||
|
||||
glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
|
||||
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
0, 0,
|
||||
3, 3,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
|
||||
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
|
||||
|
||||
driver->command_queue->n_uploads++;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_texture_atlases_pack (GskGLDriver *driver,
|
||||
int width,
|
||||
int height,
|
||||
GskGLTextureAtlas **out_atlas,
|
||||
int *out_x,
|
||||
int *out_y)
|
||||
gsk_gl_texture_library_pack_any_atlas (GskGLTextureLibrary *self,
|
||||
int width,
|
||||
int height,
|
||||
GskGLTextureAtlas **out_atlas,
|
||||
int *out_x,
|
||||
int *out_y)
|
||||
{
|
||||
GskGLTextureAtlas *atlas = NULL;
|
||||
int x, y;
|
||||
|
||||
for (guint i = 0; i < driver->atlases->len; i++)
|
||||
{
|
||||
atlas = g_ptr_array_index (driver->atlases, i);
|
||||
g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
|
||||
g_assert (width > 0);
|
||||
g_assert (height > 0);
|
||||
g_assert (out_atlas != NULL);
|
||||
g_assert (out_x != NULL);
|
||||
g_assert (out_y != NULL);
|
||||
|
||||
if (gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
|
||||
for (guint i = 0; i < self->atlases->len; i++)
|
||||
{
|
||||
atlas = g_ptr_array_index (self->atlases, i);
|
||||
|
||||
if (gsk_gl_texture_library_allocate (self, atlas, width, height, &x, &y))
|
||||
break;
|
||||
|
||||
atlas = NULL;
|
||||
@@ -326,12 +349,10 @@ gsk_gl_texture_atlases_pack (GskGLDriver *driver,
|
||||
if (atlas == NULL)
|
||||
{
|
||||
/* No atlas has enough space, so create a new one... */
|
||||
atlas = gsk_gl_driver_create_atlas (driver);
|
||||
|
||||
gsk_gl_texture_atlas_initialize (driver, atlas);
|
||||
atlas = gsk_gl_texture_library_acquire_atlas (self);
|
||||
|
||||
/* Pack it onto that one, which surely has enough space... */
|
||||
if (!gsk_gl_texture_atlas_pack (atlas, width, height, &x, &y))
|
||||
if (!gsk_gl_texture_library_allocate (self, atlas, width, height, &x, &y))
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
@@ -380,17 +401,19 @@ gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
|
||||
*out_packed_x = 0;
|
||||
*out_packed_y = 0;
|
||||
}
|
||||
else if (width <= self->max_entry_size && height <= self->max_entry_size)
|
||||
else if (self->max_entry_size == 0 ||
|
||||
(width <= self->max_entry_size &&
|
||||
height <= self->max_entry_size))
|
||||
{
|
||||
int packed_x;
|
||||
int packed_y;
|
||||
|
||||
gsk_gl_texture_atlases_pack (self->driver,
|
||||
padding + width + padding,
|
||||
padding + height + padding,
|
||||
&atlas,
|
||||
&packed_x,
|
||||
&packed_y);
|
||||
gsk_gl_texture_library_pack_any_atlas (self,
|
||||
padding + width + padding,
|
||||
padding + height + padding,
|
||||
&atlas,
|
||||
&packed_x,
|
||||
&packed_y);
|
||||
|
||||
entry->atlas = atlas;
|
||||
entry->is_atlased = TRUE;
|
||||
@@ -424,3 +447,134 @@ gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/*
|
||||
* gsk_gl_texture_library_clear_cache:
|
||||
*
|
||||
* Clear the front cache if the texture library is using one. For
|
||||
* example the glyph cache would drop it's front cache to force
|
||||
* next lookups to fall through to the GHashTable key lookup.
|
||||
*/
|
||||
void
|
||||
gsk_gl_texture_library_clear_cache (GskGLTextureLibrary *self)
|
||||
{
|
||||
g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
|
||||
|
||||
if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache)
|
||||
GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache (self);
|
||||
}
|
||||
|
||||
/*
|
||||
* gsk_gl_texture_library_compact:
|
||||
*
|
||||
* Requests that the texture library compact it's altases. That
|
||||
* generally means to traverse them to look for unused pixels over
|
||||
* a certain threshold and release them if necessary.
|
||||
*
|
||||
* Returns: %TRUE if any compaction occurred.
|
||||
*/
|
||||
gboolean
|
||||
gsk_gl_texture_library_compact (GskGLTextureLibrary *self,
|
||||
gint64 frame_id)
|
||||
{
|
||||
g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), FALSE);
|
||||
|
||||
if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->compact)
|
||||
return GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->compact (self, frame_id);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_texture_library_reset (GskGLTextureLibrary *self)
|
||||
{
|
||||
g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
|
||||
|
||||
gsk_gl_texture_library_clear_cache (self);
|
||||
|
||||
g_hash_table_remove_all (self->hash_table);
|
||||
|
||||
if (self->atlases->len)
|
||||
g_ptr_array_remove_range (self->atlases, 0, self->atlases->len);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_texture_library_set_atlas_size (GskGLTextureLibrary *self,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
|
||||
|
||||
if (width <= 0)
|
||||
width = DEFAULT_ATLAS_WIDTH;
|
||||
|
||||
if (height <= 0)
|
||||
height = DEFAULT_ATLAS_HEIGHT;
|
||||
|
||||
self->atlas_height = height;
|
||||
self->atlas_width = width;
|
||||
|
||||
gsk_gl_texture_library_reset (self);
|
||||
}
|
||||
|
||||
/*
|
||||
* gsk_gl_texture_library_acquire_atlas:
|
||||
*
|
||||
* Allocates a new texture atlas based on the current size
|
||||
* and format requirements.
|
||||
*/
|
||||
GskGLTextureAtlas *
|
||||
gsk_gl_texture_library_acquire_atlas (GskGLTextureLibrary *self)
|
||||
{
|
||||
GskGLTextureAtlas *atlas;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), NULL);
|
||||
g_return_val_if_fail (GSK_IS_GL_DRIVER (self->driver), NULL);
|
||||
g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->driver->command_queue), NULL);
|
||||
g_return_val_if_fail (self->atlas_width > 0, NULL);
|
||||
g_return_val_if_fail (self->atlas_height > 0, NULL);
|
||||
|
||||
atlas = g_slice_new0 (GskGLTextureAtlas);
|
||||
atlas->width = self->atlas_width;
|
||||
atlas->height = self->atlas_height;
|
||||
/* TODO: We might want to change the strategy about the amount of
|
||||
* nodes here? stb_rect_pack.h says width is optimal. */
|
||||
atlas->nodes = g_malloc0_n (atlas->width, sizeof (struct stbrp_node));
|
||||
stbrp_init_target (&atlas->context, atlas->width, atlas->height, atlas->nodes, atlas->width);
|
||||
atlas->texture_id = gsk_gl_command_queue_create_texture (self->driver->command_queue,
|
||||
atlas->width,
|
||||
atlas->height,
|
||||
GL_RGBA8,
|
||||
GL_LINEAR,
|
||||
GL_LINEAR);
|
||||
|
||||
gdk_gl_context_label_object_printf (gdk_gl_context_get_current (),
|
||||
GL_TEXTURE, atlas->texture_id,
|
||||
"Texture atlas %d",
|
||||
atlas->texture_id);
|
||||
|
||||
g_ptr_array_add (self->atlases, atlas);
|
||||
|
||||
if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->init_atlas)
|
||||
GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->init_atlas (self, atlas);
|
||||
|
||||
return atlas;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_gl_texture_library_allocate (GskGLTextureLibrary *self,
|
||||
GskGLTextureAtlas *atlas,
|
||||
int width,
|
||||
int height,
|
||||
int *out_x,
|
||||
int *out_y)
|
||||
{
|
||||
g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self));
|
||||
g_assert (atlas != NULL);
|
||||
g_assert (width > 0);
|
||||
g_assert (height > 0);
|
||||
g_assert (out_x != NULL);
|
||||
g_assert (out_y != NULL);
|
||||
|
||||
return GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->allocate (self, atlas, width, height, out_x, out_y);
|
||||
}
|
||||
|
||||
@@ -37,9 +37,14 @@ G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GskGLTextureAtlas
|
||||
{
|
||||
/* Used by Glyph/Icons */
|
||||
struct stbrp_context context;
|
||||
struct stbrp_node *nodes;
|
||||
|
||||
/* Used by Glyphy */
|
||||
int cursor_x;
|
||||
int cursor_y;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
|
||||
@@ -49,8 +54,6 @@ typedef struct _GskGLTextureAtlas
|
||||
* But are now unused.
|
||||
*/
|
||||
int unused_pixels;
|
||||
|
||||
void *user_data;
|
||||
} GskGLTextureAtlas;
|
||||
|
||||
typedef struct _GskGLTextureAtlasEntry
|
||||
@@ -89,40 +92,67 @@ typedef struct _GskGLTextureAtlasEntry
|
||||
|
||||
typedef struct _GskGLTextureLibrary
|
||||
{
|
||||
GObject parent_instance;
|
||||
GObject parent_instance;
|
||||
GskGLDriver *driver;
|
||||
GHashTable *hash_table;
|
||||
guint max_entry_size;
|
||||
GPtrArray *atlases;
|
||||
GHashTable *hash_table;
|
||||
guint max_entry_size;
|
||||
guint max_frame_age;
|
||||
guint atlas_width;
|
||||
guint atlas_height;
|
||||
} GskGLTextureLibrary;
|
||||
|
||||
typedef struct _GskGLTextureLibraryClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*begin_frame) (GskGLTextureLibrary *library,
|
||||
gint64 frame_id,
|
||||
GPtrArray *removed_atlases);
|
||||
void (*begin_frame) (GskGLTextureLibrary *library,
|
||||
gint64 frame_id);
|
||||
gboolean (*compact) (GskGLTextureLibrary *library,
|
||||
gint64 frame_id);
|
||||
void (*clear_cache) (GskGLTextureLibrary *library);
|
||||
void (*init_atlas) (GskGLTextureLibrary *library,
|
||||
GskGLTextureAtlas *atlas);
|
||||
gboolean (*allocate) (GskGLTextureLibrary *library,
|
||||
GskGLTextureAtlas *atlas,
|
||||
int width,
|
||||
int height,
|
||||
int *out_x,
|
||||
int *out_y);
|
||||
} GskGLTextureLibraryClass;
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GskGLTextureLibrary, g_object_unref)
|
||||
|
||||
GType gsk_gl_texture_library_get_type (void) G_GNUC_CONST;
|
||||
void gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self,
|
||||
GHashFunc hash_func,
|
||||
GEqualFunc equal_func,
|
||||
GDestroyNotify key_destroy,
|
||||
GDestroyNotify value_destroy);
|
||||
void gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self,
|
||||
gint64 frame_id,
|
||||
GPtrArray *removed_atlases);
|
||||
gpointer gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
|
||||
gpointer key,
|
||||
gsize valuelen,
|
||||
guint width,
|
||||
guint height,
|
||||
int padding,
|
||||
guint *out_packed_x,
|
||||
guint *out_packed_y);
|
||||
GType gsk_gl_texture_library_get_type (void) G_GNUC_CONST;
|
||||
gboolean gsk_gl_texture_library_compact (GskGLTextureLibrary *self,
|
||||
gint64 frame_id);
|
||||
void gsk_gl_texture_library_clear_cache (GskGLTextureLibrary *self);
|
||||
void gsk_gl_texture_library_reset (GskGLTextureLibrary *self);
|
||||
void gsk_gl_texture_library_set_atlas_size (GskGLTextureLibrary *self,
|
||||
int width,
|
||||
int height);
|
||||
GskGLTextureAtlas *gsk_gl_texture_library_acquire_atlas (GskGLTextureLibrary *self);
|
||||
void gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self,
|
||||
GHashFunc hash_func,
|
||||
GEqualFunc equal_func,
|
||||
GDestroyNotify key_destroy,
|
||||
GDestroyNotify value_destroy);
|
||||
void gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self,
|
||||
gint64 frame_id);
|
||||
gboolean gsk_gl_texture_library_allocate (GskGLTextureLibrary *self,
|
||||
GskGLTextureAtlas *atlas,
|
||||
int width,
|
||||
int height,
|
||||
int *out_x,
|
||||
int *out_y);
|
||||
gpointer gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
|
||||
gpointer key,
|
||||
gsize valuelen,
|
||||
guint width,
|
||||
guint height,
|
||||
int padding,
|
||||
guint *out_packed_x,
|
||||
guint *out_packed_y);
|
||||
|
||||
static inline void
|
||||
gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self,
|
||||
|
||||
@@ -37,6 +37,7 @@ typedef struct _GskGLCompiler GskGLCompiler;
|
||||
typedef struct _GskGLDrawVertex GskGLDrawVertex;
|
||||
typedef struct _GskGLRenderTarget GskGLRenderTarget;
|
||||
typedef struct _GskGLGlyphLibrary GskGLGlyphLibrary;
|
||||
typedef struct _GskGLGlyphyLibrary GskGLGlyphyLibrary;
|
||||
typedef struct _GskGLIconLibrary GskGLIconLibrary;
|
||||
typedef struct _GskGLProgram GskGLProgram;
|
||||
typedef struct _GskGLRenderJob GskGLRenderJob;
|
||||
|
||||
15
gsk/gl/resources/glyphy.atlas.glsl
Normal file
15
gsk/gl/resources/glyphy.atlas.glsl
Normal file
@@ -0,0 +1,15 @@
|
||||
uniform ivec4 u_atlas_info;
|
||||
|
||||
#define GLYPHY_TEXTURE1D_EXTRA_DECLS , sampler2D _tex, ivec4 _atlas_info, ivec2 _atlas_pos
|
||||
#define GLYPHY_TEXTURE1D_EXTRA_ARGS , _tex, _atlas_info, _atlas_pos
|
||||
#define GLYPHY_DEMO_EXTRA_ARGS , u_source, u_atlas_info, gi.atlas_pos
|
||||
|
||||
vec4
|
||||
glyphy_texture1D_func (int offset GLYPHY_TEXTURE1D_EXTRA_DECLS)
|
||||
{
|
||||
ivec2 item_geom = _atlas_info.zw;
|
||||
vec2 pos = (vec2 (_atlas_pos.xy * item_geom +
|
||||
ivec2 (mod (float (offset), float (item_geom.x)), offset / item_geom.x)) +
|
||||
+ vec2 (.5, .5)) / vec2(_atlas_info.xy);
|
||||
return GskTexture (_tex, pos);
|
||||
}
|
||||
62
gsk/gl/resources/glyphy.fs.glsl
Normal file
62
gsk/gl/resources/glyphy.fs.glsl
Normal file
@@ -0,0 +1,62 @@
|
||||
// FRAGMENT_SHADER:
|
||||
// glyphy.fs.glsl
|
||||
|
||||
uniform float u_contrast;
|
||||
uniform float u_gamma_adjust;
|
||||
uniform float u_outline_thickness;
|
||||
uniform bool u_outline;
|
||||
uniform float u_boldness;
|
||||
|
||||
_IN_ vec4 v_glyph;
|
||||
_IN_ vec4 final_color;
|
||||
|
||||
#define SQRT2 1.4142135623730951
|
||||
#define SQRT2_INV 0.70710678118654757 /* 1 / sqrt(2.) */
|
||||
|
||||
struct glyph_info_t {
|
||||
ivec2 nominal_size;
|
||||
ivec2 atlas_pos;
|
||||
};
|
||||
|
||||
glyph_info_t
|
||||
glyph_info_decode (vec4 v)
|
||||
{
|
||||
glyph_info_t gi;
|
||||
gi.nominal_size = (ivec2 (mod (v.zw, 256.)) + 2) / 4;
|
||||
gi.atlas_pos = ivec2 (v_glyph.zw) / 256;
|
||||
return gi;
|
||||
}
|
||||
|
||||
float
|
||||
antialias (float d)
|
||||
{
|
||||
return smoothstep (-.75, +.75, d);
|
||||
}
|
||||
|
||||
void
|
||||
main()
|
||||
{
|
||||
vec2 p = v_glyph.xy;
|
||||
glyph_info_t gi = glyph_info_decode (v_glyph);
|
||||
|
||||
/* isotropic antialiasing */
|
||||
vec2 dpdx = dFdx (p);
|
||||
vec2 dpdy = dFdy (p);
|
||||
float m = length (vec2 (length (dpdx), length (dpdy))) * SQRT2_INV;
|
||||
|
||||
float gsdist = glyphy_sdf (p, gi.nominal_size GLYPHY_DEMO_EXTRA_ARGS);
|
||||
gsdist -= u_boldness;
|
||||
float sdist = gsdist / m * u_contrast;
|
||||
|
||||
if (u_outline)
|
||||
sdist = abs (sdist) - u_outline_thickness * .5;
|
||||
|
||||
if (sdist > 1.)
|
||||
discard;
|
||||
|
||||
float alpha = antialias (-sdist);
|
||||
if (u_gamma_adjust != 1.)
|
||||
alpha = pow (alpha, 1./u_gamma_adjust);
|
||||
|
||||
gskSetOutputColor(final_color * alpha);
|
||||
}
|
||||
24
gsk/gl/resources/glyphy.vs.glsl
Normal file
24
gsk/gl/resources/glyphy.vs.glsl
Normal file
@@ -0,0 +1,24 @@
|
||||
// VERTEX_SHADER:
|
||||
// glyphy.vs.glsl
|
||||
|
||||
_OUT_ vec4 v_glyph;
|
||||
_OUT_ vec4 final_color;
|
||||
|
||||
vec4
|
||||
glyph_vertex_transcode (vec2 v)
|
||||
{
|
||||
ivec2 g = ivec2 (v);
|
||||
ivec2 corner = ivec2 (mod (v, 2.));
|
||||
g /= 2;
|
||||
ivec2 nominal_size = ivec2 (mod (vec2(g), 64.));
|
||||
return vec4 (corner * nominal_size, g * 4);
|
||||
}
|
||||
|
||||
void
|
||||
main()
|
||||
{
|
||||
v_glyph = glyph_vertex_transcode(aUv);
|
||||
vUv = v_glyph.zw;
|
||||
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
|
||||
final_color = gsk_scaled_premultiply(aColor, u_alpha);
|
||||
}
|
||||
@@ -19,6 +19,9 @@ gsk_private_gl_shaders = [
|
||||
'gl/resources/repeat.glsl',
|
||||
'gl/resources/custom.glsl',
|
||||
'gl/resources/filled_border.glsl',
|
||||
'gl/resources/glyphy.atlas.glsl',
|
||||
'gl/resources/glyphy.fs.glsl',
|
||||
'gl/resources/glyphy.vs.glsl',
|
||||
]
|
||||
|
||||
gsk_public_sources = files([
|
||||
@@ -59,6 +62,7 @@ gsk_private_sources = files([
|
||||
'gl/gskglprofiler.c',
|
||||
'gl/gskgldriver.c',
|
||||
'gl/gskglglyphlibrary.c',
|
||||
'gl/gskglglyphylibrary.c',
|
||||
'gl/gskgliconlibrary.c',
|
||||
'gl/gskglprogram.c',
|
||||
'gl/gskglrenderjob.c',
|
||||
@@ -196,6 +200,7 @@ gsk_deps = [
|
||||
cairo_csi_dep,
|
||||
pixbuf_dep,
|
||||
libgdk_dep,
|
||||
libglyphy_dep,
|
||||
]
|
||||
|
||||
libgsk_f16c = static_library('gsk_f16c',
|
||||
|
||||
@@ -23,6 +23,7 @@ epoxy_req = '>= 1.4'
|
||||
cloudproviders_req = '>= 0.3.1'
|
||||
xkbcommon_req = '>= 0.2.0'
|
||||
sysprof_req = '>= 3.38.0'
|
||||
libglyphy_req = '>= 0.2.0'
|
||||
|
||||
fs = import('fs')
|
||||
gnome = import('gnome')
|
||||
@@ -392,6 +393,8 @@ pango_dep = dependency('pango', version: pango_req,
|
||||
fallback : ['pango', 'libpango_dep'])
|
||||
fribidi_dep = dependency('fribidi', version: fribidi_req,
|
||||
fallback : ['fribidi', 'libfribidi_dep'])
|
||||
libglyphy_dep = dependency('glyphy', version: libglyphy_req,
|
||||
fallback : ['glyphy', 'libglyphy_dep'])
|
||||
|
||||
# Require PangoFT2 if on X11 or wayland
|
||||
require_pangoft2 = wayland_enabled or x11_enabled
|
||||
|
||||
5
subprojects/glyphy.wrap
Normal file
5
subprojects/glyphy.wrap
Normal file
@@ -0,0 +1,5 @@
|
||||
[wrap-git]
|
||||
directory=glyphy
|
||||
url=https://github.com/behdad/glyphy.git
|
||||
revision=master
|
||||
depth=1
|
||||
117
tests/bigfont.c
Normal file
117
tests/bigfont.c
Normal file
@@ -0,0 +1,117 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define DEMO_TYPE_WIDGET (demo_widget_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (DemoWidget, demo_widget, DEMO, WIDGET, GtkWidget)
|
||||
|
||||
struct _DemoWidget
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
};
|
||||
|
||||
struct _DemoWidgetClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (DemoWidget, demo_widget, GTK_TYPE_WIDGET)
|
||||
|
||||
static void
|
||||
demo_widget_init (DemoWidget *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
DemoWidget *self = DEMO_WIDGET (widget);
|
||||
PangoLayout *layout;
|
||||
int width, height;
|
||||
int pwidth, pheight;
|
||||
PangoFontDescription *desc;
|
||||
int size;
|
||||
double scale;
|
||||
int x, y;
|
||||
GtkStyleContext *context;
|
||||
|
||||
width = gtk_widget_get_width (widget);
|
||||
height = gtk_widget_get_height (widget);
|
||||
|
||||
layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), "Best Aa");
|
||||
|
||||
pango_layout_get_pixel_size (layout, &pwidth, &pheight);
|
||||
desc = pango_font_description_copy_static (pango_context_get_font_description (pango_layout_get_context (layout)));
|
||||
size = pango_font_description_get_size (desc);
|
||||
|
||||
scale = MIN (width / (double)pwidth, height / (double)pheight);
|
||||
|
||||
pango_font_description_set_size (desc, size * scale * 0.5);
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_font_description_free (desc);
|
||||
|
||||
pango_layout_get_pixel_size (layout, &pwidth, &pheight);
|
||||
|
||||
x = floor ((width - pwidth) / 2);
|
||||
y = floor ((height - pheight) / 2);
|
||||
|
||||
context = gtk_widget_get_style_context (widget);
|
||||
|
||||
gtk_snapshot_render_layout (snapshot, context, x, y, layout);
|
||||
|
||||
g_object_unref (layout);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_dispose (GObject *object)
|
||||
{
|
||||
G_OBJECT_CLASS (demo_widget_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_class_init (DemoWidgetClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
|
||||
object_class->dispose = demo_widget_dispose;
|
||||
|
||||
widget_class->snapshot = demo_widget_snapshot;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
demo_widget_new (void)
|
||||
{
|
||||
return g_object_new (DEMO_TYPE_WIDGET, NULL);
|
||||
}
|
||||
|
||||
static const char css[] =
|
||||
"* {\n"
|
||||
" font-family: Cantarell;\n"
|
||||
" font-weight: 520;\n"
|
||||
"}";
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkCssProvider *style;
|
||||
GtkWidget *window;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
style = gtk_css_provider_new ();
|
||||
gtk_css_provider_load_from_data (style, css, strlen (css));
|
||||
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
|
||||
GTK_STYLE_PROVIDER (style),
|
||||
800);
|
||||
|
||||
window = gtk_window_new ();
|
||||
|
||||
gtk_window_set_child (GTK_WINDOW (window), demo_widget_new ());
|
||||
|
||||
gtk_widget_show (window);
|
||||
|
||||
while (1)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -4,8 +4,10 @@ gtk_tests = [
|
||||
['testpopup'],
|
||||
['animated-resizing', ['frame-stats.c', 'variable.c']],
|
||||
['animated-revealing', ['frame-stats.c', 'variable.c']],
|
||||
['bigfont'],
|
||||
['blur-performance', ['../gsk/gskcairoblur.c']],
|
||||
['motion-compression'],
|
||||
['movingtext'],
|
||||
['overlayscroll'],
|
||||
['curve2'],
|
||||
['testupload'],
|
||||
|
||||
206
tests/movingtext.c
Normal file
206
tests/movingtext.c
Normal file
@@ -0,0 +1,206 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define DEMO_TYPE_WIDGET (demo_widget_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (DemoWidget, demo_widget, DEMO, WIDGET, GtkWidget)
|
||||
|
||||
struct _DemoWidget
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
guint tick_cb;
|
||||
guint64 start_time;
|
||||
guint64 stop_time;
|
||||
|
||||
char *text;
|
||||
|
||||
float angle;
|
||||
float size;
|
||||
};
|
||||
|
||||
struct _DemoWidgetClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (DemoWidget, demo_widget, GTK_TYPE_WIDGET)
|
||||
|
||||
static gboolean
|
||||
tick_cb (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer user_data)
|
||||
{
|
||||
DemoWidget *self = DEMO_WIDGET (widget);
|
||||
guint64 now;
|
||||
|
||||
now = gdk_frame_clock_get_frame_time (frame_clock);
|
||||
|
||||
if (self->start_time == 0)
|
||||
self->start_time = now;
|
||||
|
||||
self->angle = 360 * (now - self->start_time) / (double)(G_TIME_SPAN_SECOND * 10);
|
||||
self->size = 150 + 130 * sin (2 * G_PI * (now - self->start_time) / (double)(G_TIME_SPAN_SECOND * 5));
|
||||
|
||||
gtk_widget_queue_draw (widget);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
pressed_cb (GtkEventController *controller,
|
||||
guint keyval,
|
||||
guint keycode,
|
||||
GdkModifierType state,
|
||||
gpointer data)
|
||||
{
|
||||
DemoWidget *self = (DemoWidget *)gtk_event_controller_get_widget (controller);
|
||||
|
||||
if (keyval == GDK_KEY_space)
|
||||
{
|
||||
GdkFrameClock *frame_clock;
|
||||
guint64 now;
|
||||
|
||||
frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (self));
|
||||
now = gdk_frame_clock_get_frame_time (frame_clock);
|
||||
|
||||
if (self->tick_cb)
|
||||
{
|
||||
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick_cb);
|
||||
self->tick_cb = 0;
|
||||
self->stop_time = now;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->start_time += now - self->stop_time;
|
||||
self->tick_cb = gtk_widget_add_tick_callback (GTK_WIDGET (self), tick_cb, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_init (DemoWidget *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
|
||||
self->start_time = 0;
|
||||
self->tick_cb = gtk_widget_add_tick_callback (GTK_WIDGET (self), tick_cb, NULL, NULL);
|
||||
|
||||
controller = gtk_event_controller_key_new ();
|
||||
g_signal_connect (controller, "key-pressed", G_CALLBACK (pressed_cb), NULL);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
DemoWidget *self = DEMO_WIDGET (widget);
|
||||
PangoLayout *layout;
|
||||
int width, height;
|
||||
int pwidth, pheight;
|
||||
PangoFontDescription *desc;
|
||||
|
||||
width = gtk_widget_get_width (widget);
|
||||
height = gtk_widget_get_height (widget);
|
||||
|
||||
gtk_snapshot_save (snapshot);
|
||||
|
||||
layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), self->text);
|
||||
desc = pango_font_description_new ();
|
||||
pango_font_description_set_family (desc, "Cantarell");
|
||||
pango_font_description_set_weight (desc, 520);
|
||||
pango_font_description_set_size (desc, self->size * PANGO_SCALE);
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_font_description_free (desc);
|
||||
pango_layout_get_pixel_size (layout, &pwidth, &pheight);
|
||||
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0.5 * width, 0.5 * height));
|
||||
gtk_snapshot_rotate (snapshot, self->angle);
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (- 0.5 * pwidth, - 0.5 * pheight));
|
||||
|
||||
gtk_snapshot_append_layout (snapshot, layout, &(GdkRGBA) { 0, 0, 0, 1});
|
||||
|
||||
g_object_unref (layout);
|
||||
|
||||
gtk_snapshot_restore (snapshot);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_dispose (GObject *object)
|
||||
{
|
||||
DemoWidget *self = DEMO_WIDGET (object);
|
||||
|
||||
g_free (self->text);
|
||||
|
||||
G_OBJECT_CLASS (demo_widget_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_class_init (DemoWidgetClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
|
||||
object_class->dispose = demo_widget_dispose;
|
||||
|
||||
widget_class->snapshot = demo_widget_snapshot;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
demo_widget_new (const char *text,
|
||||
gsize length)
|
||||
{
|
||||
DemoWidget *demo;
|
||||
|
||||
demo = g_object_new (DEMO_TYPE_WIDGET, NULL);
|
||||
|
||||
demo->text = g_strndup (text, length);
|
||||
|
||||
return GTK_WIDGET (demo);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkWidget *demo;
|
||||
char *text;
|
||||
gsize length;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
window = gtk_window_new ();
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!g_file_get_contents (argv[1], &text, &length, &error))
|
||||
{
|
||||
g_warning ("%s", error->message);
|
||||
g_error_free (error);
|
||||
text = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!text)
|
||||
{
|
||||
text = g_strdup ("Best Aa");
|
||||
length = strlen (text);
|
||||
}
|
||||
|
||||
demo = demo_widget_new (text, length);
|
||||
gtk_window_set_child (GTK_WINDOW (window), demo);
|
||||
|
||||
g_free (text);
|
||||
|
||||
gtk_widget_show (window);
|
||||
gtk_widget_grab_focus (demo);
|
||||
|
||||
while (1)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user