[gtk/wip/otte/dithered-gradients: 1/3] gsk/gl: Apply dither to gradients and shadows




commit c39a551fee3a14b88cce7d2d5c4e57578dd45bd2
Author: Sebastian Wick <sebastian wick redhat com>
Date:   Tue May 3 15:22:49 2022 +0200

    gsk/gl: Apply dither to gradients and shadows

 gsk/gl/gskglcommandqueue.c            |  16 +++++-
 gsk/gl/gskglcommandqueueprivate.h     |   3 +-
 gsk/gl/gskgldriver.c                  |   7 ++-
 gsk/gl/gskgldriverprivate.h           |   2 +
 gsk/gl/gskglrenderjob.c               | 103 +++++++++++++++++++++++++---------
 gsk/gl/resources/conic_gradient.glsl  |   2 +-
 gsk/gl/resources/linear_gradient.glsl |   6 +-
 gsk/gl/resources/outset_shadow.glsl   |   2 +-
 gsk/gl/resources/preamble.fs.glsl     |  65 ++++++++++++++-------
 gsk/gl/resources/radial_gradient.glsl |   2 +-
 10 files changed, 153 insertions(+), 55 deletions(-)
---
diff --git a/gsk/gl/gskglcommandqueue.c b/gsk/gl/gskglcommandqueue.c
index 4d7ef4b3d3..ed70fb1aad 100644
--- a/gsk/gl/gskglcommandqueue.c
+++ b/gsk/gl/gskglcommandqueue.c
@@ -1246,10 +1246,13 @@ gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self,
                                            int                min_filter,
                                            int                mag_filter,
                                            guint             *out_fbo_id,
-                                           guint             *out_texture_id)
+                                           guint             *out_texture_id,
+                                           int               *out_bit_depth)
 {
+  GdkGLContext *context = self->context;
   GLuint fbo_id = 0;
   GLint texture_id;
+  int bit_depth;
 
   g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
   g_assert (width > 0);
@@ -1266,6 +1269,7 @@ gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self,
     {
       *out_fbo_id = 0;
       *out_texture_id = 0;
+      *out_bit_depth = 0;
       return FALSE;
     }
 
@@ -1275,8 +1279,18 @@ gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self,
   glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0);
   g_assert_cmphex (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE);
 
+  bit_depth = 8;
+  if (context && gdk_gl_context_check_version (context, 0, 0, 3, 0))
+    {
+      glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER,
+                                             GL_COLOR_ATTACHMENT0,
+                                             GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE,
+                                             &bit_depth);
+    }
+
   *out_fbo_id = fbo_id;
   *out_texture_id = texture_id;
+  *out_bit_depth = bit_depth;
 
   return TRUE;
 }
diff --git a/gsk/gl/gskglcommandqueueprivate.h b/gsk/gl/gskglcommandqueueprivate.h
index 48911305a8..a3985370ee 100644
--- a/gsk/gl/gskglcommandqueueprivate.h
+++ b/gsk/gl/gskglcommandqueueprivate.h
@@ -298,7 +298,8 @@ gboolean            gsk_gl_command_queue_create_render_target (GskGLCommandQueue
                                                                int                   min_filter,
                                                                int                   mag_filter,
                                                                guint                *out_fbo_id,
-                                                               guint                *out_texture_id);
+                                                               guint                *out_texture_id,
+                                                               int                  *out_bit_depth);
 void                gsk_gl_command_queue_delete_program       (GskGLCommandQueue    *self,
                                                                guint                 program_id);
 void                gsk_gl_command_queue_clear                (GskGLCommandQueue    *self,
diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c
index 179e870d80..7bda780159 100644
--- a/gsk/gl/gskgldriver.c
+++ b/gsk/gl/gskgldriver.c
@@ -387,6 +387,7 @@ gsk_gl_driver_load_programs (GskGLDriver  *self,
     gsk_gl_program_add_uniform (program, "u_viewport", UNIFORM_SHARED_VIEWPORT);                \
     gsk_gl_program_add_uniform (program, "u_projection", UNIFORM_SHARED_PROJECTION);            \
     gsk_gl_program_add_uniform (program, "u_modelview", UNIFORM_SHARED_MODELVIEW);              \
+    gsk_gl_program_add_uniform (program, "u_bit_depth", UNIFORM_SHARED_BIT_DEPTH);              \
                                                                                                 \
     uniforms                                                                                    \
                                                                                                 \
@@ -899,6 +900,7 @@ gsk_gl_driver_create_render_target (GskGLDriver        *self,
 {
   guint framebuffer_id;
   guint texture_id;
+  int bit_depth;
 
   g_return_val_if_fail (GSK_IS_GL_DRIVER (self), FALSE);
   g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->command_queue), FALSE);
@@ -927,7 +929,8 @@ gsk_gl_driver_create_render_target (GskGLDriver        *self,
                                                   width, height,
                                                   format,
                                                   min_filter, mag_filter,
-                                                  &framebuffer_id, &texture_id))
+                                                  &framebuffer_id, &texture_id,
+                                                  &bit_depth))
     {
       GskGLRenderTarget *render_target;
 
@@ -939,6 +942,7 @@ gsk_gl_driver_create_render_target (GskGLDriver        *self,
       render_target->height = height;
       render_target->framebuffer_id = framebuffer_id;
       render_target->texture_id = texture_id;
+      render_target->bit_depth = bit_depth;
 
       *out_render_target = render_target;
 
@@ -1098,6 +1102,7 @@ gsk_gl_driver_lookup_shader (GskGLDriver  *self,
           gsk_gl_program_add_uniform (program, "u_viewport", UNIFORM_SHARED_VIEWPORT);
           gsk_gl_program_add_uniform (program, "u_projection", UNIFORM_SHARED_PROJECTION);
           gsk_gl_program_add_uniform (program, "u_modelview", UNIFORM_SHARED_MODELVIEW);
+          gsk_gl_program_add_uniform (program, "u_bit_depth", UNIFORM_SHARED_BIT_DEPTH);
           have_alpha = gsk_gl_program_add_uniform (program, "u_alpha", UNIFORM_SHARED_ALPHA);
 
           gsk_gl_program_add_uniform (program, "u_size", UNIFORM_CUSTOM_SIZE);
diff --git a/gsk/gl/gskgldriverprivate.h b/gsk/gl/gskgldriverprivate.h
index accae5b3bb..1cb439da1a 100644
--- a/gsk/gl/gskgldriverprivate.h
+++ b/gsk/gl/gskgldriverprivate.h
@@ -35,6 +35,7 @@ enum {
   UNIFORM_SHARED_VIEWPORT,
   UNIFORM_SHARED_PROJECTION,
   UNIFORM_SHARED_MODELVIEW,
+  UNIFORM_SHARED_BIT_DEPTH,
 
   UNIFORM_SHARED_LAST
 };
@@ -91,6 +92,7 @@ struct _GskGLRenderTarget
   int format;
   int width;
   int height;
+  int bit_depth;
 };
 
 struct _GskGLDriver
diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c
index 97b3d9d4c9..3d59b2340e 100644
--- a/gsk/gl/gskglrenderjob.c
+++ b/gsk/gl/gskglrenderjob.c
@@ -173,6 +173,10 @@ struct _GskGLRenderJob
    * looking at the format of the framebuffer we are rendering on.
    */
   int target_format;
+
+  /* Bit depth of the framebuffer we render into
+   */
+  int bit_depth;
 };
 
 typedef struct _GskGLRenderOffscreen
@@ -209,6 +213,39 @@ static gboolean gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob
                                                              const GskRenderNode  *node,
                                                              GskGLRenderOffscreen *offscreen);
 
+static int
+get_framebuffer_bit_depth (GdkGLContext *context,
+                           guint         framebuffer)
+{
+  int size;
+
+  if (!gdk_gl_context_check_version (context, 0, 0, 3, 0))
+    return 8;
+
+  glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
+  glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER,
+                                         framebuffer ? GL_COLOR_ATTACHMENT0
+                                                     : gdk_gl_context_get_use_es (context) ? GL_BACK
+                                                                                           : GL_BACK_LEFT,
+                                         GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, &size);
+
+  return size;
+}
+
+static int
+get_framebuffer_format (GdkGLContext *context,
+                        guint         framebuffer)
+{
+  int size = get_framebuffer_bit_depth (context, framebuffer);
+
+  if (size > 16)
+    return GL_RGBA32F;
+  else if (size > 8)
+    return GL_RGBA16F;
+  else
+    return GL_RGBA8;
+}
+
 static inline int
 get_target_format (GskGLRenderJob      *job,
                    const GskRenderNode *node)
@@ -475,6 +512,21 @@ init_projection_matrix (graphene_matrix_t     *projection,
   graphene_matrix_scale (projection, 1, -1, 1);
 }
 
+static inline int
+gsk_gl_render_job_set_bit_depth (GskGLRenderJob *job,
+                                 int             bit_depth)
+{
+  if (job->bit_depth != bit_depth)
+    {
+      int ret = job->bit_depth;
+      job->bit_depth = bit_depth;
+      job->driver->stamps[UNIFORM_SHARED_BIT_DEPTH]++;
+      return ret;
+    }
+
+  return bit_depth;
+}
+
 static inline float
 gsk_gl_render_job_set_alpha (GskGLRenderJob *job,
                              float           alpha)
@@ -1157,6 +1209,12 @@ gsk_gl_render_job_begin_draw (GskGLRenderJob *job,
                               UNIFORM_SHARED_ALPHA,
                               job->driver->stamps[UNIFORM_SHARED_ALPHA],
                               job->alpha);
+
+  gsk_gl_uniform_state_set1f (program->uniforms,
+                              program->program_info,
+                              UNIFORM_SHARED_BIT_DEPTH,
+                              job->driver->stamps[UNIFORM_SHARED_BIT_DEPTH],
+                              job->bit_depth);
 }
 
 #define CHOOSE_PROGRAM(job,name) \
@@ -1312,6 +1370,7 @@ blur_offscreen (GskGLRenderJob       *job,
   graphene_matrix_t prev_projection;
   graphene_rect_t prev_viewport;
   guint prev_fbo;
+  int prev_bit_depth;
 
   g_assert (blur_radius_x > 0);
   g_assert (blur_radius_y > 0);
@@ -1346,6 +1405,7 @@ blur_offscreen (GskGLRenderJob       *job,
   /* Bind new framebuffer and clear it */
   prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, pass1->framebuffer_id);
   gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
+  prev_bit_depth = gsk_gl_render_job_set_bit_depth (job, pass1->bit_depth);
 
   /* Begin drawing the first horizontal pass, using offscreen as the
    * source texture for the program.
@@ -1375,6 +1435,7 @@ blur_offscreen (GskGLRenderJob       *job,
   /* Bind second pass framebuffer and clear it */
   gsk_gl_command_queue_bind_framebuffer (job->command_queue, pass2->framebuffer_id);
   gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
+  gsk_gl_render_job_set_bit_depth (job, pass2->bit_depth);
 
   /* Draw using blur program with first pass as source texture */
   gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blur));
@@ -1404,6 +1465,7 @@ blur_offscreen (GskGLRenderJob       *job,
   gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL);
   gsk_gl_render_job_set_projection (job, &prev_projection);
   gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo);
+  gsk_gl_render_job_set_bit_depth (job, prev_bit_depth);
 
   gsk_gl_driver_release_render_target (job->driver, pass1, TRUE);
 
@@ -2223,6 +2285,7 @@ gsk_gl_render_job_visit_blurred_inset_shadow_node (GskGLRenderJob      *job,
       graphene_matrix_t prev_projection;
       graphene_rect_t prev_viewport;
       guint prev_fbo;
+      int prev_bit_depth;
 
       /* TODO: In the following code, we have to be careful about where we apply the scale.
        * We're manually scaling stuff (e.g. the outline) so we can later use texture_width
@@ -2265,6 +2328,7 @@ gsk_gl_render_job_visit_blurred_inset_shadow_node (GskGLRenderJob      *job,
 
       prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
       gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
+      prev_bit_depth = gsk_gl_render_job_set_bit_depth (job, render_target->bit_depth);
 
       gsk_gl_render_job_transform_rounded_rect (job, &outline_to_blur, &transformed_outline);
 
@@ -2291,6 +2355,7 @@ gsk_gl_render_job_visit_blurred_inset_shadow_node (GskGLRenderJob      *job,
       gsk_gl_render_job_set_projection (job, &prev_projection);
       gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL);
       gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo);
+      gsk_gl_render_job_set_bit_depth (job, prev_bit_depth);
 
       offscreen.texture_id = render_target->texture_id;
       init_full_texture_region (&offscreen);
@@ -2523,6 +2588,7 @@ gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob      *job,
       graphene_matrix_t prev_projection;
       graphene_rect_t prev_viewport;
       guint prev_fbo;
+      int prev_bit_depth;
 
       gsk_gl_driver_create_render_target (job->driver,
                                           texture_width, texture_height,
@@ -2553,6 +2619,7 @@ gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob      *job,
       /* Bind render target and clear it */
       prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
       gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
+      prev_bit_depth = gsk_gl_render_job_set_bit_depth (job, render_target->bit_depth);
 
       /* Draw the outline using color program */
       gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, color));
@@ -2582,6 +2649,7 @@ gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob      *job,
                                     blurred_texture_id);
 
       gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo);
+      gsk_gl_render_job_set_bit_depth (job, prev_bit_depth);
     }
   else
     {
@@ -3924,6 +3992,7 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob       *job,
   graphene_rect_t prev_viewport;
   float prev_alpha;
   guint prev_fbo;
+  int prev_bit_depth;
 
   if (!gsk_gl_driver_create_render_target (job->driver,
                                            texture_width, texture_height,
@@ -3954,6 +4023,7 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob       *job,
 
   prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
   gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
+  prev_bit_depth = gsk_gl_render_job_set_bit_depth (job, render_target->bit_depth);
 
   if (offscreen->reset_clip)
     gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (job->viewport));
@@ -3973,6 +4043,7 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob       *job,
   gsk_gl_render_job_set_projection (job, &prev_projection);
   gsk_gl_render_job_set_alpha (job, prev_alpha);
   gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo);
+  gsk_gl_render_job_set_bit_depth (job, prev_bit_depth);
 
   job->offset_x = offset_x;
   job->offset_y = offset_y;
@@ -3996,6 +4067,8 @@ gsk_gl_render_job_render_flipped (GskGLRenderJob *job,
   guint framebuffer_id;
   guint texture_id;
   guint surface_height;
+  int bit_depth;
+  int prev_bit_depth;
 
   g_return_if_fail (job != NULL);
   g_return_if_fail (root != NULL);
@@ -4017,12 +4090,14 @@ gsk_gl_render_job_render_flipped (GskGLRenderJob *job,
                                                   MAX (1, job->viewport.size.height),
                                                   job->target_format,
                                                   GL_NEAREST, GL_NEAREST,
-                                                  &framebuffer_id, &texture_id))
+                                                  &framebuffer_id, &texture_id,
+                                                  &bit_depth))
     return;
 
   /* Setup drawing to our offscreen texture/framebuffer which is flipped */
   gsk_gl_command_queue_bind_framebuffer (job->command_queue, framebuffer_id);
   gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
+  prev_bit_depth = gsk_gl_render_job_set_bit_depth (job, bit_depth);
 
   /* Visit all nodes creating batches */
   gdk_gl_context_push_debug_group (job->command_queue->context, "Building command queue");
@@ -4033,6 +4108,7 @@ 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);
+  gsk_gl_render_job_set_bit_depth (job, prev_bit_depth);
   gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
   gsk_gl_program_set_uniform_texture (job->current_program,
                                       UNIFORM_SHARED_SOURCE, 0,
@@ -4107,30 +4183,6 @@ gsk_gl_render_job_set_debug_fallback (GskGLRenderJob *job,
   job->debug_fallback = !!debug_fallback;
 }
 
-static int
-get_framebuffer_format (GdkGLContext *context,
-                        guint         framebuffer)
-{
-  int size;
-
-  if (!gdk_gl_context_check_version (context, 0, 0, 3, 0))
-    return GL_RGBA8;
-
-  glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
-  glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER,
-                                         framebuffer ? GL_COLOR_ATTACHMENT0
-                                                     : gdk_gl_context_get_use_es (context) ? GL_BACK
-                                                                                           : GL_BACK_LEFT,
-                                         GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, &size);
-
-  if (size > 16)
-    return GL_RGBA32F;
-  else if (size > 8)
-    return GL_RGBA16F;
-  else
-    return GL_RGBA8;
-}
-
 GskGLRenderJob *
 gsk_gl_render_job_new (GskGLDriver           *driver,
                        const graphene_rect_t *viewport,
@@ -4177,6 +4229,7 @@ gsk_gl_render_job_new (GskGLDriver           *driver,
   gsk_gl_render_job_set_alpha (job, 1.0f);
   gsk_gl_render_job_set_projection_from_rect (job, viewport, NULL);
   gsk_gl_render_job_set_modelview (job, gsk_transform_scale (NULL, scale_factor, scale_factor));
+  gsk_gl_render_job_set_bit_depth (job, get_framebuffer_bit_depth (job->command_queue->context, 
framebuffer));
 
   /* Setup our initial clip. If region is NULL then we are drawing the
    * whole viewport. Otherwise, we need to convert the region to a
diff --git a/gsk/gl/resources/conic_gradient.glsl b/gsk/gl/resources/conic_gradient.glsl
index f1d33cd6a7..422a9b4563 100644
--- a/gsk/gl/resources/conic_gradient.glsl
+++ b/gsk/gl/resources/conic_gradient.glsl
@@ -78,7 +78,7 @@ void main() {
       vec4 curr_color = gsk_premultiply(get_color(i));
       vec4 next_color = gsk_premultiply(get_color(i + 1));
       vec4 color = mix(curr_color, next_color, f);
-      gskSetScaledOutputColor(color, u_alpha);
+      gskSetScaledDitheredOutputColor(color, u_alpha);
       return;
     }
   }
diff --git a/gsk/gl/resources/linear_gradient.glsl b/gsk/gl/resources/linear_gradient.glsl
index fa130be4cb..5ff81c75fb 100644
--- a/gsk/gl/resources/linear_gradient.glsl
+++ b/gsk/gl/resources/linear_gradient.glsl
@@ -87,7 +87,8 @@ void main() {
   }
 
   if (offset >= get_offset(u_num_color_stops - 1)) {
-    gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha));
+    vec4 color = get_color(u_num_color_stops - 1);
+    gskSetDitheredOutputColor(gsk_scaled_premultiply(color, u_alpha));
     return;
   }
 
@@ -99,8 +100,7 @@ void main() {
       float f = (offset - curr_offset) / (next_offset - curr_offset);
       vec4 curr_color = gsk_premultiply(get_color(i));
       vec4 next_color = gsk_premultiply(get_color(i + 1));
-      vec4 color = mix(curr_color, next_color, f);
-      gskSetScaledOutputColor(color, u_alpha);
+      gskSetScaledDitheredOutputColor(mix(curr_color, next_color, f), u_alpha);
       return;
     }
   }
diff --git a/gsk/gl/resources/outset_shadow.glsl b/gsk/gl/resources/outset_shadow.glsl
index 44b05aa152..2a262cf066 100644
--- a/gsk/gl/resources/outset_shadow.glsl
+++ b/gsk/gl/resources/outset_shadow.glsl
@@ -30,5 +30,5 @@ void main() {
 
   alpha *= (1.0 -  clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outline), frag), 0.0, 1.0));
 
-  gskSetScaledOutputColor(final_color, alpha);
+  gskSetScaledDitheredOutputColor(final_color, alpha);
 }
diff --git a/gsk/gl/resources/preamble.fs.glsl b/gsk/gl/resources/preamble.fs.glsl
index c1c5a954ee..534311f4db 100644
--- a/gsk/gl/resources/preamble.fs.glsl
+++ b/gsk/gl/resources/preamble.fs.glsl
@@ -4,6 +4,7 @@ uniform mat4 u_modelview;
 uniform float u_alpha;
 uniform vec4 u_viewport;
 uniform vec4[3] u_clip_rect;
+uniform float u_bit_depth;
 
 #if defined(GSK_LEGACY)
 _OUT_ vec4 outputColor;
@@ -117,29 +118,25 @@ vec2 gsk_get_frag_coord() {
   return fc;
 }
 
-void gskSetOutputColor(vec4 color) {
-  vec4 result;
+// from "NEXT GENERATION POST PROCESSING IN CALL OF DUTY: ADVANCED WARFARE"
+// http://advances.realtimerendering.com/s2014/index.html
+float gsk_interleaved_gradient_noise(vec2 uv) {
+    const vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189);
+    return fract(magic.z * fract(dot(uv, magic.xy)));
+}
 
-#if defined(NO_CLIP)
-  result = color;
-#elif defined(RECT_CLIP)
-  float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
-                                     gsk_get_frag_coord());
-  result = color * coverage;
-#else
-  float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
-                                             gsk_get_frag_coord());
-  result = color * coverage;
-#endif
+vec4 gskDither(vec4 color) {
+  if (u_bit_depth > 10)
+    return color;
 
-#if defined(GSK_GLES) || defined(GSK_LEGACY)
-  gl_FragColor = result;
-#else
-  outputColor = result;
-#endif
+  float noise = gsk_interleaved_gradient_noise(gsk_get_frag_coord()) - 0.5;
+  // scale the noise to the number of steps we have
+  noise = noise / (pow(2.0, u_bit_depth) - 1.0);
+  color = vec4(color.rgb + noise, color.a);
+  return color;
 }
 
-void gskSetScaledOutputColor(vec4 color, float alpha) {
+vec4 gsk_get_output_color(vec4 color, float alpha) {
   vec4 result;
 
 #if defined(NO_CLIP)
@@ -154,9 +151,35 @@ void gskSetScaledOutputColor(vec4 color, float alpha) {
   result = color * (alpha * coverage);
 #endif
 
+  return result;
+}
+
+void gsk_set_output_color(vec4 color) {
 #if defined(GSK_GLES) || defined(GSK_LEGACY)
-  gl_FragColor = result;
+  gl_FragColor = color;
 #else
-  outputColor = result;
+  outputColor = color;
 #endif
 }
+
+void gskSetOutputColor(vec4 color) {
+  vec4 result = gsk_get_output_color(color, 1.0);
+  gsk_set_output_color(result);
+}
+
+void gskSetDitheredOutputColor(vec4 color) {
+  vec4 result = gsk_get_output_color(color, 1.0);
+  result = gskDither(result);
+  gsk_set_output_color(result);
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+  vec4 result = gsk_get_output_color(color, alpha);
+  gsk_set_output_color(result);
+}
+
+void gskSetScaledDitheredOutputColor(vec4 color, float alpha) {
+  vec4 result = gsk_get_output_color(color, alpha);
+  result = gskDither(result);
+  gsk_set_output_color(result);
+}
diff --git a/gsk/gl/resources/radial_gradient.glsl b/gsk/gl/resources/radial_gradient.glsl
index 59fad00290..82c5600769 100644
--- a/gsk/gl/resources/radial_gradient.glsl
+++ b/gsk/gl/resources/radial_gradient.glsl
@@ -80,7 +80,7 @@ void main() {
       vec4 curr_color = gsk_premultiply(get_color(i));
       vec4 next_color = gsk_premultiply(get_color(i + 1));
       vec4 color = mix(curr_color, next_color, f);
-      gskSetScaledOutputColor(color, u_alpha);
+      gskSetScaledDitheredOutputColor(color, u_alpha);
       return;
     }
   }


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