gsk: Fix luminance in Cairo and GL renderer
authorBenjamin Otte <otte@redhat.com>
Mon, 3 Jul 2023 18:45:53 +0000 (20:45 +0200)
committerBenjamin Otte <otte@redhat.com>
Mon, 3 Jul 2023 20:02:44 +0000 (22:02 +0200)
In particular, fix the combination of luminance and alpha. We want to do
  mask = luminance * alpha
and for inverted
  mask = (1.0 - luminance) * alpha
so add a test that makes sure we do that and then fix the code and
existing tests to conform to it.

gsk/gl/resources/mask.glsl
gsk/gskrendernodeimpl.c
testsuite/gsk/compare/mask-modes-with-alpha.node [new file with mode: 0644]
testsuite/gsk/compare/mask-modes-with-alpha.png [new file with mode: 0644]
testsuite/gsk/compare/mask-modes.png
testsuite/gsk/meson.build

index 602c83dd643e0de03dc5c7dbbcd7febf7ec06c32..0c2814be9f132865c53bcc3a476473f8660993ee 100644 (file)
@@ -13,6 +13,12 @@ void main() {
 uniform int u_mode;
 uniform sampler2D u_mask;
 
+float
+luminance (vec3 color)
+{
+  return dot (vec3 (0.2126, 0.7152, 0.0722), color);
+}
+
 void main() {
   vec4 source = GskTexture(u_source, vUv);
   vec4 mask = GskTexture(u_mask, vUv);
@@ -23,9 +29,9 @@ void main() {
   else if (u_mode == 1)
     mask_value = 1.0 - mask.a;
   else if (u_mode == 2)
-    mask_value = (0.2126 * mask.r + 0.7152 * mask.g + 0.0722 * mask.b) * mask.a;
+    mask_value = luminance (mask.rgb);
   else if (u_mode == 3)
-    mask_value = 1.0 - (0.2126 * mask.r + 0.7152 * mask.g + 0.0722 * mask.b) * mask.a;
+    mask_value = mask.a - luminance (mask.rgb);
   else
     mask_value = 0.0;
 
index e3ca2b8b73ad88169b469a44ddd6fe23361b8b39..54234992f99b8417982be1e40ca599c973224ffc 100644 (file)
@@ -3702,8 +3702,7 @@ gsk_color_matrix_node_finalize (GskRenderNode *node)
 static void
 apply_color_matrix_to_pattern (cairo_pattern_t         *pattern,
                                const graphene_matrix_t *color_matrix,
-                               const graphene_vec4_t   *color_offset,
-                               gboolean                 multiply_alpha)
+                               const graphene_vec4_t   *color_offset)
 {
   cairo_surface_t *surface, *image_surface;
   guchar *data;
@@ -3741,13 +3740,6 @@ apply_color_matrix_to_pattern (cairo_pattern_t         *pattern,
               graphene_matrix_transform_vec4 (color_matrix, &pixel, &pixel);
             }
 
-          if (multiply_alpha)
-            graphene_vec4_init (&pixel,
-                                graphene_vec4_get_x (&pixel),
-                                graphene_vec4_get_y (&pixel),
-                                graphene_vec4_get_z (&pixel),
-                                alpha * graphene_vec4_get_w (&pixel));
-
           graphene_vec4_add (&pixel, color_offset, &pixel);
 
           alpha = graphene_vec4_get_w (&pixel);
@@ -3793,7 +3785,7 @@ gsk_color_matrix_node_draw (GskRenderNode *node,
 
   pattern = cairo_pop_group (cr);
 
-  apply_color_matrix_to_pattern (pattern, &self->color_matrix, &self->color_offset, FALSE);
+  apply_color_matrix_to_pattern (pattern, &self->color_matrix, &self->color_offset);
 
   cairo_set_source (cr, pattern);
   cairo_paint (cr);
@@ -5654,6 +5646,50 @@ gsk_mask_node_finalize (GskRenderNode *node)
   parent_class->finalize (node);
 }
 
+static void
+apply_luminance_to_pattern (cairo_pattern_t *pattern,
+                            gboolean         invert_luminance)
+{
+  cairo_surface_t *surface, *image_surface;
+  guchar *data;
+  gsize x, y, width, height, stride;
+  int red, green, blue, alpha, luminance;
+  guint32* pixel_data;
+
+  cairo_pattern_get_surface (pattern, &surface);
+  image_surface = cairo_surface_map_to_image (surface, NULL);
+
+  data = cairo_image_surface_get_data (image_surface);
+  width = cairo_image_surface_get_width (image_surface);
+  height = cairo_image_surface_get_height (image_surface);
+  stride = cairo_image_surface_get_stride (image_surface);
+
+  for (y = 0; y < height; y++)
+    {
+      pixel_data = (guint32 *) data;
+      for (x = 0; x < width; x++)
+        {
+          alpha = (pixel_data[x] >> 24) & 0xFF;
+          red   = (pixel_data[x] >> 16) & 0xFF;
+          green = (pixel_data[x] >>  8) & 0xFF;
+          blue  = (pixel_data[x] >>  0) & 0xFF;
+
+          luminance = 2126 * red + 7152 * green + 722 * blue;
+          if (invert_luminance)
+            luminance = 10000 * alpha - luminance;
+          luminance = (luminance + 5000) / 10000;
+
+          pixel_data[x] = luminance * 0x1010101;
+        }
+      data += stride;
+    }
+
+  cairo_surface_mark_dirty (image_surface);
+  cairo_surface_unmap_image (surface, image_surface);
+  /* https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/487 */
+  cairo_surface_mark_dirty (surface);
+}
+
 static void
 gsk_mask_node_draw (GskRenderNode *node,
                     cairo_t       *cr)
@@ -5676,28 +5712,18 @@ gsk_mask_node_draw (GskRenderNode *node,
     case GSK_MASK_MODE_ALPHA:
       break;
     case GSK_MASK_MODE_INVERTED_ALPHA:
-      graphene_matrix_init_from_float (&color_matrix, (float[]){ 1, 0, 0, 0,
-                                                                 0, 1, 0, 0,
-                                                                 0, 0, 1, 0,
-                                                                 0, 0, 0, -1 });
-      graphene_vec4_init (&color_offset, 0, 0, 0, 1);
-      apply_color_matrix_to_pattern (mask_pattern, &color_matrix, &color_offset, FALSE);
+      graphene_matrix_init_from_float (&color_matrix, (float[]){  0,  0,  0,  0,
+                                                                  0,  0,  0,  0,
+                                                                  0,  0,  0,  0,
+                                                                 -1, -1, -1, -1 });
+      graphene_vec4_init (&color_offset, 1, 1, 1, 1);
+      apply_color_matrix_to_pattern (mask_pattern, &color_matrix, &color_offset);
       break;
     case GSK_MASK_MODE_LUMINANCE:
-      graphene_matrix_init_from_float (&color_matrix, (float[]){ 1, 0, 0, 0.2126,
-                                                                 0, 1, 0, 0.7152,
-                                                                 0, 0, 1, 0.0722,
-                                                                 0, 0, 0, 0 });
-      graphene_vec4_init (&color_offset, 0, 0, 0, 0);
-      apply_color_matrix_to_pattern (mask_pattern, &color_matrix, &color_offset, TRUE);
+      apply_luminance_to_pattern (mask_pattern, FALSE);
       break;
     case GSK_MASK_MODE_INVERTED_LUMINANCE:
-      graphene_matrix_init_from_float (&color_matrix, (float[]){ 1, 0, 0, -0.2126,
-                                                                 0, 1, 0, -0.7152,
-                                                                 0, 0, 1, -0.0722,
-                                                                 0, 0, 0,  0 });
-      graphene_vec4_init (&color_offset, 0, 0, 0, 1);
-      apply_color_matrix_to_pattern (mask_pattern, &color_matrix, &color_offset, TRUE);
+      apply_luminance_to_pattern (mask_pattern, TRUE);
       break;
     default:
       g_assert_not_reached ();
diff --git a/testsuite/gsk/compare/mask-modes-with-alpha.node b/testsuite/gsk/compare/mask-modes-with-alpha.node
new file mode 100644 (file)
index 0000000..b40fd82
--- /dev/null
@@ -0,0 +1,43 @@
+mask {
+  source: color {
+    bounds: 0 0 50 50;
+    color: rgb(255,0,0);
+  }
+  mask: color {
+    bounds: 0 0 50 50;
+    color: rgba(204,204,204,0.666667);
+  }
+}
+mask {
+  mode: luminance;
+  source: color {
+    bounds: 50 0 50 50;
+    color: rgb(255,0,0);
+  }
+  mask: color {
+    bounds: 50 0 50 50;
+    color: rgba(204,204,204,0.666667);
+  }
+}
+mask {
+  mode: inverted-alpha;
+  source: color {
+    bounds: 0 50 50 50;
+    color: rgb(255,0,0);
+  }
+  mask: color {
+    bounds: 0 50 50 50;
+    color: rgba(204,204,204,0.666667);
+  }
+}
+mask {
+  mode: inverted-luminance;
+  source: color {
+    bounds: 50 50 50 50;
+    color: rgb(255,0,0);
+  }
+  mask: color {
+    bounds: 50 50 50 50;
+    color: rgba(204,204,204,0.666667);
+  }
+}
diff --git a/testsuite/gsk/compare/mask-modes-with-alpha.png b/testsuite/gsk/compare/mask-modes-with-alpha.png
new file mode 100644 (file)
index 0000000..b376da9
Binary files /dev/null and b/testsuite/gsk/compare/mask-modes-with-alpha.png differ
index 148fbf887b02c46b24dc471d62eaaef7bca0a85c..ee2f59e1b01bd89ddef9ad9865a948b49c2609f8 100644 (file)
Binary files a/testsuite/gsk/compare/mask-modes.png and b/testsuite/gsk/compare/mask-modes.png differ
index 1a57cfa3ad771602d69fa3f6827199ee86aba1b3..9317e3a3e149d21abea755c741ffd5f04647e540 100644 (file)
@@ -66,6 +66,7 @@ compare_render_tests = [
   'issue-3615',
   'mask',
   'mask-modes',
+  'mask-modes-with-alpha',
   'nested-rounded-clips',
   'opacity_clip',
   'opacity-overdraw',