From a37bf14a0cc6acdef97073e6ca0bb79885cf6703 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 1 Jul 2026 08:15:17 +0530 Subject: [PATCH] Port the screenshot shader --- kitty/shaders/screenshot.slang | 68 ++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 kitty/shaders/screenshot.slang diff --git a/kitty/shaders/screenshot.slang b/kitty/shaders/screenshot.slang new file mode 100644 index 000000000..34c9985da --- /dev/null +++ b/kitty/shaders/screenshot.slang @@ -0,0 +1,68 @@ +#language slang 2026 +// Copyright (C) 2026 Kovid Goyal +// Distributed under terms of the GPLv3 license. + +import blit_common; +import linear2srgb; + +struct VSOutput { + float2 texcoord : TEXCOORD; + float4 position : SV_Position; +}; + + +[shader("vertex")] +VSOutput vertex_main( + uint vertex_id : SV_VertexID, + uniform float4 src_rect, + uniform float4 dest_rect, +) { + BlitOutput ans = get_coords_for_blit(vertex_id, src_rect, dest_rect); + return {ans.texcoord, float4(ans.position[0], ans.position[1], 0.0, 1.0)}; +} + +uniform Sampler2D image; + +float3 safe_unpremult_to_linear(float4 s) { + // Avoid division by zero by replacing 0.0 alpha with 1.0. + // If alpha is 0.0, the division is safe, and lerp masks the result to 0.0 anyway. + float safe_alpha = lerp(1.0f, s.a, step(0.00001f, s.a)); + float3 unpremult = s.rgb / safe_alpha; + // Select between the computed linear color and float3(0.0) based on alpha presence + return lerp(float3(0.0f), srgb2linear(unpremult), step(0.00001f, s.a)); +} + +[shader("fragment")] +float4 fragment_main(float2 tc : TEXCOORD, uniform float2 src_size) : SV_Target { + // Calculate the texel size + float2 texel_size = 1.0 / src_size; + + // Sample a 2x2 grid for better quality downscaling + float4 s00 = image.Sample(tc + float2(-0.25, -0.25) * texel_size); + float4 s10 = image.Sample(tc + float2( 0.25, -0.25) * texel_size); + float4 s01 = image.Sample(tc + float2(-0.25, 0.25) * texel_size); + float4 s11 = image.Sample(tc + float2( 0.25, 0.25) * texel_size); + + // Unpremultiply and convert to linear for each sample + float3 linear00 = safe_unpremult_to_linear(s00); + float3 linear10 = safe_unpremult_to_linear(s10); + float3 linear01 = safe_unpremult_to_linear(s01); + float3 linear11 = safe_unpremult_to_linear(s11); + + // Average the alpha values + float avg_alpha = (s00.a + s10.a + s01.a + s11.a) * 0.25; + + // For proper downsampling with transparency, weight colors by their alpha + float3 weighted_sum = linear00 * s00.a + linear10 * s10.a + linear01 * s01.a + linear11 * s11.a; + float total_weight = s00.a + s10.a + s01.a + s11.a; + + // Calculate the weighted average color in linear space + float safe_total_weight = lerp(1.0f, total_weight, step(0.00001f, total_weight)); + float3 avg_linear = lerp(float3(0.0f), weighted_sum / safe_total_weight, step(0.00001f, total_weight)); + + // Convert back to sRGB + float3 srgb_color = linear2srgb(avg_linear); + + // Output unpremultiplied sRGB color + return float4(srgb_color, avg_alpha); +}