diff --git a/gsk/gpu/gskgpunodeprocessor.c b/gsk/gpu/gskgpunodeprocessor.c index 41b768ff30..6b5b85123b 100644 --- a/gsk/gpu/gskgpunodeprocessor.c +++ b/gsk/gpu/gskgpunodeprocessor.c @@ -2122,6 +2122,46 @@ gsk_gpu_node_processor_add_shadow_node (GskGpuNodeProcessor *self, g_object_unref (image); } +static gboolean +gsk_gpu_node_processor_create_blend_pattern (GskGpuPatternWriter *self, + GskRenderNode *node) +{ + GskRenderNode *bottom_child, *top_child; + + bottom_child = gsk_blend_node_get_bottom_child (node); + top_child = gsk_blend_node_get_top_child (node); + + if (!gsk_gpu_node_processor_create_node_pattern (self, bottom_child)) + return FALSE; + if (!gsk_rect_contains_rect (&bottom_child->bounds, &node->bounds)) + { + gsk_gpu_pattern_writer_append_uint (self, GSK_GPU_PATTERN_CLIP); + gsk_gpu_pattern_writer_append_rect (self, &bottom_child->bounds, &self->offset); + } + + gsk_gpu_pattern_writer_append_uint (self, GSK_GPU_PATTERN_PUSH_COLOR); + + if (!gsk_gpu_pattern_writer_push_stack (self)) + return FALSE; + + if (!gsk_gpu_node_processor_create_node_pattern (self, top_child)) + { + gsk_gpu_pattern_writer_pop_stack (self); + return FALSE; + } + if (!gsk_rect_contains_rect (&top_child->bounds, &node->bounds)) + { + gsk_gpu_pattern_writer_append_uint (self, GSK_GPU_PATTERN_CLIP); + gsk_gpu_pattern_writer_append_rect (self, &top_child->bounds, &self->offset); + } + + gsk_gpu_pattern_writer_append_uint (self, GSK_GPU_PATTERN_BLEND_DEFAULT + gsk_blend_node_get_blend_mode (node)); + + gsk_gpu_pattern_writer_pop_stack (self); + + return TRUE; +} + static gboolean gsk_gpu_node_processor_create_cross_fade_pattern (GskGpuPatternWriter *self, GskRenderNode *node) @@ -2712,9 +2752,9 @@ static const struct }, [GSK_BLEND_NODE] = { 0, - 0, - NULL, - NULL, + GSK_GPU_HANDLE_OPACITY, + gsk_gpu_node_processor_add_node_as_pattern, + gsk_gpu_node_processor_create_blend_pattern, }, [GSK_CROSS_FADE_NODE] = { 0, diff --git a/gsk/gpu/gskgputypesprivate.h b/gsk/gpu/gskgputypesprivate.h index 37aedf60e9..44a94f6041 100644 --- a/gsk/gpu/gskgputypesprivate.h +++ b/gsk/gpu/gskgputypesprivate.h @@ -1,6 +1,7 @@ #pragma once #include +#include "gsk/gskenums.h" #include "gdk/gdkmemoryformatprivate.h" @@ -67,8 +68,40 @@ typedef enum { GSK_GPU_PATTERN_POP_MASK_LUMINANCE, GSK_GPU_PATTERN_POP_MASK_INVERTED_LUMINANCE, GSK_GPU_PATTERN_AFFINE, + GSK_GPU_PATTERN_BLEND_DEFAULT, + GSK_GPU_PATTERN_BLEND_MULTIPLY, + GSK_GPU_PATTERN_BLEND_SCREEN, + GSK_GPU_PATTERN_BLEND_OVERLAY, + GSK_GPU_PATTERN_BLEND_DARKEN, + GSK_GPU_PATTERN_BLEND_LIGHTEN, + GSK_GPU_PATTERN_BLEND_COLOR_DODGE, + GSK_GPU_PATTERN_BLEND_COLOR_BURN, + GSK_GPU_PATTERN_BLEND_HARD_LIGHT, + GSK_GPU_PATTERN_BLEND_SOFT_LIGHT, + GSK_GPU_PATTERN_BLEND_DIFFERENCE, + GSK_GPU_PATTERN_BLEND_EXCLUSION, + GSK_GPU_PATTERN_BLEND_COLOR, + GSK_GPU_PATTERN_BLEND_HUE, + GSK_GPU_PATTERN_BLEND_SATURATION, + GSK_GPU_PATTERN_BLEND_LUMINOSITY, } GskGpuPatternType; +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_MULTIPLY == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_MULTIPLY); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_SCREEN == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_SCREEN); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_OVERLAY == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_OVERLAY); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_DARKEN == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_DARKEN); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_LIGHTEN == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_LIGHTEN); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_COLOR_DODGE == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_COLOR_DODGE); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_COLOR_BURN == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_COLOR_BURN); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_HARD_LIGHT == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_HARD_LIGHT); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_SOFT_LIGHT == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_SOFT_LIGHT); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_DIFFERENCE == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_DIFFERENCE); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_EXCLUSION == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_EXCLUSION); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_COLOR == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_COLOR); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_HUE == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_HUE); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_SATURATION == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_SATURATION); +G_STATIC_ASSERT (GSK_GPU_PATTERN_BLEND_LUMINOSITY == GSK_GPU_PATTERN_BLEND_DEFAULT + GSK_BLEND_MODE_LUMINOSITY); + typedef enum { GSK_GPU_OPTIMIZE_UBER = 1 << 0, GSK_GPU_OPTIMIZE_CLEAR = 1 << 1, diff --git a/gsk/gpu/shaders/blendmode.glsl b/gsk/gpu/shaders/blendmode.glsl new file mode 100644 index 0000000000..69849785be --- /dev/null +++ b/gsk/gpu/shaders/blendmode.glsl @@ -0,0 +1,300 @@ +#ifndef _BLENDMODE_ +#define _BLENDMODE_ + +vec4 +composite (vec4 Cs, vec4 Cb, vec3 B) +{ + float ao = Cs.a + Cb.a * (1.0 - Cs.a); + vec3 Co = (Cs.a*(1.0 - Cb.a)*Cs.rgb + Cs.a*Cb.a*B + (1.0 - Cs.a)*Cb.a*Cb.rgb) / ao; + return vec4(Co, ao); +} + +vec4 +normal (vec4 Cs, vec4 Cb) +{ + return composite (Cs, Cb, Cs.rgb); +} + +vec4 +multiply (vec4 Cs, vec4 Cb) +{ + return composite (Cs, Cb, Cs.rgb * Cb.rgb); +} + +vec4 +difference (vec4 Cs, vec4 Cb) +{ + return composite (Cs, Cb, abs(Cs.rgb - Cb.rgb)); +} + +vec4 +screen (vec4 Cs, vec4 Cb) +{ + return composite (Cs, Cb, Cs.rgb + Cb.rgb - Cs.rgb * Cb.rgb); +} + +float +hard_light (float source, float backdrop) +{ + if (source <= 0.5) + return 2.0 * backdrop * source; + else + return 2.0 * (backdrop + source - backdrop * source) - 1.0; +} + +vec4 +hard_light (vec4 Cs, vec4 Cb) +{ + vec3 B = vec3 (hard_light (Cs.r, Cb.r), + hard_light (Cs.g, Cb.g), + hard_light (Cs.b, Cb.b)); + return composite (Cs, Cb, B); +} + +float +soft_light (float source, float backdrop) +{ + float db; + + if (backdrop <= 0.25) + db = ((16.0 * backdrop - 12.0) * backdrop + 4.0) * backdrop; + else + db = sqrt (backdrop); + + if (source <= 0.5) + return backdrop - (1.0 - 2.0 * source) * backdrop * (1.0 - backdrop); + else + return backdrop + (2.0 * source - 1.0) * (db - backdrop); +} + +vec4 +soft_light (vec4 Cs, vec4 Cb) +{ + vec3 B = vec3 (soft_light (Cs.r, Cb.r), + soft_light (Cs.g, Cb.g), + soft_light (Cs.b, Cb.b)); + return composite (Cs, Cb, B); +} + +vec4 +overlay (vec4 Cs, vec4 Cb) +{ + vec3 B = vec3 (hard_light (Cb.r, Cs.r), + hard_light (Cb.g, Cs.g), + hard_light (Cb.b, Cs.b)); + return composite (Cs, Cb, B); +} + +vec4 +darken (vec4 Cs, vec4 Cb) +{ + vec3 B = min (Cs.rgb, Cb.rgb); + return composite (Cs, Cb, B); +} + +vec4 +lighten (vec4 Cs, vec4 Cb) +{ + vec3 B = max (Cs.rgb, Cb.rgb); + return composite (Cs, Cb, B); +} + +float +color_dodge (float source, float backdrop) +{ + return (source == 1.0) ? source : min (backdrop / (1.0 - source), 1.0); +} + +vec4 +color_dodge (vec4 Cs, vec4 Cb) +{ + vec3 B = vec3 (color_dodge (Cs.r, Cb.r), + color_dodge (Cs.g, Cb.g), + color_dodge (Cs.b, Cb.b)); + return composite (Cs, Cb, B); +} + + +float +color_burn (float source, float backdrop) +{ + return (source == 0.0) ? source : max ((1.0 - ((1.0 - backdrop) / source)), 0.0); +} + +vec4 +color_burn (vec4 Cs, vec4 Cb) +{ + vec3 B = vec3 (color_burn (Cs.r, Cb.r), + color_burn (Cs.g, Cb.g), + color_burn (Cs.b, Cb.b)); + return composite (Cs, Cb, B); +} + +vec4 +exclusion (vec4 Cs, vec4 Cb) +{ + vec3 B = Cb.rgb + Cs.rgb - 2.0 * Cb.rgb * Cs.rgb; + return composite (Cs, Cb, B); +} + +float +lum (vec3 c) +{ + return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; +} + +vec3 +clip_color (vec3 c) +{ + float l = lum (c); + float n = min (c.r, min (c.g, c.b)); + float x = max (c.r, max (c.g, c.b)); + if (n < 0.0) c = l + (((c - l) * l) / (l - n)); + if (x > 1.0) c = l + (((c - l) * (1.0 - l)) / (x - l)); + return c; +} + +vec3 +set_lum (vec3 c, float l) +{ + float d = l - lum (c); + return clip_color (vec3 (c.r + d, c.g + d, c.b + d)); +} + +float +sat (vec3 c) +{ + return max (c.r, max (c.g, c.b)) - min (c.r, min (c.g, c.b)); +} + +vec3 +set_sat (vec3 c, float s) +{ + float cmin = min (c.r, min (c.g, c.b)); + float cmax = max (c.r, max (c.g, c.b)); + vec3 res; + + if (cmax == cmin) + res = vec3 (0, 0, 0); + else + { + if (c.r == cmax) + { + if (c.g == cmin) + { + res.b = ((c.b - cmin) * s) / (cmax - cmin); + res.g = 0.0; + } + else + { + res.g = ((c.g - cmin) * s) / (cmax - cmin); + res.b = 0.0; + } + res.r = s; + } + else if (c.g == cmax) + { + if (c.r == cmin) + { + res.b = ((c.b - cmin) * s) / (cmax - cmin); + res.r = 0.0; + } + else + { + res.r = ((c.r - cmin) * s) / (cmax - cmin); + res.b = 0.0; + } + res.g = s; + } + else + { + if (c.r == cmin) + { + res.g = ((c.g - cmin) * s) / (cmax - cmin); + res.r = 0.0; + } + else + { + res.r = ((c.r - cmin) * s) / (cmax - cmin); + res.g = 0.0; + } + res.b = s; + } + } + return res; +} + +vec4 +color (vec4 Cs, vec4 Cb) +{ + vec3 B = set_lum (Cs.rgb, lum (Cb.rgb)); + return composite (Cs, Cb, B); +} + +vec4 +hue (vec4 Cs, vec4 Cb) +{ + vec3 B = set_lum (set_sat (Cs.rgb, sat (Cb.rgb)), lum (Cb.rgb)); + return composite (Cs, Cb, B); +} + +vec4 +saturation (vec4 Cs, vec4 Cb) +{ + vec3 B = set_lum (set_sat (Cb.rgb, sat (Cs.rgb)), lum (Cb.rgb)); + return composite (Cs, Cb, B); +} + +vec4 +luminosity (vec4 Cs, vec4 Cb) +{ + vec3 B = set_lum (Cb.rgb, lum (Cs.rgb)); + return composite (Cs, Cb, B); +} + +vec4 +blend_mode (vec4 bottom_color, + vec4 top_color, + uint u_mode) +{ + vec4 result; + + if (u_mode == 0u) + result = normal(top_color, bottom_color); + else if (u_mode == 1u) + result = multiply(top_color, bottom_color); + else if (u_mode == 2u) + result = screen(top_color, bottom_color); + else if (u_mode == 3u) + result = overlay(top_color, bottom_color); + else if (u_mode == 4u) + result = darken(top_color, bottom_color); + else if (u_mode == 5u) + result = lighten(top_color, bottom_color); + else if (u_mode == 6u) + result = color_dodge(top_color, bottom_color); + else if (u_mode == 7u) + result = color_burn(top_color, bottom_color); + else if (u_mode == 8u) + result = hard_light(top_color, bottom_color); + else if (u_mode == 9u) + result = soft_light(top_color, bottom_color); + else if (u_mode == 10u) + result = difference(top_color, bottom_color); + else if (u_mode == 11u) + result = exclusion(top_color, bottom_color); + else if (u_mode == 12u) + result = color(top_color, bottom_color); + else if (u_mode == 13u) + result = hue(top_color, bottom_color); + else if (u_mode == 14u) + result = saturation(top_color, bottom_color); + else if (u_mode == 15u) + result = luminosity(top_color, bottom_color); + else + result = vec4 (10.0, 0.0, 0.8, 1.0); + + return result; +} + +#endif diff --git a/gsk/gpu/shaders/enums.glsl b/gsk/gpu/shaders/enums.glsl index 973b9f516b..0dce96c564 100644 --- a/gsk/gpu/shaders/enums.glsl +++ b/gsk/gpu/shaders/enums.glsl @@ -30,6 +30,22 @@ #define GSK_GPU_PATTERN_POP_MASK_LUMINANCE 20u #define GSK_GPU_PATTERN_POP_MASK_INVERTED_LUMINANCE 21u #define GSK_GPU_PATTERN_AFFINE 22u +#define GSK_GPU_PATTERN_BLEND_DEFAULT 23u +#define GSK_GPU_PATTERN_BLEND_MULTIPLY 24u +#define GSK_GPU_PATTERN_BLEND_SCREEN 25u +#define GSK_GPU_PATTERN_BLEND_OVERLAY 26u +#define GSK_GPU_PATTERN_BLEND_DARKEN 27u +#define GSK_GPU_PATTERN_BLEND_LIGHTEN 28u +#define GSK_GPU_PATTERN_BLEND_COLOR_DODGE 29u +#define GSK_GPU_PATTERN_BLEND_COLOR_BURN 30u +#define GSK_GPU_PATTERN_BLEND_HARD_LIGHT 31u +#define GSK_GPU_PATTERN_BLEND_SOFT_LIGHT 32u +#define GSK_GPU_PATTERN_BLEND_DIFFERENCE 33u +#define GSK_GPU_PATTERN_BLEND_EXCLUSION 34u +#define GSK_GPU_PATTERN_BLEND_COLOR 35u +#define GSK_GPU_PATTERN_BLEND_HUE 36u +#define GSK_GPU_PATTERN_BLEND_SATURATION 37u +#define GSK_GPU_PATTERN_BLEND_LUMINOSITY 38u #define TOP 0u #define RIGHT 1u diff --git a/gsk/gpu/shaders/meson.build b/gsk/gpu/shaders/meson.build index 559b8e7d40..0be1cb5981 100644 --- a/gsk/gpu/shaders/meson.build +++ b/gsk/gpu/shaders/meson.build @@ -1,4 +1,5 @@ gsk_private_gpu_include_shaders = files([ + 'blendmode.glsl', 'color.glsl', 'common.glsl', 'common-gl.glsl', diff --git a/gsk/gpu/shaders/pattern.glsl b/gsk/gpu/shaders/pattern.glsl index 126accc4b4..9c8837eeea 100644 --- a/gsk/gpu/shaders/pattern.glsl +++ b/gsk/gpu/shaders/pattern.glsl @@ -2,6 +2,7 @@ #define _PATTERN_ #include "common.glsl" +#include "blendmode.glsl" #include "gradient.glsl" #include "rect.glsl" @@ -222,6 +223,15 @@ mask_inverted_luminance_pattern (inout uint reader, color = source * (color.a - luminance (color.rgb)); } +void +blend_mode_pattern (inout vec4 color, + uint mode) +{ + vec4 bottom = stack_pop (); + + color = blend_mode (bottom, color, mode); +} + vec4 glyphs_pattern (inout uint reader, Position pos) @@ -422,6 +432,24 @@ pattern (uint reader, case GSK_GPU_PATTERN_AFFINE: affine_pattern (reader, pos); break; + case GSK_GPU_PATTERN_BLEND_DEFAULT: + case GSK_GPU_PATTERN_BLEND_MULTIPLY: + case GSK_GPU_PATTERN_BLEND_SCREEN: + case GSK_GPU_PATTERN_BLEND_OVERLAY: + case GSK_GPU_PATTERN_BLEND_DARKEN: + case GSK_GPU_PATTERN_BLEND_LIGHTEN: + case GSK_GPU_PATTERN_BLEND_COLOR_DODGE: + case GSK_GPU_PATTERN_BLEND_COLOR_BURN: + case GSK_GPU_PATTERN_BLEND_HARD_LIGHT: + case GSK_GPU_PATTERN_BLEND_SOFT_LIGHT: + case GSK_GPU_PATTERN_BLEND_DIFFERENCE: + case GSK_GPU_PATTERN_BLEND_EXCLUSION: + case GSK_GPU_PATTERN_BLEND_COLOR: + case GSK_GPU_PATTERN_BLEND_HUE: + case GSK_GPU_PATTERN_BLEND_SATURATION: + case GSK_GPU_PATTERN_BLEND_LUMINOSITY: + blend_mode_pattern (color, type - GSK_GPU_PATTERN_BLEND_DEFAULT); + break; } } }