diff --git a/gsk/gl/gskglglyphlibrary.c b/gsk/gl/gskglglyphlibrary.c index b35a06f049..ce28ccdce6 100644 --- a/gsk/gl/gskglglyphlibrary.c +++ b/gsk/gl/gskglglyphlibrary.c @@ -30,6 +30,13 @@ #include "gskdebugprivate.h" +#ifdef HAVE_PANGOFT +#include +#include +#include FT_FREETYPE_H +#include FT_PARAMETER_TAGS_H +#endif + #define MAX_GLYPH_SIZE 128 G_DEFINE_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK_TYPE_GL_TEXTURE_LIBRARY) @@ -218,6 +225,16 @@ render_glyph (cairo_surface_t *surface, cairo_t *cr; PangoGlyphString glyph_string; PangoGlyphInfo glyph_info; +#ifdef HAVE_PANGOFT + FT_Face face; + FT_Bool darken = 1; + FT_Parameter property = { FT_PARAM_TAG_STEM_DARKENING, &darken }; + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + face = pango_fc_font_lock_face (PANGO_FC_FONT (key->font)); +G_GNUC_END_IGNORE_DEPRECATIONS + FT_Face_Properties (face, 1, &property); +#endif g_assert (surface != NULL); @@ -236,6 +253,12 @@ render_glyph (cairo_surface_t *surface, cairo_destroy (cr); cairo_surface_flush (surface); + +#ifdef HAVE_PANGOFT +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + pango_fc_font_unlock_face (PANGO_FC_FONT (key->font)); +G_GNUC_END_IGNORE_DEPRECATIONS +#endif } static void diff --git a/gsk/gl/gskglprograms.defs b/gsk/gl/gskglprograms.defs index 0eabd8a9bd..daecfc8c9e 100644 --- a/gsk/gl/gskglprograms.defs +++ b/gsk/gl/gskglprograms.defs @@ -102,3 +102,8 @@ GSK_GL_DEFINE_PROGRAM_NO_CLIP (external, GSK_GL_DEFINE_PROGRAM_NO_CLIP (premultiply, GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("premultiply.glsl")), GSK_GL_NO_UNIFORMS) + +GSK_GL_DEFINE_PROGRAM (colorconvert, + GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("colorconvert.glsl")), + GSK_GL_ADD_UNIFORM (1, TO_LINEAR, u_to_linear)) + diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index 9530d316b4..dc4f0e3635 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -362,6 +363,7 @@ gsk_gl_renderer_render (GskRenderer *renderer, GdkSurface *surface; gboolean clear_framebuffer; float scale; + GdkColorState *target_color_state; g_assert (GSK_IS_GL_RENDERER (renderer)); g_assert (root != NULL); @@ -384,6 +386,17 @@ gsk_gl_renderer_render (GskRenderer *renderer, gsk_render_node_get_preferred_depth (root), update_area); + if (gdk_surface_get_gl_is_srgb (surface)) + { + g_debug ("Relying on GL to do srgb-linear->srgb conversion"); + target_color_state = GDK_COLOR_STATE_SRGB_LINEAR; + } + else + { + g_debug ("Using an offscreen for srgb-linear->srgb conversion"); + target_color_state = GDK_COLOR_STATE_SRGB; + } + gdk_gl_context_make_current (self->context); /* Must be called *AFTER* gdk_draw_context_begin_frame() */ @@ -392,7 +405,7 @@ gsk_gl_renderer_render (GskRenderer *renderer, gsk_gl_driver_begin_frame (self->driver, self->command_queue); job = gsk_gl_render_job_new (self->driver, &viewport, scale, render_region, 0, clear_framebuffer); - gsk_gl_render_job_render (job, root); + gsk_gl_render_job_render (job, root, target_color_state); gsk_gl_driver_end_frame (self->driver); gsk_gl_render_job_free (job); @@ -476,7 +489,7 @@ gsk_gl_renderer_render_texture (GskRenderer *renderer, { gsk_gl_driver_begin_frame (self->driver, self->command_queue); job = gsk_gl_render_job_new (self->driver, viewport, 1, NULL, render_target->framebuffer_id, TRUE); - gsk_gl_render_job_render_flipped (job, root); + gsk_gl_render_job_render_flipped (job, root, GDK_COLOR_STATE_SRGB); texture_id = gsk_gl_driver_release_render_target (self->driver, render_target, FALSE); texture = gsk_gl_driver_create_gdk_texture (self->driver, texture_id, gdk_format); gsk_gl_driver_end_frame (self->driver); diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c index 10f4184da6..d651f75557 100644 --- a/gsk/gl/gskglrenderjob.c +++ b/gsk/gl/gskglrenderjob.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -972,11 +973,28 @@ gsk_gl_render_job_update_clip (GskGLRenderJob *job, return TRUE; } + +static inline float +srgb_inverse_transfer_function (float v) +{ + if (v >= 0.04045) + return powf (((v + 0.055)/(1 + 0.055)), 2.4); + else + return v / 12.92; +} + static inline void rgba_to_half (const GdkRGBA *rgba, guint16 h[4]) { - float_to_half4 ((const float *)rgba, h); + float v[4]; + + v[0] = srgb_inverse_transfer_function (rgba->red); + v[1] = srgb_inverse_transfer_function (rgba->green); + v[2] = srgb_inverse_transfer_function (rgba->blue); + v[3] = rgba->alpha; + + float_to_half4 (v, h); } /* fill_vertex_data */ @@ -3677,8 +3695,11 @@ gsk_gl_render_job_visit_texture (GskGLRenderJob *job, g_assert (offscreen.texture_id); g_assert (offscreen.was_offscreen == FALSE); - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) + if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, colorconvert))) { + gsk_gl_program_set_uniform1i (job->current_program, + UNIFORM_TO_LINEAR, 0, + 1); gsk_gl_program_set_uniform_texture_with_sync (job->current_program, UNIFORM_SHARED_SOURCE, 0, GL_TEXTURE_2D, @@ -3704,7 +3725,7 @@ gsk_gl_render_job_visit_texture (GskGLRenderJob *job, g_assert (slices != NULL); g_assert (n_slices > 0); - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) + if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, colorconvert))) { for (unsigned int i = 0; i < n_slices; i++) { @@ -3718,6 +3739,9 @@ gsk_gl_render_job_visit_texture (GskGLRenderJob *job, if (i > 0) gsk_gl_render_job_split_draw (job); + gsk_gl_program_set_uniform1i (job->current_program, + UNIFORM_TO_LINEAR, 0, + 1); gsk_gl_program_set_uniform_texture_with_filter (job->current_program, UNIFORM_SHARED_SOURCE, 0, GL_TEXTURE_2D, @@ -3832,8 +3856,11 @@ gsk_gl_render_job_visit_texture_scale_node (GskGLRenderJob *job, u1 = (clip_rect.origin.x + clip_rect.size.width - bounds->origin.x) / bounds->size.width; v1 = (clip_rect.origin.y + clip_rect.size.height - bounds->origin.y) / bounds->size.height; - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) + if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, colorconvert))) { + gsk_gl_program_set_uniform1i (job->current_program, + UNIFORM_TO_LINEAR, 0, + 1); gsk_gl_program_set_uniform_texture_with_sync (job->current_program, UNIFORM_SHARED_SOURCE, 0, GL_TEXTURE_2D, @@ -3859,7 +3886,7 @@ gsk_gl_render_job_visit_texture_scale_node (GskGLRenderJob *job, gsk_gl_driver_slice_texture (job->driver, texture, need_mipmap, &slices, &n_slices); - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) + if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, colorconvert))) { for (guint i = 0; i < n_slices; i++) { @@ -3877,6 +3904,9 @@ gsk_gl_render_job_visit_texture_scale_node (GskGLRenderJob *job, if (i > 0) gsk_gl_render_job_split_draw (job); + gsk_gl_program_set_uniform1i (job->current_program, + UNIFORM_TO_LINEAR, 0, + 1); gsk_gl_program_set_uniform_texture_with_filter (job->current_program, UNIFORM_SHARED_SOURCE, 0, GL_TEXTURE_2D, @@ -3910,8 +3940,11 @@ gsk_gl_render_job_visit_texture_scale_node (GskGLRenderJob *job, gsk_gl_driver_cache_texture (job->driver, &key, texture_id); render_texture: - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) + if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, colorconvert))) { + gsk_gl_program_set_uniform1i (job->current_program, + UNIFORM_TO_LINEAR, 0, + 1); gsk_gl_program_set_uniform_texture (job->current_program, UNIFORM_SHARED_SOURCE, 0, GL_TEXTURE_2D, @@ -4464,7 +4497,8 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob *job, void gsk_gl_render_job_render_flipped (GskGLRenderJob *job, - GskRenderNode *root) + GskRenderNode *root, + GdkColorState *target_color_state) { graphene_matrix_t proj; guint framebuffer_id; @@ -4506,8 +4540,17 @@ gsk_gl_render_job_render_flipped (GskGLRenderJob *job, gsk_gl_render_job_set_alpha (job, 1.0f); gsk_gl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer); gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport); - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) + + GskGLProgram *program; + if (target_color_state == GDK_COLOR_STATE_SRGB) + program = CHOOSE_PROGRAM (job, colorconvert); + else + program = CHOOSE_PROGRAM (job, blit); + + if (gsk_gl_render_job_begin_draw (job, program)) { + if (target_color_state == GDK_COLOR_STATE_SRGB) + gsk_gl_program_set_uniform1i (job->current_program, UNIFORM_TO_LINEAR, 0, 0); gsk_gl_program_set_uniform_texture (job->current_program, UNIFORM_SHARED_SOURCE, 0, GL_TEXTURE_2D, @@ -4528,7 +4571,8 @@ gsk_gl_render_job_render_flipped (GskGLRenderJob *job, void gsk_gl_render_job_render (GskGLRenderJob *job, - GskRenderNode *root) + GskRenderNode *root, + GdkColorState *target_color_state) { G_GNUC_UNUSED gint64 start_time; float scale; @@ -4551,7 +4595,38 @@ gsk_gl_render_job_render (GskGLRenderJob *job, gsk_gl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer); if (job->clear_framebuffer) gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport); - gsk_gl_render_job_visit_node (job, root); + + if (target_color_state == GDK_COLOR_STATE_SRGB_LINEAR) + { + gsk_gl_render_job_visit_node (job, root); + } + else + { + GskGLRenderOffscreen offscreen = {0}; + + offscreen.bounds = &root->bounds; + offscreen.force_offscreen = TRUE; + offscreen.reset_clip = TRUE; + offscreen.do_not_cache = TRUE; + + gsk_gl_render_job_visit_node_with_offscreen (job, root, &offscreen); + + g_assert (offscreen.texture_id); + + if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, colorconvert))) + { + gsk_gl_program_set_uniform1i (job->current_program, UNIFORM_TO_LINEAR, 0, 0); + gsk_gl_program_set_uniform_texture (job->current_program, + UNIFORM_SHARED_SOURCE, 0, + GL_TEXTURE_2D, + GL_TEXTURE0, + offscreen.texture_id); + job->source_is_glyph_atlas = FALSE; + gsk_gl_render_job_draw_offscreen_rect (job, &root->bounds); + gsk_gl_render_job_end_draw (job); + } + } + gdk_gl_context_pop_debug_group (job->command_queue->context); gdk_profiler_end_mark (start_time, "Build GL command queue", ""); diff --git a/gsk/gl/gskglrenderjobprivate.h b/gsk/gl/gskglrenderjobprivate.h index 2e38b07a2e..cb51d0fc4c 100644 --- a/gsk/gl/gskglrenderjobprivate.h +++ b/gsk/gl/gskglrenderjobprivate.h @@ -30,7 +30,9 @@ GskGLRenderJob *gsk_gl_render_job_new (GskGLDriver *dri gboolean clear_framebuffer); void gsk_gl_render_job_free (GskGLRenderJob *job); void gsk_gl_render_job_render (GskGLRenderJob *job, - GskRenderNode *root); + GskRenderNode *root, + GdkColorState *color_state); void gsk_gl_render_job_render_flipped (GskGLRenderJob *job, - GskRenderNode *root); + GskRenderNode *root, + GdkColorState *color_state); diff --git a/gsk/gl/resources/colorconvert.glsl b/gsk/gl/resources/colorconvert.glsl new file mode 100644 index 0000000000..8a7cec0fae --- /dev/null +++ b/gsk/gl/resources/colorconvert.glsl @@ -0,0 +1,63 @@ +// VERTEX_SHADER: +// colorconvert.glsl + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); +} + +// FRAGMENT_SHADER: +// colorconvert.glsl + +uniform int u_to_linear; + +float +srgb_transfer_function (float v) +{ + if (v >= 0.04045) + return pow (((v + 0.055)/(1.0 + 0.055)), 2.4); + else + return v / 12.92; +} + +float +srgb_inverse_transfer_function (float v) +{ + if (v > 0.0031308) + return 1.055 * pow (v, 1.0/2.4) - 0.055; + else + return 12.92 * v; +} + +vec4 +srgb_to_linear_srgb (vec4 color) +{ + return vec4 (srgb_transfer_function (color.r), + srgb_transfer_function (color.g), + srgb_transfer_function (color.b), + color.a); +} + +vec4 +linear_srgb_to_srgb (vec4 color) +{ + return vec4 (srgb_inverse_transfer_function (color.r), + srgb_inverse_transfer_function (color.g), + srgb_inverse_transfer_function (color.b), + color.a); +} + +void main() { + vec4 color = GskTexture(u_source, vUv); + + if (color.a != 0.0) + color.rgb /= color.a; + + if (u_to_linear != 0) + color = srgb_to_linear_srgb (color); + else + color = linear_srgb_to_srgb (color); + + gskSetOutputColor(gsk_scaled_premultiply(color, u_alpha)); +} diff --git a/gsk/meson.build b/gsk/meson.build index 5023cde38e..95e9b07777 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -4,6 +4,7 @@ gsk_private_gl_shaders = [ 'gl/resources/preamble.vs.glsl', 'gl/resources/border.glsl', 'gl/resources/blit.glsl', + 'gl/resources/colorconvert.glsl', 'gl/resources/coloring.glsl', 'gl/resources/color.glsl', 'gl/resources/linear_gradient.glsl',