From d9839fa015c4b0e5477613124d91ad04eed1c082 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 8 Jun 2024 08:59:42 -0400 Subject: [PATCH] gpu: (De)linearize images when needed Make the straightalpha shader handle conversions between srgb and linear srgb as well. This is not a full-fledged color space conversion, but just applying or removing gamma. --- gsk/gpu/gskgpunodeprocessor.c | 43 +++++++++++++++++++----- gsk/gpu/gskgpustraightalphaop.c | 11 +++++- gsk/gpu/gskgpustraightalphaopprivate.h | 3 ++ gsk/gpu/shaders/color.glsl | 37 ++++++++++++++++++++ gsk/gpu/shaders/gskgpustraightalpha.glsl | 18 +++++++--- 5 files changed, 99 insertions(+), 13 deletions(-) diff --git a/gsk/gpu/gskgpunodeprocessor.c b/gsk/gpu/gskgpunodeprocessor.c index d936807767..2622394a77 100644 --- a/gsk/gpu/gskgpunodeprocessor.c +++ b/gsk/gpu/gskgpunodeprocessor.c @@ -710,16 +710,36 @@ gsk_gpu_node_processor_image_op (GskGpuNodeProcessor *self, const graphene_rect_t *tex_rect) { guint32 descriptor; + gboolean straight_alpha = FALSE; + gboolean linearize = FALSE; + gboolean delinearize = FALSE; g_assert (self->pending_globals == 0); descriptor = gsk_gpu_node_processor_add_image (self, image, GSK_GPU_SAMPLER_DEFAULT); - if (gsk_gpu_image_get_flags (image) & GSK_GPU_IMAGE_STRAIGHT_ALPHA) + straight_alpha = (gsk_gpu_image_get_flags (image) & GSK_GPU_IMAGE_STRAIGHT_ALPHA) != 0; + + if (!gdk_color_state_equal (gsk_gpu_image_get_color_state (image), self->color_state)) + { + if (gsk_gpu_image_get_color_state (image) == gdk_color_state_get_srgb () && + self->color_state == gdk_color_state_get_srgb_linear ()) + linearize = TRUE; + else if (gsk_gpu_image_get_color_state (image) == gdk_color_state_get_srgb_linear () && + self->color_state == gdk_color_state_get_srgb ()) + delinearize = TRUE; + else + g_warning ("Unhandled color states"); + } + + if (straight_alpha || linearize || delinearize) { gsk_gpu_straight_alpha_op (self->frame, gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, rect), self->opacity, + straight_alpha, + linearize, + delinearize, self->desc, descriptor, rect, @@ -857,7 +877,8 @@ static GskGpuImage * gsk_gpu_node_processor_ensure_image (GskGpuFrame *frame, GskGpuImage *image, GskGpuImageFlags required_flags, - GskGpuImageFlags disallowed_flags) + GskGpuImageFlags disallowed_flags, + GdkColorState *target_color_state) { GskGpuImageFlags flags, missing_flags; GskGpuImage *copy; @@ -887,10 +908,11 @@ gsk_gpu_node_processor_ensure_image (GskGpuFrame *frame, copy = gsk_gpu_device_create_offscreen_image (gsk_gpu_frame_get_device (frame), required_flags & (GSK_GPU_IMAGE_CAN_MIPMAP | GSK_GPU_IMAGE_MIPMAP) ? TRUE : FALSE, gdk_memory_format_get_depth (gsk_gpu_image_get_format (image)), - gsk_gpu_image_get_color_state (image), + target_color_state, width, height); if (gsk_gpu_frame_should_optimize (frame, GSK_GPU_OPTIMIZE_BLIT) && + gdk_color_state_equal (gsk_gpu_image_get_color_state (image), target_color_state) && (flags & (GSK_GPU_IMAGE_NO_BLIT | GSK_GPU_IMAGE_STRAIGHT_ALPHA | GSK_GPU_IMAGE_FILTERABLE)) == GSK_GPU_IMAGE_FILTERABLE) { gsk_gpu_blit_op (frame, @@ -990,7 +1012,8 @@ gsk_gpu_node_processor_get_node_as_image (GskGpuNodeProcessor *self, ensure = gsk_gpu_node_processor_ensure_image (self->frame, image, required_flags, - disallowed_flags); + disallowed_flags, + self->color_state); /* if we fixed up a cached texture, cache the fixed up version instead */ if (ensure != image && disallowed_flags && @@ -1221,7 +1244,8 @@ gsk_gpu_node_processor_try_node_as_pattern (GskGpuNodeProcessor *self, self->frame, &self->scale, &self->offset, - &clipped); + &clipped, + self->color_state); if (!gsk_gpu_node_processor_create_node_pattern (&writer, node)) { @@ -1972,7 +1996,8 @@ gsk_gpu_node_processor_add_texture_node (GskGpuNodeProcessor *self, image = gsk_gpu_node_processor_ensure_image (self->frame, image, GSK_GPU_IMAGE_MIPMAP, - GSK_GPU_IMAGE_STRAIGHT_ALPHA); + GSK_GPU_IMAGE_STRAIGHT_ALPHA, + self->color_state); descriptor = gsk_gpu_node_processor_add_image (self, image, GSK_GPU_SAMPLER_MIPMAP_DEFAULT); if (self->opacity < 1.0) { @@ -2037,7 +2062,8 @@ gsk_gpu_node_processor_create_texture_pattern (GskGpuPatternWriter *self, image = gsk_gpu_node_processor_ensure_image (self->frame, image, GSK_GPU_IMAGE_MIPMAP, - GSK_GPU_IMAGE_STRAIGHT_ALPHA); + GSK_GPU_IMAGE_STRAIGHT_ALPHA, + self->color_state); sampler = GSK_GPU_SAMPLER_MIPMAP_DEFAULT; } else @@ -2136,7 +2162,8 @@ gsk_gpu_node_processor_add_texture_scale_node (GskGpuNodeProcessor *self, image = gsk_gpu_node_processor_ensure_image (self->frame, image, need_mipmap ? (GSK_GPU_IMAGE_CAN_MIPMAP | GSK_GPU_IMAGE_MIPMAP) : 0, - GSK_GPU_IMAGE_STRAIGHT_ALPHA); + GSK_GPU_IMAGE_STRAIGHT_ALPHA, + self->color_state); switch (scaling_filter) { diff --git a/gsk/gpu/gskgpustraightalphaop.c b/gsk/gpu/gskgpustraightalphaop.c index 1e9b784627..ff2e2f52d7 100644 --- a/gsk/gpu/gskgpustraightalphaop.c +++ b/gsk/gpu/gskgpustraightalphaop.c @@ -10,6 +10,8 @@ #define VARIATION_OPACITY (1 << 0) #define VARIATION_STRAIGHT_ALPHA (1 << 1) +#define VARIATION_LINEARIZE (1 << 2) +#define VARIATION_DELINEARIZE (1 << 3) typedef struct _GskGpuStraightAlphaOp GskGpuStraightAlphaOp; @@ -54,6 +56,9 @@ void gsk_gpu_straight_alpha_op (GskGpuFrame *frame, GskGpuShaderClip clip, float opacity, + gboolean straight_alpha, + gboolean linearize, + gboolean delinearize, GskGpuDescriptors *desc, guint32 descriptor, const graphene_rect_t *rect, @@ -62,10 +67,14 @@ gsk_gpu_straight_alpha_op (GskGpuFrame *frame, { GskGpuStraightalphaInstance *instance; + g_assert (!(linearize && delinearize)); + gsk_gpu_shader_op_alloc (frame, &GSK_GPU_STRAIGHT_ALPHA_OP_CLASS, (opacity < 1.0 ? VARIATION_OPACITY : 0) | - VARIATION_STRAIGHT_ALPHA, + (straight_alpha ? VARIATION_STRAIGHT_ALPHA : 0) | + (linearize ? VARIATION_LINEARIZE : 0) | + (delinearize ? VARIATION_DELINEARIZE : 0), clip, desc, &instance); diff --git a/gsk/gpu/gskgpustraightalphaopprivate.h b/gsk/gpu/gskgpustraightalphaopprivate.h index 127fc44bfa..e5eb380d40 100644 --- a/gsk/gpu/gskgpustraightalphaopprivate.h +++ b/gsk/gpu/gskgpustraightalphaopprivate.h @@ -9,6 +9,9 @@ G_BEGIN_DECLS void gsk_gpu_straight_alpha_op (GskGpuFrame *frame, GskGpuShaderClip clip, float opacity, + gboolean straight_alpha, + gboolean linearize, + gboolean delinearize, GskGpuDescriptors *desc, guint32 descriptor, const graphene_rect_t *rect, diff --git a/gsk/gpu/shaders/color.glsl b/gsk/gpu/shaders/color.glsl index 3f18784593..043e32a22f 100644 --- a/gsk/gpu/shaders/color.glsl +++ b/gsk/gpu/shaders/color.glsl @@ -19,4 +19,41 @@ luminance (vec3 color) return dot (vec3 (0.2126, 0.7152, 0.0722), color); } +float +apply_gamma (float v) +{ + if (v > 0.0031308) + return 1.055 * pow (v, 1.0/2.4) - 0.055; + else + return 12.92 * v; +} + +float +unapply_gamma (float v) +{ + if (v >= 0.04045) + return pow (((v + 0.055)/(1.0 + 0.055)), 2.4); + else + return v / 12.92; +} + +vec4 +srgb_to_linear (vec4 c) +{ + return vec4(unapply_gamma (c.r), + unapply_gamma (c.g), + unapply_gamma (c.b), + c.a); +} + +vec4 +srgb_from_linear (vec4 c) +{ + return vec4(apply_gamma (c.r), + apply_gamma (c.g), + apply_gamma (c.b), + c.a); +} + #endif /* _COLOR_ */ + diff --git a/gsk/gpu/shaders/gskgpustraightalpha.glsl b/gsk/gpu/shaders/gskgpustraightalpha.glsl index 22e4b6f020..0bb88aec83 100644 --- a/gsk/gpu/shaders/gskgpustraightalpha.glsl +++ b/gsk/gpu/shaders/gskgpustraightalpha.glsl @@ -2,8 +2,10 @@ #define VARIATION_OPACITY (1u << 0) #define VARIATION_STRAIGHT_ALPHA (1u << 1) +#define VARIATION_LINEARIZE (1u << 2) +#define VARIATION_DELINEARIZE (1u << 3) -#define HAS_VARIATION(var) ((GSK_VARIATION & var) == var) +#define HAS_VARIATION(var) ((GSK_VARIATION & (var)) == (var)) PASS(0) vec2 _pos; PASS_FLAT(1) Rect _rect; @@ -23,7 +25,7 @@ void run (out vec2 pos) { Rect r = rect_from_gsk (in_rect); - + pos = rect_get_position (r); _pos = pos; @@ -43,15 +45,23 @@ void run (out vec4 color, out vec2 position) { + vec4 c; + float alpha = rect_coverage (_rect, _pos); if (HAS_VARIATION (VARIATION_OPACITY)) alpha *= _opacity; if (HAS_VARIATION (VARIATION_STRAIGHT_ALPHA)) - color = gsk_texture_straight_alpha (_tex_id, _tex_coord) * alpha; + c = gsk_texture_straight_alpha (_tex_id, _tex_coord); else - color = gsk_texture (_tex_id, _tex_coord) * alpha; + c = gsk_texture (_tex_id, _tex_coord); + if (HAS_VARIATION (VARIATION_LINEARIZE)) + c = srgb_to_linear (c); + else if (HAS_VARIATION (VARIATION_DELINEARIZE)) + c = srgb_from_linear (c); + + color = c * alpha; position = _pos; }