From c6eb7fd4837c34ab83de7c79c141404862b80810 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Mon, 3 Jul 2023 20:45:53 +0200 Subject: [PATCH] gsk: Fix luminance in Cairo and GL renderer 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 | 10 ++- gsk/gskrendernodeimpl.c | 82 ++++++++++++------ .../gsk/compare/mask-modes-with-alpha.node | 43 +++++++++ .../gsk/compare/mask-modes-with-alpha.png | Bin 0 -> 337 bytes testsuite/gsk/compare/mask-modes.png | Bin 255 -> 223 bytes testsuite/gsk/meson.build | 1 + 6 files changed, 106 insertions(+), 30 deletions(-) create mode 100644 testsuite/gsk/compare/mask-modes-with-alpha.node create mode 100644 testsuite/gsk/compare/mask-modes-with-alpha.png diff --git a/gsk/gl/resources/mask.glsl b/gsk/gl/resources/mask.glsl index 602c83dd64..0c2814be9f 100644 --- a/gsk/gl/resources/mask.glsl +++ b/gsk/gl/resources/mask.glsl @@ -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; diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index e3ca2b8b73..54234992f9 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -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 index 0000000000..b40fd82bab --- /dev/null +++ b/testsuite/gsk/compare/mask-modes-with-alpha.node @@ -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 index 0000000000000000000000000000000000000000..b376da993c6352ec682e54665bd51eaee4e58fcd GIT binary patch literal 337 zcmeAS@N?(olHy`uVBq!ia0vp^DImPM`W@x`5f5+JHpDj_;Sf^x=ut4F1LOIbnzH_g sC%0@9Sou2zRoo^>I3}^sgYlC|+K}aB^V{fuz@TFAboFyt=akR{0Bkd9OaK4? literal 0 HcmV?d00001 diff --git a/testsuite/gsk/compare/mask-modes.png b/testsuite/gsk/compare/mask-modes.png index 148fbf887b02c46b24dc471d62eaaef7bca0a85c..ee2f59e1b01bd89ddef9ad9865a948b49c2609f8 100644 GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)%RF5iLn`LHz2nH$Vj#e9u;G|`IczalI yK^CB;P~bOd+p+Q=FCVt(eyo2qsZu2Y7c5~mNM(=a3VIU@(&_2y=d#Wzp$P!5L_=c$ literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)$2?seLn`LHz2nGr*no$@arKw~ z@n!P^PO#l+o4GqMqKIo^y6w@p-$!m-?_B7wl%RqGe$i|HzUv=n=CxTyzr%|EM($~x z*nkTHZ3Tjwuh#o-rrkdF%U;L;7tE4={qWLZ&ge(qrKMjl=aAsU0c#o5FLPEi2cND6 One6H6=d#Wzp$P!ANmaA} diff --git a/testsuite/gsk/meson.build b/testsuite/gsk/meson.build index 1a57cfa3ad..9317e3a3e1 100644 --- a/testsuite/gsk/meson.build +++ b/testsuite/gsk/meson.build @@ -66,6 +66,7 @@ compare_render_tests = [ 'issue-3615', 'mask', 'mask-modes', + 'mask-modes-with-alpha', 'nested-rounded-clips', 'opacity_clip', 'opacity-overdraw', -- 2.30.2