gpu: Add a color convert shader

This shader converts between two color states, by using the
same functions that we use on the cpu. The conversion to perform
is passed as part of the variation.

As premultiplication is part of color states on the shader, we also
encode the premultiplication in the shader.
And because opacity is a useful optimization, we also allow setting
opacity.

For now, the only possible color states are srgb and srgb-linear.
This commit is contained in:
Matthias Clasen 2024-06-22 23:53:01 +02:00 committed by Benjamin Otte
parent 4fa6f791f4
commit a78796f22c
7 changed files with 332 additions and 0 deletions

View File

@ -81,6 +81,12 @@ gdk_color_state_get_depth (GdkColorState *self)
return self->depth;
}
static inline GdkColorState *
gdk_color_state_get_by_id (GdkColorStateId id)
{
return (GdkColorState *) &gdk_default_color_states[id];
}
#define gdk_color_state_ref(self) _gdk_color_state_ref (self)
static inline GdkColorState *
_gdk_color_state_ref (GdkColorState *self)

146
gsk/gpu/gskgpuconvertop.c Normal file
View File

@ -0,0 +1,146 @@
#include "config.h"
#include "gskgpuconvertopprivate.h"
#include "gskgpuframeprivate.h"
#include "gskgpuprintprivate.h"
#include "gskrectprivate.h"
#include "gdk/gdkcolorstateprivate.h"
#include "gpu/shaders/gskgpuconvertinstance.h"
typedef struct _GskGpuConvertOp GskGpuConvertOp;
struct _GskGpuConvertOp
{
GskGpuShaderOp op;
};
#define VARIATION_OPACITY (1u << 0)
#define VARIATION_ALPHA_ALL_CHANNELS (1u << 1)
#define VARIATION_SOURCE_UNPREMULTIPLY (1u << 2)
#define VARIATION_TARGET_PREMULTIPLY (1u << 3)
#define VARIATION_SOURCE_SHIFT 8u
#define VARIATION_TARGET_SHIFT 16u
#define VARIATION_COLOR_STATE_MASK 0xFFu
static guint
gsk_gpu_convert_encode_variation (GdkColorState *source,
gboolean source_premultiplied,
GdkColorState *target,
gboolean target_premultiplied,
gboolean opacity)
{
guint conversion;
if (source == target)
{
if (source_premultiplied == target_premultiplied)
{
/* no changes should be caught before running a shader */
g_assert (opacity);
if (source_premultiplied)
conversion = VARIATION_ALPHA_ALL_CHANNELS;
else
conversion = 0;
}
else
{
if (source_premultiplied)
conversion = VARIATION_SOURCE_UNPREMULTIPLY;
else
conversion = VARIATION_TARGET_PREMULTIPLY;
}
}
else
{
conversion = GDK_DEFAULT_COLOR_STATE_ID (source) << VARIATION_SOURCE_SHIFT;
conversion |= GDK_DEFAULT_COLOR_STATE_ID (target) << VARIATION_TARGET_SHIFT;
if (source_premultiplied)
conversion |= VARIATION_SOURCE_UNPREMULTIPLY;
if (target_premultiplied)
conversion |= VARIATION_TARGET_PREMULTIPLY;
}
if (opacity)
conversion |= VARIATION_OPACITY;
return conversion;
}
static void
gsk_gpu_convert_op_print_instance (GskGpuShaderOp *shader,
gpointer instance_,
GString *string)
{
GskGpuConvertInstance *instance = (GskGpuConvertInstance *) instance_;
GdkColorState *source, *target;
source = gdk_color_state_get_by_id ((shader->variation >> VARIATION_SOURCE_SHIFT) & VARIATION_COLOR_STATE_MASK);
target = gdk_color_state_get_by_id ((shader->variation >> VARIATION_TARGET_SHIFT) & VARIATION_COLOR_STATE_MASK);
gsk_gpu_print_rect (string, instance->rect);
gsk_gpu_print_image_descriptor (string, shader->desc, instance->tex_id);
g_string_append_printf (string, "%s%s -> %s%s ",
gdk_color_state_get_name (source),
(shader->variation & VARIATION_SOURCE_UNPREMULTIPLY) ? "(p)" : "",
gdk_color_state_get_name (target),
(shader->variation & VARIATION_TARGET_PREMULTIPLY) ? "(p)" : "");
}
static const GskGpuShaderOpClass GSK_GPU_CONVERT_OP_CLASS = {
{
GSK_GPU_OP_SIZE (GskGpuConvertOp),
GSK_GPU_STAGE_SHADER,
gsk_gpu_shader_op_finish,
gsk_gpu_shader_op_print,
#ifdef GDK_RENDERING_VULKAN
gsk_gpu_shader_op_vk_command,
#endif
gsk_gpu_shader_op_gl_command
},
"gskgpuconvert",
sizeof (GskGpuConvertInstance),
#ifdef GDK_RENDERING_VULKAN
&gsk_gpu_convert_info,
#endif
gsk_gpu_convert_op_print_instance,
gsk_gpu_convert_setup_attrib_locations,
gsk_gpu_convert_setup_vao
};
void
gsk_gpu_convert_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
GdkColorState *source,
gboolean source_premultiplied,
GdkColorState *target,
gboolean target_premultiplied,
float opacity,
GskGpuDescriptors *desc,
guint32 descriptor,
const graphene_rect_t *rect,
const graphene_point_t *offset,
const graphene_rect_t *tex_rect)
{
GskGpuConvertInstance *instance;
guint variation;
variation = gsk_gpu_convert_encode_variation (source,
source_premultiplied,
target,
target_premultiplied,
opacity < 1.0);
gsk_gpu_shader_op_alloc (frame,
&GSK_GPU_CONVERT_OP_CLASS,
variation,
clip,
desc,
&instance);
gsk_gpu_rect_to_float (rect, offset, instance->rect);
gsk_gpu_rect_to_float (tex_rect, offset, instance->tex_rect);
instance->tex_id = descriptor;
instance->opacity = opacity;
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "gskgpushaderopprivate.h"
#include <graphene.h>
G_BEGIN_DECLS
void gsk_gpu_convert_op (GskGpuFrame *frame,
GskGpuShaderClip clip,
GdkColorState *from,
gboolean from_premultiplied,
GdkColorState *to,
gboolean to_premultiplied,
float opacity,
GskGpuDescriptors *desc,
guint32 descriptor,
const graphene_rect_t *rect,
const graphene_point_t *offset,
const graphene_rect_t *tex_rect);
G_END_DECLS

View File

@ -52,6 +52,9 @@
#define GSK_MASK_MODE_LUMINANCE 2u
#define GSK_MASK_MODE_INVERTED_LUMINANCE 3u
#define GDK_COLOR_STATE_ID_SRGB 0u
#define GDK_COLOR_STATE_ID_SRGB_LINEAR 1u
#define TOP 0u
#define RIGHT 1u
#define BOTTOM 2u

View File

@ -0,0 +1,151 @@
#include "common.glsl"
#define VARIATION_OPACITY (1u << 0)
#define VARIATION_ALPHA_ALL_CHANNELS (1u << 1)
#define VARIATION_SOURCE_UNPREMULTIPLY (1u << 2)
#define VARIATION_TARGET_PREMULTIPLY (1u << 3)
#define VARIATION_SOURCE_SHIFT 8u
#define VARIATION_TARGET_SHIFT 16u
#define VARIATION_COLOR_STATE_MASK 0xFFu
#define HAS_VARIATION(var) ((GSK_VARIATION & var) == var)
#define SOURCE_COLOR_STATE ((GSK_VARIATION >> VARIATION_SOURCE_SHIFT) & VARIATION_COLOR_STATE_MASK)
#define TARGET_COLOR_STATE ((GSK_VARIATION >> VARIATION_TARGET_SHIFT) & VARIATION_COLOR_STATE_MASK)
float
srgb_eotf (float v)
{
if (v >= 0.04045)
return pow (((v + 0.055) / (1.0 + 0.055)), 2.4);
else
return v / 12.92;
}
float
srgb_oetf (float v)
{
if (v > 0.0031308)
return 1.055 * pow (v, 1.0 / 2.4) - 0.055;
else
return 12.92 * v;
}
vec4
srgb_to_linear_srgb (vec4 color)
{
return vec4 (srgb_eotf (color.r),
srgb_eotf (color.g),
srgb_eotf (color.b),
color.a);
}
vec4
linear_srgb_to_srgb (vec4 color)
{
return vec4 (srgb_oetf (color.r),
srgb_oetf (color.g),
srgb_oetf (color.b),
color.a);
}
#define PAIR(_from_cs, _to_cs) ((_from_cs) << 16 | (_to_cs))
bool
do_conversion (vec4 color,
uint from_cs,
uint to_cs,
out vec4 result)
{
switch (PAIR (from_cs, to_cs))
{
case PAIR (GDK_COLOR_STATE_ID_SRGB, GDK_COLOR_STATE_ID_SRGB_LINEAR):
result = srgb_to_linear_srgb (color);
break;
case PAIR (GDK_COLOR_STATE_ID_SRGB_LINEAR, GDK_COLOR_STATE_ID_SRGB):
result = linear_srgb_to_srgb (color);
break;
default:
return false;
}
return true;
}
vec4
color_convert (vec4 color)
{
vec4 result;
if (SOURCE_COLOR_STATE == TARGET_COLOR_STATE)
return color;
if (!do_conversion (color, SOURCE_COLOR_STATE, TARGET_COLOR_STATE, result))
result = vec4 (1.0, 0.0, 0.8, 1.0);
return result;
}
PASS(0) vec2 _pos;
PASS_FLAT(1) Rect _rect;
PASS(2) vec2 _tex_coord;
PASS_FLAT(3) uint _tex_id;
PASS_FLAT(4) float _opacity;
#ifdef GSK_VERTEX_SHADER
IN(0) vec4 in_rect;
IN(1) vec4 in_tex_rect;
IN(2) uint in_tex_id;
IN(3) float in_opacity;
void
run (out vec2 pos)
{
Rect r = rect_from_gsk (in_rect);
pos = rect_get_position (r);
_pos = pos;
_rect = r;
_tex_coord = rect_get_coord (rect_from_gsk (in_tex_rect), pos);
_tex_id = in_tex_id;
_opacity = in_opacity;
}
#endif
#ifdef GSK_FRAGMENT_SHADER
void
run (out vec4 color,
out vec2 position)
{
vec4 pixel = gsk_texture (_tex_id, _tex_coord);
if (HAS_VARIATION (VARIATION_SOURCE_UNPREMULTIPLY))
pixel = color_unpremultiply (pixel);
pixel = color_convert (pixel);
float alpha = rect_coverage (_rect, _pos);
if (HAS_VARIATION (VARIATION_OPACITY))
alpha *= _opacity;
if (HAS_VARIATION (VARIATION_ALPHA_ALL_CHANNELS))
pixel *= alpha;
else
pixel.a *= alpha;
if (HAS_VARIATION (VARIATION_TARGET_PREMULTIPLY))
color = color_premultiply (pixel);
else
color = pixel;
position = _pos;
}
#endif

View File

@ -19,6 +19,7 @@ gsk_private_gpu_shaders = files([
'gskgpucolorize.glsl',
'gskgpucolormatrix.glsl',
'gskgpuconicgradient.glsl',
'gskgpuconvert.glsl',
'gskgpucrossfade.glsl',
'gskgpulineargradient.glsl',
'gskgpumask.glsl',

View File

@ -88,6 +88,7 @@ gsk_private_sources = files([
'gpu/gskgpucolormatrixop.c',
'gpu/gskgpucolorop.c',
'gpu/gskgpuconicgradientop.c',
'gpu/gskgpuconvertop.c',
'gpu/gskgpucrossfadeop.c',
'gpu/gskgpudescriptors.c',
'gpu/gskgpudownloadop.c',