[gtk/matthiasc/color-profiles] Linearize gl textures




commit c2af5d917ed2a6bce3256038e2e5ae2d3f1f5ba2
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Sep 30 01:08:16 2021 -0400

    Linearize gl textures
    
    GL textures are typically in non-linear sRGB,
    and in that case, we need to linearize them.

 gsk/meson.build                  |   1 +
 gsk/ngl/gskngldriver.c           | 120 ++++++++++++++++++++++++++++++++++++++-
 gsk/ngl/gsknglprograms.defs      |   4 ++
 gsk/ngl/resources/linearize.glsl |  43 ++++++++++++++
 4 files changed, 167 insertions(+), 1 deletion(-)
---
diff --git a/gsk/meson.build b/gsk/meson.build
index eb33866da5..7c1442cfd6 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -20,6 +20,7 @@ gsk_private_ngl_shaders = [
   'ngl/resources/custom.glsl',
   'ngl/resources/filled_border.glsl',
   'ngl/resources/postprocessing.glsl',
+  'ngl/resources/linearize.glsl',
 ]
 
 gsk_public_sources = files([
diff --git a/gsk/ngl/gskngldriver.c b/gsk/ngl/gskngldriver.c
index 07ac4b3c75..1ac952cf12 100644
--- a/gsk/ngl/gskngldriver.c
+++ b/gsk/ngl/gskngldriver.c
@@ -39,6 +39,7 @@
 #include "gsknglprogramprivate.h"
 #include "gsknglshadowlibraryprivate.h"
 #include "gskngltexturepoolprivate.h"
+#include "fp16private.h"
 
 #define ATLAS_SIZE 512
 #define MAX_OLD_RATIO 0.5
@@ -705,6 +706,74 @@ gsk_ngl_driver_cache_texture (GskNglDriver        *self,
   g_hash_table_insert (self->texture_id_to_key, GUINT_TO_POINTER (texture_id), k);
 }
 
+static void
+draw_offscreen (GskNglCommandQueue *command_queue,
+                float               min_x,
+                float               min_y,
+                float               max_x,
+                float               max_y)
+{
+  GskNglDrawVertex *vertices = gsk_ngl_command_queue_add_vertices (command_queue);
+  float min_u = 0;
+  float min_v = 1;
+  float max_u = 1;
+  float max_v = 0;
+  guint16 c[4] = { FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO };
+
+  vertices[0] = (GskNglDrawVertex) { .position = { min_x, min_y }, .uv = { min_u, min_v }, .color = { c[0], 
c[1], c[2], c[3] } };
+  vertices[1] = (GskNglDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c[0], 
c[1], c[2], c[3] } };
+  vertices[2] = (GskNglDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c[0], 
c[1], c[2], c[3] } };
+  vertices[3] = (GskNglDrawVertex) { .position = { max_x, max_y }, .uv = { max_u, max_v }, .color = { c[0], 
c[1], c[2], c[3] } };
+  vertices[4] = (GskNglDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c[0], 
c[1], c[2], c[3] } };
+  vertices[5] = (GskNglDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c[0], 
c[1], c[2], c[3] } };
+}
+
+static void
+set_viewport_for_size (GskNglProgram *program,
+                         float         width,
+                         float         height)
+{
+  float viewport[4] = { 0, 0, width, height };
+
+  gsk_ngl_uniform_state_set4fv (program->uniforms,
+                                program->program_info,
+                                UNIFORM_SHARED_VIEWPORT, 0,
+                                1,
+                                (const float *)&viewport);
+}
+
+#define ORTHO_NEAR_PLANE   -10000
+#define ORTHO_FAR_PLANE     10000
+
+static void
+set_projection_for_size (GskNglProgram *program,
+                       float            width,
+                       float            height)
+{
+  graphene_matrix_t projection;
+
+  graphene_matrix_init_ortho (&projection, 0, width, 0, height, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE);
+  graphene_matrix_scale (&projection, 1, -1, 1);
+
+  gsk_ngl_uniform_state_set_matrix (program->uniforms,
+                                    program->program_info,
+                                    UNIFORM_SHARED_PROJECTION, 0,
+                                    &projection);
+}
+
+static void
+reset_modelview (GskNglProgram *program)
+{
+  graphene_matrix_t modelview;
+
+  graphene_matrix_init_identity (&modelview);
+
+  gsk_ngl_uniform_state_set_matrix (program->uniforms,
+                                    program->program_info,
+                                    UNIFORM_SHARED_MODELVIEW, 0,
+                                    &modelview);
+}
+
 /**
  * gsk_ngl_driver_load_texture:
  * @self: a `GdkTexture`
@@ -754,8 +823,57 @@ gsk_ngl_driver_load_texture (GskNglDriver *self,
 
       if (gdk_gl_context_is_shared (context, texture_context))
         {
+          guint gl_texture_id;
+
+          gl_texture_id = gdk_gl_texture_get_id (gl_texture);
+
           /* A GL texture from the same GL context is a simple task... */
-          return gdk_gl_texture_get_id (gl_texture);
+          if (gdk_color_profile_is_linear (gdk_texture_get_color_profile (texture)))
+            {
+              return gl_texture_id;
+            }
+          else
+            {
+              GskNglRenderTarget *target;
+              guint prev_fbo;
+
+              /* The GL texture isn't linear sRGB, so we need to convert
+               * it before we can use it. For now, we just assume that it
+               * is nonlinear sRGB. Eventually, we should figure out how
+               * to convert from other color spaces to linear sRGB
+               */
+              gdk_gl_context_make_current (context);
+
+              width = gdk_texture_get_width (texture);
+              height = gdk_texture_get_height (texture);
+
+              gsk_ngl_driver_create_render_target (self,
+                                                   width, height,
+                                                   min_filter, mag_filter,
+                                                   &target);
+
+              prev_fbo = gsk_ngl_command_queue_bind_framebuffer (self->command_queue, 
target->framebuffer_id);
+              gsk_ngl_command_queue_clear (self->command_queue, 0, &GRAPHENE_RECT_INIT (0, 0, width, 
height));
+
+              gsk_ngl_command_queue_begin_draw (self->command_queue,
+                                                self->linearize_no_clip->program_info,
+                                                width, height);
+
+              set_projection_for_size (self->linearize_no_clip, width, height);
+              set_viewport_for_size (self->linearize_no_clip, width, height);
+              reset_modelview (self->linearize_no_clip);
+
+              gsk_ngl_program_set_uniform_texture (self->linearize_no_clip,
+                                                   UNIFORM_SHARED_SOURCE, 0,
+                                                   GL_TEXTURE_2D, GL_TEXTURE0, gl_texture_id);
+              draw_offscreen (self->command_queue, 0, 0, width, height);
+
+              gsk_ngl_command_queue_end_draw (self->command_queue);
+
+              gsk_ngl_command_queue_bind_framebuffer (self->command_queue, prev_fbo);
+
+              return gsk_ngl_driver_release_render_target (self, target, FALSE);
+            }
         }
       else
         {
diff --git a/gsk/ngl/gsknglprograms.defs b/gsk/ngl/gsknglprograms.defs
index 72b383b8fa..1454e56770 100644
--- a/gsk/ngl/gsknglprograms.defs
+++ b/gsk/ngl/gsknglprograms.defs
@@ -86,3 +86,7 @@ GSK_NGL_DEFINE_PROGRAM (unblurred_outset_shadow,
 GSK_NGL_DEFINE_PROGRAM (postprocessing,
                         "/org/gtk/libgsk/ngl/postprocessing.glsl",
                         GSK_NGL_NO_UNIFORMS)
+
+GSK_NGL_DEFINE_PROGRAM (linearize,
+                        "/org/gtk/libgsk/ngl/linearize.glsl",
+                        GSK_NGL_NO_UNIFORMS)
diff --git a/gsk/ngl/resources/linearize.glsl b/gsk/ngl/resources/linearize.glsl
new file mode 100644
index 0000000000..73806e156a
--- /dev/null
+++ b/gsk/ngl/resources/linearize.glsl
@@ -0,0 +1,43 @@
+
+// VERTEX_SHADER:
+// linearize.glsl
+
+void main() {
+  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+  vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+// linearize.glsl
+
+float u_gamma = 2.2;
+
+vec4 gsk_unpremultiply(vec4 c)
+{
+  if (c.a != 0)
+    return vec4(c.rgb / c.a, c.a);
+  else
+    return c;
+}
+
+vec4 gsk_srgb_to_linear(vec4 srgb)
+{
+  vec3 linear_rgb = pow(srgb.rgb, vec3(u_gamma));
+  return vec4(linear_rgb, srgb.a);
+}
+
+vec4 gsk_linear_to_srgb(vec4 linear_rgba)
+{
+  vec3 srgb = pow(linear_rgba.rgb , vec3(1/u_gamma));
+  return vec4(srgb, linear_rgba.a);
+}
+void main() {
+  vec4 diffuse = GskTexture(u_source, vUv);
+
+  diffuse = gsk_unpremultiply(diffuse);
+  diffuse = gsk_srgb_to_linear(diffuse);
+  diffuse = gsk_premultiply(diffuse);
+
+  gskSetOutputColor(diffuse);
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]