Port the cell fragment shader

This commit is contained in:
Kovid Goyal
2026-07-02 14:47:36 +05:30
parent f322d6d9c4
commit 1252974765

View File

@@ -7,6 +7,8 @@
import utils;
import hsluv;
import alpha_blend;
import linear2srgb;
#define NUM_COLORS 256
@@ -470,4 +472,98 @@ VertexOutput vertex_main(
return vo;
}
uniform Sampler2DArray sprites;
// Scaling factor for the extra text-alpha adjustment for luminance-difference.
static const float text_gamma_scaling = 0.5;
float clamp_to_unit_float(float x) {
// Clamp value to suitable output range
return clamp(x, 0.0f, 1.0f);
}
float4 foreground_contrast_new(float4 over, float3 under, float text_contrast, float text_gamma_adjustment) {
float under_luminance = dot(under, Y);
float over_lumininace = dot(over.rgb, Y);
// Apply additional gamma-adjustment scaled by the luminance difference, the darker the foreground the more adjustment we apply.
// A multiplicative contrast is also available to increase saturation.
over.a = clamp_to_unit_float(lerp(over.a, pow(over.a, text_gamma_adjustment), (1 - over_lumininace + under_luminance) * text_gamma_scaling) * text_contrast);
return over;
}
float4 foreground_contrast_old(float4 over, float3 under) {
// Simulation of gamma-incorrect blending
float under_luminance = dot(under, Y);
float over_lumininace = dot(over.rgb, Y);
// This is the original gamma-incorrect rendering, it is the solution of the following equation:
//
// linear2srgb(over * overA2 + under * (1 - overA2)) = linear2srgb(over) * over.a + linear2srgb(under) * (1 - over.a)
// ^ gamma correct blending with new alpha ^ gamma incorrect blending with old alpha
over.a = clamp_to_unit_float((srgb2linear(linear2srgb(over_lumininace) * over.a + linear2srgb(under_luminance) * (1.0f - over.a)) - under_luminance) / (over_lumininace - under_luminance));
return over;
}
float4 foreground_contrast(float4 over, float3 under, float text_contrast, float text_gamma_adjustment) {
if (TEXT_NEW_GAMMA) return foreground_contrast_new(over, under, text_contrast, text_gamma_adjustment);
return foreground_contrast_old(over, under);
}
float4 load_text_foreground_color(float3 sprite_pos, float colored_sprite, float3 cell_foreground) {
// For colored sprites use the color from the sprite rather than the text foreground
// Return non-premultiplied foreground color
float4 text_fg = sprites.Sample(sprite_pos);
return float4(lerp(cell_foreground, text_fg.xyz, colored_sprite), text_fg.w);
}
float4 calculate_premul_foreground_from_sprites(
float3 sprite_pos, float3 underline_pos, float3 cursor_pos, float3 strike_pos, uint underline_exclusion_pos,
float4 text_fg, float3 decoration_fg, float4 cursor_color_premult,
float effective_text_alpha,
) {
// Return premul foreground color from decorations (cursor, underline, strikethrough)
int width, height, layer;
sprites.GetDimensions(width, height, layer);
float3 sz = float3(float(width), float(height), float(layer));
float underline_alpha = sprites.Sample(underline_pos).w;
int3 fetch_coord = int3(int(sprite_pos.x * sz.x), int(underline_exclusion_pos), int(sprite_pos.z));
float underline_exclusion = sprites[fetch_coord].w;
underline_alpha *= 1.0 - underline_exclusion;
float strike_alpha = sprites.Sample(strike_pos).w;
float cursor_alpha = sprites.Sample(cursor_pos).w;
float combined_alpha = min(text_fg.w + strike_alpha, 1.0);
float4 ans = alpha_blend(
float4(text_fg.rgb, combined_alpha * effective_text_alpha),
float4(decoration_fg, underline_alpha * effective_text_alpha)
);
return lerp(ans, cursor_color_premult, cursor_alpha * cursor_color_premult.w);
}
float4 adjust_foreground_contrast_with_background(float4 text_fg, float3 bg, float text_contrast, float text_gamma_adjustment) {
return foreground_contrast(text_fg, bg, text_contrast, text_gamma_adjustment);
}
[shader("fragment")]
float4 fragment_main(
VertexOutput vo,
uniform float text_contrast,
uniform float text_gamma_adjustment,
) : SV_Target {
float4 ans_premul = 0;
if (!ONLY_FOREGROUND) ans_premul = vo.effective_background_premul;
if (!ONLY_BACKGROUND) {
// blend in the foreground color
float4 text_fg = load_text_foreground_color(vo.sprite_pos, vo.colored_sprite, vo.cell_foreground);
text_fg = adjust_foreground_contrast_with_background(text_fg, vo.background, text_contrast, text_gamma_adjustment);
float4 text_fg_premul = calculate_premul_foreground_from_sprites(
vo.sprite_pos, vo.underline_pos, vo.cursor_pos, vo.strike_pos, vo.underline_exclusion_pos,
text_fg, vo.decoration_fg, vo.cursor_color_premult, vo.effective_text_alpha);
if (ONLY_FOREGROUND) ans_premul = text_fg_premul;
else ans_premul = alpha_blend_premul(text_fg_premul, ans_premul);
}
return ans_premul;
}