gpu: Add support for blend modes

This commit is contained in:
Benjamin Otte
2023-11-25 12:16:18 +01:00
parent 496ecd68f2
commit d8a0cd24d7
6 changed files with 421 additions and 3 deletions

View File

@@ -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,

View File

@@ -1,6 +1,7 @@
#pragma once
#include <gdk/gdk.h>
#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,

View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +1,5 @@
gsk_private_gpu_include_shaders = files([
'blendmode.glsl',
'color.glsl',
'common.glsl',
'common-gl.glsl',

View File

@@ -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;
}
}
}