renderer/metal: minimum contrast experiment

This uses WCAG2 algorithms with a minimum ratio hardcoded of 3:1. This
is not shippable in its current state because we want the ratio to be
configurable and I'm not happy with the way data is being sent to the
shader.
This commit is contained in:
Mitchell Hashimoto
2023-12-01 20:51:16 -08:00
parent 0d82b120da
commit 6c859cca82
3 changed files with 71 additions and 2 deletions

View File

@ -1667,7 +1667,7 @@ pub fn updateCell(
const alpha: u8 = if (cell.attrs.faint) 175 else 255; const alpha: u8 = if (cell.attrs.faint) 175 else 255;
// If the cell has a background, we always draw it. // If the cell has a background, we always draw it.
if (colors.bg) |rgb| { const bg: [4]u8 = if (colors.bg) |rgb| bg: {
// Determine our background alpha. If we have transparency configured // Determine our background alpha. If we have transparency configured
// then this is dynamic depending on some situations. This is all // then this is dynamic depending on some situations. This is all
// in an attempt to make transparency look the best for various // in an attempt to make transparency look the best for various
@ -1701,8 +1701,11 @@ pub fn updateCell(
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) }, .grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
.cell_width = cell.widthLegacy(), .cell_width = cell.widthLegacy(),
.color = .{ rgb.r, rgb.g, rgb.b, bg_alpha }, .color = .{ rgb.r, rgb.g, rgb.b, bg_alpha },
.bg_color = .{ 0, 0, 0, 0 },
}); });
}
break :bg .{ rgb.r, rgb.g, rgb.b, bg_alpha };
} else .{ 0, 0, 0, 0 };
// If the cell has a character, draw it // If the cell has a character, draw it
if (cell.char > 0) { if (cell.char > 0) {
@ -1729,6 +1732,7 @@ pub fn updateCell(
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) }, .grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
.cell_width = cell.widthLegacy(), .cell_width = cell.widthLegacy(),
.color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha }, .color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha },
.bg_color = bg,
.glyph_pos = .{ glyph.atlas_x, glyph.atlas_y }, .glyph_pos = .{ glyph.atlas_x, glyph.atlas_y },
.glyph_size = .{ glyph.width, glyph.height }, .glyph_size = .{ glyph.width, glyph.height },
.glyph_offset = .{ glyph.offset_x, glyph.offset_y }, .glyph_offset = .{ glyph.offset_x, glyph.offset_y },
@ -1759,6 +1763,7 @@ pub fn updateCell(
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) }, .grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
.cell_width = cell.widthLegacy(), .cell_width = cell.widthLegacy(),
.color = .{ color.r, color.g, color.b, alpha }, .color = .{ color.r, color.g, color.b, alpha },
.bg_color = bg,
.glyph_pos = .{ glyph.atlas_x, glyph.atlas_y }, .glyph_pos = .{ glyph.atlas_x, glyph.atlas_y },
.glyph_size = .{ glyph.width, glyph.height }, .glyph_size = .{ glyph.width, glyph.height },
.glyph_offset = .{ glyph.offset_x, glyph.offset_y }, .glyph_offset = .{ glyph.offset_x, glyph.offset_y },
@ -1771,6 +1776,7 @@ pub fn updateCell(
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) }, .grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
.cell_width = cell.widthLegacy(), .cell_width = cell.widthLegacy(),
.color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha }, .color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha },
.bg_color = bg,
}); });
} }
@ -1834,6 +1840,7 @@ fn addCursor(
}, },
.cell_width = if (wide) 2 else 1, .cell_width = if (wide) 2 else 1,
.color = .{ color.r, color.g, color.b, alpha }, .color = .{ color.r, color.g, color.b, alpha },
.bg_color = .{ 0, 0, 0, 0 },
.glyph_pos = .{ glyph.atlas_x, glyph.atlas_y }, .glyph_pos = .{ glyph.atlas_x, glyph.atlas_y },
.glyph_size = .{ glyph.width, glyph.height }, .glyph_size = .{ glyph.width, glyph.height },
.glyph_offset = .{ glyph.offset_x, glyph.offset_y }, .glyph_offset = .{ glyph.offset_x, glyph.offset_y },
@ -1886,6 +1893,7 @@ fn addPreeditCell(
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) }, .grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
.cell_width = if (cp.wide) 2 else 1, .cell_width = if (cp.wide) 2 else 1,
.color = .{ bg.r, bg.g, bg.b, 255 }, .color = .{ bg.r, bg.g, bg.b, 255 },
.bg_color = .{ bg.r, bg.g, bg.b, 255 },
}); });
// Add our text // Add our text
@ -1894,6 +1902,7 @@ fn addPreeditCell(
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) }, .grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
.cell_width = if (cp.wide) 2 else 1, .cell_width = if (cp.wide) 2 else 1,
.color = .{ fg.r, fg.g, fg.b, 255 }, .color = .{ fg.r, fg.g, fg.b, 255 },
.bg_color = .{ bg.r, bg.g, bg.b, 255 },
.glyph_pos = .{ glyph.atlas_x, glyph.atlas_y }, .glyph_pos = .{ glyph.atlas_x, glyph.atlas_y },
.glyph_size = .{ glyph.width, glyph.height }, .glyph_size = .{ glyph.width, glyph.height },
.glyph_offset = .{ glyph.offset_x, glyph.offset_y }, .glyph_offset = .{ glyph.offset_x, glyph.offset_y },

View File

@ -95,6 +95,7 @@ pub const Cell = extern struct {
glyph_size: [2]u32 = .{ 0, 0 }, glyph_size: [2]u32 = .{ 0, 0 },
glyph_offset: [2]i32 = .{ 0, 0 }, glyph_offset: [2]i32 = .{ 0, 0 },
color: [4]u8, color: [4]u8,
bg_color: [4]u8,
cell_width: u8, cell_width: u8,
pub const Mode = enum(u8) { pub const Mode = enum(u8) {
@ -401,6 +402,17 @@ fn initCellPipeline(device: objc.Object, library: objc.Object) !objc.Object {
attr.setProperty("offset", @as(c_ulong, @offsetOf(Cell, "color"))); attr.setProperty("offset", @as(c_ulong, @offsetOf(Cell, "color")));
attr.setProperty("bufferIndex", @as(c_ulong, 0)); attr.setProperty("bufferIndex", @as(c_ulong, 0));
} }
{
const attr = attrs.msgSend(
objc.Object,
objc.sel("objectAtIndexedSubscript:"),
.{@as(c_ulong, 7)},
);
attr.setProperty("format", @intFromEnum(mtl.MTLVertexFormat.uchar4));
attr.setProperty("offset", @as(c_ulong, @offsetOf(Cell, "bg_color")));
attr.setProperty("bufferIndex", @as(c_ulong, 0));
}
{ {
const attr = attrs.msgSend( const attr = attrs.msgSend(
objc.Object, objc.Object,

View File

@ -30,6 +30,7 @@ struct VertexIn {
uchar4 color [[ attribute(5) ]]; uchar4 color [[ attribute(5) ]];
// The fields below are present only when rendering text. // The fields below are present only when rendering text.
uchar4 bg_color [[ attribute(7) ]];
// The position of the glyph in the texture (x,y) // The position of the glyph in the texture (x,y)
uint2 glyph_pos [[ attribute(2) ]]; uint2 glyph_pos [[ attribute(2) ]];
@ -49,6 +50,51 @@ struct VertexOut {
float2 tex_coord; float2 tex_coord;
}; };
//-------------------------------------------------------------------
// Color Functions
//-------------------------------------------------------------------
#pragma mark - Colors
// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
float luminance_component(float c) {
if (c <= 0.03928f) {
return c / 12.92f;
} else {
return pow((c + 0.055f) / 1.055f, 2.4f);
}
}
float relative_luminance(float3 color) {
color.r = luminance_component(color.r);
color.g = luminance_component(color.g);
color.b = luminance_component(color.b);
float3 weights = float3(0.2126f, 0.7152f, 0.0722f);
return dot(color, weights);
}
// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
float contrast_ratio(float3 color1, float3 color2) {
float l1 = relative_luminance(color1);
float l2 = relative_luminance(color2);
return (max(l1, l2) + 0.05f) / (min(l1, l2) + 0.05f);
}
float4 contrasted_color(float4 fg, float4 bg) {
float3 fg_premult = fg.rgb * fg.a;
float3 bg_premult = bg.rgb * bg.a;
float ratio = contrast_ratio(fg_premult, bg_premult);
if (ratio <= 3.0f) {
float ratio = contrast_ratio(float3(1.0f), bg_premult);
if (ratio > 3.0f) {
return float4(1.0f);
} else {
return float4(0.0f, 0.0f, 0.0f, 1.0f);
}
}
return fg;
}
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Terminal Grid Cell Shader // Terminal Grid Cell Shader
//------------------------------------------------------------------- //-------------------------------------------------------------------
@ -112,6 +158,8 @@ vertex VertexOut uber_vertex(
// Calculate the texture coordinate in pixels. This is NOT normalized // Calculate the texture coordinate in pixels. This is NOT normalized
// (between 0.0 and 1.0) and must be done in the fragment shader. // (between 0.0 and 1.0) and must be done in the fragment shader.
out.tex_coord = float2(input.glyph_pos) + float2(input.glyph_size) * position; out.tex_coord = float2(input.glyph_pos) + float2(input.glyph_size) * position;
out.color = contrasted_color(out.color, float4(input.bg_color) / 255.0f);
break; break;
} }