mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 08:16:13 +03:00
Merge pull request #1711 from mitchellh/strikesprite
Strikethrough should be sprite-rendered
This commit is contained in:
@ -129,6 +129,7 @@ fn reloadMetrics(self: *SharedGrid, thicken: bool) !void {
|
|||||||
.thickness = self.metrics.underline_thickness *
|
.thickness = self.metrics.underline_thickness *
|
||||||
@as(u32, if (thicken) 2 else 1),
|
@as(u32, if (thicken) 2 else 1),
|
||||||
.underline_position = self.metrics.underline_position,
|
.underline_position = self.metrics.underline_position,
|
||||||
|
.strikethrough_position = self.metrics.strikethrough_position,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ pub const Sprite = enum(u32) {
|
|||||||
underline_dashed,
|
underline_dashed,
|
||||||
underline_curly,
|
underline_curly,
|
||||||
|
|
||||||
|
strikethrough,
|
||||||
|
|
||||||
cursor_rect,
|
cursor_rect,
|
||||||
cursor_hollow_rect,
|
cursor_hollow_rect,
|
||||||
cursor_bar,
|
cursor_bar,
|
||||||
|
@ -30,11 +30,17 @@ height: u32,
|
|||||||
|
|
||||||
/// Base thickness value for lines of sprites. This is in pixels. If you
|
/// Base thickness value for lines of sprites. This is in pixels. If you
|
||||||
/// want to do any DPI scaling, it is expected to be done earlier.
|
/// want to do any DPI scaling, it is expected to be done earlier.
|
||||||
thickness: u32,
|
thickness: u32 = 1,
|
||||||
|
|
||||||
/// The position of the underline.
|
/// The position of the underline.
|
||||||
underline_position: u32 = 0,
|
underline_position: u32 = 0,
|
||||||
|
|
||||||
|
/// The position of the strikethrough.
|
||||||
|
// NOTE(mitchellh): We don't use a dedicated strikethrough thickness
|
||||||
|
// setting yet but fonts can in theory set this. If this becomes an
|
||||||
|
// issue in practice we can add it here.
|
||||||
|
strikethrough_position: u32 = 0,
|
||||||
|
|
||||||
/// Returns true if the codepoint exists in our sprite font.
|
/// Returns true if the codepoint exists in our sprite font.
|
||||||
pub fn hasCodepoint(self: Face, cp: u32, p: ?font.Presentation) bool {
|
pub fn hasCodepoint(self: Face, cp: u32, p: ?font.Presentation) bool {
|
||||||
// We ignore presentation. No matter what presentation is requested
|
// We ignore presentation. No matter what presentation is requested
|
||||||
@ -113,6 +119,16 @@ pub fn renderGlyph(
|
|||||||
self.thickness,
|
self.thickness,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
.strikethrough => try underline.renderGlyph(
|
||||||
|
alloc,
|
||||||
|
atlas,
|
||||||
|
@enumFromInt(cp),
|
||||||
|
width,
|
||||||
|
self.height,
|
||||||
|
self.strikethrough_position,
|
||||||
|
self.thickness,
|
||||||
|
),
|
||||||
|
|
||||||
.powerline => powerline: {
|
.powerline => powerline: {
|
||||||
const f: Powerline = .{
|
const f: Powerline = .{
|
||||||
.width = width,
|
.width = width,
|
||||||
@ -129,6 +145,7 @@ pub fn renderGlyph(
|
|||||||
const Kind = enum {
|
const Kind = enum {
|
||||||
box,
|
box,
|
||||||
underline,
|
underline,
|
||||||
|
strikethrough,
|
||||||
powerline,
|
powerline,
|
||||||
|
|
||||||
pub fn init(cp: u32) ?Kind {
|
pub fn init(cp: u32) ?Kind {
|
||||||
@ -141,6 +158,9 @@ const Kind = enum {
|
|||||||
.underline_curly,
|
.underline_curly,
|
||||||
=> .underline,
|
=> .underline,
|
||||||
|
|
||||||
|
.strikethrough,
|
||||||
|
=> .strikethrough,
|
||||||
|
|
||||||
.cursor_rect,
|
.cursor_rect,
|
||||||
.cursor_hollow_rect,
|
.cursor_hollow_rect,
|
||||||
.cursor_bar,
|
.cursor_bar,
|
||||||
@ -189,3 +209,7 @@ const Kind = enum {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
test {
|
||||||
|
@import("std").testing.refAllDecls(@This());
|
||||||
|
}
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
//! to maintain and debug another set of shaders for each renderer instead of
|
//! to maintain and debug another set of shaders for each renderer instead of
|
||||||
//! just relying on the glyph system we already need to support for text
|
//! just relying on the glyph system we already need to support for text
|
||||||
//! anyways.
|
//! anyways.
|
||||||
|
//!
|
||||||
|
//! This also renders strikethrough, so its really more generally a
|
||||||
|
//! "horizontal line" renderer.
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
@ -71,6 +74,7 @@ const Draw = struct {
|
|||||||
.underline_dotted => self.drawDotted(canvas),
|
.underline_dotted => self.drawDotted(canvas),
|
||||||
.underline_dashed => self.drawDashed(canvas),
|
.underline_dashed => self.drawDashed(canvas),
|
||||||
.underline_curly => self.drawCurly(canvas),
|
.underline_curly => self.drawCurly(canvas),
|
||||||
|
.strikethrough => self.drawSingle(canvas),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -225,6 +229,24 @@ test "single" {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "strikethrough" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
var atlas_greyscale = try font.Atlas.init(alloc, 512, .greyscale);
|
||||||
|
defer atlas_greyscale.deinit(alloc);
|
||||||
|
|
||||||
|
_ = try renderGlyph(
|
||||||
|
alloc,
|
||||||
|
&atlas_greyscale,
|
||||||
|
.strikethrough,
|
||||||
|
36,
|
||||||
|
18,
|
||||||
|
9,
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
test "single large thickness" {
|
test "single large thickness" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
@ -452,8 +452,6 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
|||||||
.uniforms = .{
|
.uniforms = .{
|
||||||
.projection_matrix = undefined,
|
.projection_matrix = undefined,
|
||||||
.cell_size = undefined,
|
.cell_size = undefined,
|
||||||
.strikethrough_position = @floatFromInt(font_critical.metrics.strikethrough_position),
|
|
||||||
.strikethrough_thickness = @floatFromInt(font_critical.metrics.strikethrough_thickness),
|
|
||||||
.min_contrast = options.config.min_contrast,
|
.min_contrast = options.config.min_contrast,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -584,8 +582,6 @@ pub fn setFontGrid(self: *Metal, grid: *font.SharedGrid) void {
|
|||||||
@floatFromInt(metrics.cell_width),
|
@floatFromInt(metrics.cell_width),
|
||||||
@floatFromInt(metrics.cell_height),
|
@floatFromInt(metrics.cell_height),
|
||||||
},
|
},
|
||||||
.strikethrough_position = @floatFromInt(metrics.strikethrough_position),
|
|
||||||
.strikethrough_thickness = @floatFromInt(metrics.strikethrough_thickness),
|
|
||||||
.min_contrast = self.uniforms.min_contrast,
|
.min_contrast = self.uniforms.min_contrast,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1436,8 +1432,6 @@ pub fn setScreenSize(
|
|||||||
@floatFromInt(self.grid_metrics.cell_width),
|
@floatFromInt(self.grid_metrics.cell_width),
|
||||||
@floatFromInt(self.grid_metrics.cell_height),
|
@floatFromInt(self.grid_metrics.cell_height),
|
||||||
},
|
},
|
||||||
.strikethrough_position = old.strikethrough_position,
|
|
||||||
.strikethrough_thickness = old.strikethrough_thickness,
|
|
||||||
.min_contrast = old.min_contrast,
|
.min_contrast = old.min_contrast,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1914,12 +1908,25 @@ fn updateCell(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (style.flags.strikethrough) {
|
if (style.flags.strikethrough) {
|
||||||
|
const render = try self.font_grid.renderGlyph(
|
||||||
|
self.alloc,
|
||||||
|
font.sprite_index,
|
||||||
|
@intFromEnum(font.Sprite.strikethrough),
|
||||||
|
.{
|
||||||
|
.cell_width = if (cell.wide == .wide) 2 else 1,
|
||||||
|
.grid_metrics = self.grid_metrics,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
self.cells.appendAssumeCapacity(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
.mode = .strikethrough,
|
.mode = .fg,
|
||||||
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
|
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
|
||||||
.cell_width = cell.gridWidth(),
|
.cell_width = cell.gridWidth(),
|
||||||
.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,
|
.bg_color = bg,
|
||||||
|
.glyph_pos = .{ render.glyph.atlas_x, render.glyph.atlas_y },
|
||||||
|
.glyph_size = .{ render.glyph.width, render.glyph.height },
|
||||||
|
.glyph_offset = .{ render.glyph.offset_x, render.glyph.offset_y },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,17 +207,6 @@ const SetFontSize = struct {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bind = try gl_state.cell_program.program.use();
|
|
||||||
defer bind.unbind();
|
|
||||||
try gl_state.cell_program.program.setUniform(
|
|
||||||
"strikethrough_position",
|
|
||||||
@as(f32, @floatFromInt(self.metrics.strikethrough_position)),
|
|
||||||
);
|
|
||||||
try gl_state.cell_program.program.setUniform(
|
|
||||||
"strikethrough_thickness",
|
|
||||||
@as(f32, @floatFromInt(self.metrics.strikethrough_thickness)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1491,17 +1480,27 @@ fn updateCell(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (style.flags.strikethrough) {
|
if (style.flags.strikethrough) {
|
||||||
|
const render = try self.font_grid.renderGlyph(
|
||||||
|
self.alloc,
|
||||||
|
font.sprite_index,
|
||||||
|
@intFromEnum(font.Sprite.strikethrough),
|
||||||
|
.{
|
||||||
|
.cell_width = if (cell.wide == .wide) 2 else 1,
|
||||||
|
.grid_metrics = self.grid_metrics,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
self.cells.appendAssumeCapacity(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
.mode = .strikethrough,
|
.mode = .fg,
|
||||||
.grid_col = @intCast(x),
|
.grid_col = @intCast(x),
|
||||||
.grid_row = @intCast(y),
|
.grid_row = @intCast(y),
|
||||||
.grid_width = cell.gridWidth(),
|
.grid_width = cell.gridWidth(),
|
||||||
.glyph_x = 0,
|
.glyph_x = render.glyph.atlas_x,
|
||||||
.glyph_y = 0,
|
.glyph_y = render.glyph.atlas_y,
|
||||||
.glyph_width = 0,
|
.glyph_width = render.glyph.width,
|
||||||
.glyph_height = 0,
|
.glyph_height = render.glyph.height,
|
||||||
.glyph_offset_x = 0,
|
.glyph_offset_x = render.glyph.offset_x,
|
||||||
.glyph_offset_y = 0,
|
.glyph_offset_y = render.glyph.offset_y,
|
||||||
.r = colors.fg.r,
|
.r = colors.fg.r,
|
||||||
.g = colors.fg.g,
|
.g = colors.fg.g,
|
||||||
.b = colors.fg.b,
|
.b = colors.fg.b,
|
||||||
|
@ -103,7 +103,6 @@ pub const Cell = extern struct {
|
|||||||
fg = 2,
|
fg = 2,
|
||||||
fg_constrained = 3,
|
fg_constrained = 3,
|
||||||
fg_color = 7,
|
fg_color = 7,
|
||||||
strikethrough = 8,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -124,10 +123,6 @@ pub const Uniforms = extern struct {
|
|||||||
/// Size of a single cell in pixels, unscaled.
|
/// Size of a single cell in pixels, unscaled.
|
||||||
cell_size: [2]f32,
|
cell_size: [2]f32,
|
||||||
|
|
||||||
/// Metrics for underline/strikethrough
|
|
||||||
strikethrough_position: f32,
|
|
||||||
strikethrough_thickness: f32,
|
|
||||||
|
|
||||||
/// The minimum contrast ratio for text. The contrast ratio is calculated
|
/// The minimum contrast ratio for text. The contrast ratio is calculated
|
||||||
/// according to the WCAG 2.0 spec.
|
/// according to the WCAG 2.0 spec.
|
||||||
min_contrast: f32,
|
min_contrast: f32,
|
||||||
|
@ -53,7 +53,6 @@ pub const CellMode = enum(u8) {
|
|||||||
fg = 2,
|
fg = 2,
|
||||||
fg_constrained = 3,
|
fg_constrained = 3,
|
||||||
fg_color = 7,
|
fg_color = 7,
|
||||||
strikethrough = 8,
|
|
||||||
|
|
||||||
// Non-exhaustive because masks change it
|
// Non-exhaustive because masks change it
|
||||||
_,
|
_,
|
||||||
|
@ -28,7 +28,6 @@ const uint MODE_BG = 1u;
|
|||||||
const uint MODE_FG = 2u;
|
const uint MODE_FG = 2u;
|
||||||
const uint MODE_FG_CONSTRAINED = 3u;
|
const uint MODE_FG_CONSTRAINED = 3u;
|
||||||
const uint MODE_FG_COLOR = 7u;
|
const uint MODE_FG_COLOR = 7u;
|
||||||
const uint MODE_STRIKETHROUGH = 8u;
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
float a;
|
float a;
|
||||||
@ -48,9 +47,5 @@ void main() {
|
|||||||
case MODE_FG_COLOR:
|
case MODE_FG_COLOR:
|
||||||
out_FragColor = texture(text_color, glyph_tex_coords);
|
out_FragColor = texture(text_color, glyph_tex_coords);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MODE_STRIKETHROUGH:
|
|
||||||
out_FragColor = color;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,54 +2,51 @@ using namespace metal;
|
|||||||
|
|
||||||
// The possible modes that a shader can take.
|
// The possible modes that a shader can take.
|
||||||
enum Mode : uint8_t {
|
enum Mode : uint8_t {
|
||||||
MODE_BG = 1u,
|
MODE_BG = 1u,
|
||||||
MODE_FG = 2u,
|
MODE_FG = 2u,
|
||||||
MODE_FG_CONSTRAINED = 3u,
|
MODE_FG_CONSTRAINED = 3u,
|
||||||
MODE_FG_COLOR = 7u,
|
MODE_FG_COLOR = 7u,
|
||||||
MODE_STRIKETHROUGH = 8u,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
float4x4 projection_matrix;
|
float4x4 projection_matrix;
|
||||||
float2 cell_size;
|
float2 cell_size;
|
||||||
float strikethrough_position;
|
|
||||||
float strikethrough_thickness;
|
|
||||||
float min_contrast;
|
float min_contrast;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexIn {
|
struct VertexIn {
|
||||||
// The mode for this cell.
|
// The mode for this cell.
|
||||||
uint8_t mode [[ attribute(0) ]];
|
uint8_t mode [[attribute(0)]];
|
||||||
|
|
||||||
// The grid coordinates (x, y) where x < columns and y < rows
|
// The grid coordinates (x, y) where x < columns and y < rows
|
||||||
float2 grid_pos [[ attribute(1) ]];
|
float2 grid_pos [[attribute(1)]];
|
||||||
|
|
||||||
// The width of the cell in cells (i.e. 2 for double-wide).
|
// The width of the cell in cells (i.e. 2 for double-wide).
|
||||||
uint8_t cell_width [[ attribute(6) ]];
|
uint8_t cell_width [[attribute(6)]];
|
||||||
|
|
||||||
// The color. For BG modes, this is the bg color, for FG modes this is
|
// The color. For BG modes, this is the bg color, for FG modes this is
|
||||||
// the text color. For styles, this is the color of the style.
|
// the text color. For styles, this is the color of the style.
|
||||||
uchar4 color [[ attribute(5) ]];
|
uchar4 color [[attribute(5)]];
|
||||||
|
|
||||||
// The fields below are present only when rendering text (fg mode)
|
// The fields below are present only when rendering text (fg mode)
|
||||||
|
|
||||||
// The background color of the cell. This is used to determine if
|
// The background color of the cell. This is used to determine if
|
||||||
// we need to render the text with a different color to ensure
|
// we need to render the text with a different color to ensure
|
||||||
// contrast.
|
// contrast.
|
||||||
uchar4 bg_color [[ attribute(7) ]];
|
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)]];
|
||||||
|
|
||||||
// The size of the glyph in the texture (w,h)
|
// The size of the glyph in the texture (w,h)
|
||||||
uint2 glyph_size [[ attribute(3) ]];
|
uint2 glyph_size [[attribute(3)]];
|
||||||
|
|
||||||
// The left and top bearings for the glyph (x,y)
|
// The left and top bearings for the glyph (x,y)
|
||||||
int2 glyph_offset [[ attribute(4) ]];
|
int2 glyph_offset [[attribute(4)]];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexOut {
|
struct VertexOut {
|
||||||
float4 position [[ position ]];
|
float4 position [[position]];
|
||||||
float2 cell_size;
|
float2 cell_size;
|
||||||
uint8_t mode;
|
uint8_t mode;
|
||||||
float4 color;
|
float4 color;
|
||||||
@ -63,11 +60,11 @@ struct VertexOut {
|
|||||||
|
|
||||||
// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
|
// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
|
||||||
float luminance_component(float c) {
|
float luminance_component(float c) {
|
||||||
if (c <= 0.03928f) {
|
if (c <= 0.03928f) {
|
||||||
return c / 12.92f;
|
return c / 12.92f;
|
||||||
} else {
|
} else {
|
||||||
return pow((c + 0.055f) / 1.055f, 2.4f);
|
return pow((c + 0.055f) / 1.055f, 2.4f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float relative_luminance(float3 color) {
|
float relative_luminance(float3 color) {
|
||||||
@ -89,20 +86,20 @@ float contrast_ratio(float3 color1, float3 color2) {
|
|||||||
// return a color that satisfies the contrast ratio. Currently, the color
|
// return a color that satisfies the contrast ratio. Currently, the color
|
||||||
// is always white or black, whichever has the highest contrast ratio.
|
// is always white or black, whichever has the highest contrast ratio.
|
||||||
float4 contrasted_color(float min, float4 fg, float4 bg) {
|
float4 contrasted_color(float min, float4 fg, float4 bg) {
|
||||||
float3 fg_premult = fg.rgb * fg.a;
|
float3 fg_premult = fg.rgb * fg.a;
|
||||||
float3 bg_premult = bg.rgb * bg.a;
|
float3 bg_premult = bg.rgb * bg.a;
|
||||||
float ratio = contrast_ratio(fg_premult, bg_premult);
|
float ratio = contrast_ratio(fg_premult, bg_premult);
|
||||||
if (ratio < min) {
|
if (ratio < min) {
|
||||||
float white_ratio = contrast_ratio(float3(1.0f), bg_premult);
|
float white_ratio = contrast_ratio(float3(1.0f), bg_premult);
|
||||||
float black_ratio = contrast_ratio(float3(0.0f), bg_premult);
|
float black_ratio = contrast_ratio(float3(0.0f), bg_premult);
|
||||||
if (white_ratio > black_ratio) {
|
if (white_ratio > black_ratio) {
|
||||||
return float4(1.0f);
|
return float4(1.0f);
|
||||||
} else {
|
} else {
|
||||||
return float4(0.0f, 0.0f, 0.0f, 1.0f);
|
return float4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return fg;
|
return fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
@ -110,11 +107,9 @@ float4 contrasted_color(float min, float4 fg, float4 bg) {
|
|||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
#pragma mark - Terminal Grid Cell Shader
|
#pragma mark - Terminal Grid Cell Shader
|
||||||
|
|
||||||
vertex VertexOut uber_vertex(
|
vertex VertexOut uber_vertex(unsigned int vid [[vertex_id]],
|
||||||
unsigned int vid [[ vertex_id ]],
|
VertexIn input [[stage_in]],
|
||||||
VertexIn input [[ stage_in ]],
|
constant Uniforms& uniforms [[buffer(1)]]) {
|
||||||
constant Uniforms &uniforms [[ buffer(1) ]]
|
|
||||||
) {
|
|
||||||
// Convert the grid x,y into world space x, y by accounting for cell size
|
// Convert the grid x,y into world space x, y by accounting for cell size
|
||||||
float2 cell_pos = uniforms.cell_size * input.grid_pos;
|
float2 cell_pos = uniforms.cell_size * input.grid_pos;
|
||||||
|
|
||||||
@ -141,115 +136,98 @@ vertex VertexOut uber_vertex(
|
|||||||
out.cell_size = uniforms.cell_size;
|
out.cell_size = uniforms.cell_size;
|
||||||
out.color = float4(input.color) / 255.0f;
|
out.color = float4(input.color) / 255.0f;
|
||||||
switch (input.mode) {
|
switch (input.mode) {
|
||||||
case MODE_BG:
|
case MODE_BG:
|
||||||
// Calculate the final position of our cell in world space.
|
// Calculate the final position of our cell in world space.
|
||||||
// We have to add our cell size since our vertices are offset
|
// We have to add our cell size since our vertices are offset
|
||||||
// one cell up and to the left. (Do the math to verify yourself)
|
// one cell up and to the left. (Do the math to verify yourself)
|
||||||
cell_pos = cell_pos + cell_size_scaled * position;
|
cell_pos = cell_pos + cell_size_scaled * position;
|
||||||
|
|
||||||
out.position = uniforms.projection_matrix * float4(cell_pos.x, cell_pos.y, 0.0f, 1.0f);
|
out.position = uniforms.projection_matrix *
|
||||||
break;
|
float4(cell_pos.x, cell_pos.y, 0.0f, 1.0f);
|
||||||
|
break;
|
||||||
|
|
||||||
case MODE_FG:
|
case MODE_FG:
|
||||||
case MODE_FG_CONSTRAINED:
|
case MODE_FG_CONSTRAINED:
|
||||||
case MODE_FG_COLOR: {
|
case MODE_FG_COLOR: {
|
||||||
float2 glyph_size = float2(input.glyph_size);
|
float2 glyph_size = float2(input.glyph_size);
|
||||||
float2 glyph_offset = float2(input.glyph_offset);
|
float2 glyph_offset = float2(input.glyph_offset);
|
||||||
|
|
||||||
// The glyph_offset.y is the y bearing, a y value that when added
|
// The glyph_offset.y is the y bearing, a y value that when added
|
||||||
// to the baseline is the offset (+y is up). Our grid goes down.
|
// to the baseline is the offset (+y is up). Our grid goes down.
|
||||||
// So we flip it with `cell_size.y - glyph_offset.y`.
|
// So we flip it with `cell_size.y - glyph_offset.y`.
|
||||||
glyph_offset.y = cell_size_scaled.y - glyph_offset.y;
|
glyph_offset.y = cell_size_scaled.y - glyph_offset.y;
|
||||||
|
|
||||||
// If we're constrained then we need to scale the glyph.
|
// If we're constrained then we need to scale the glyph.
|
||||||
// We also always constrain colored glyphs since we should have
|
// We also always constrain colored glyphs since we should have
|
||||||
// their scaled cell size exactly correct.
|
// their scaled cell size exactly correct.
|
||||||
if (input.mode == MODE_FG_CONSTRAINED || input.mode == MODE_FG_COLOR) {
|
if (input.mode == MODE_FG_CONSTRAINED || input.mode == MODE_FG_COLOR) {
|
||||||
if (glyph_size.x > cell_size_scaled.x) {
|
if (glyph_size.x > cell_size_scaled.x) {
|
||||||
float new_y = glyph_size.y * (cell_size_scaled.x / glyph_size.x);
|
float new_y = glyph_size.y * (cell_size_scaled.x / glyph_size.x);
|
||||||
glyph_offset.y += (glyph_size.y - new_y) / 2;
|
glyph_offset.y += (glyph_size.y - new_y) / 2;
|
||||||
glyph_size.y = new_y;
|
glyph_size.y = new_y;
|
||||||
glyph_size.x = cell_size_scaled.x;
|
glyph_size.x = cell_size_scaled.x;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate the final position of the cell which uses our glyph size
|
||||||
|
// and glyph offset to create the correct bounding box for the glyph.
|
||||||
|
cell_pos = cell_pos + glyph_size * position + glyph_offset;
|
||||||
|
out.position = uniforms.projection_matrix *
|
||||||
|
float4(cell_pos.x, cell_pos.y, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
// Calculate the texture coordinate in pixels. This is NOT normalized
|
||||||
|
// (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;
|
||||||
|
|
||||||
|
// If we have a minimum contrast, we need to check if we need to
|
||||||
|
// change the color of the text to ensure it has enough contrast
|
||||||
|
// with the background.
|
||||||
|
if (uniforms.min_contrast > 1.0f && input.mode == MODE_FG) {
|
||||||
|
float4 bg_color = float4(input.bg_color) / 255.0f;
|
||||||
|
out.color =
|
||||||
|
contrasted_color(uniforms.min_contrast, out.color, bg_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the final position of the cell which uses our glyph size
|
|
||||||
// and glyph offset to create the correct bounding box for the glyph.
|
|
||||||
cell_pos = cell_pos + glyph_size * position + glyph_offset;
|
|
||||||
out.position = uniforms.projection_matrix * float4(cell_pos.x, cell_pos.y, 0.0f, 1.0f);
|
|
||||||
|
|
||||||
// Calculate the texture coordinate in pixels. This is NOT normalized
|
|
||||||
// (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;
|
|
||||||
|
|
||||||
// If we have a minimum contrast, we need to check if we need to
|
|
||||||
// change the color of the text to ensure it has enough contrast
|
|
||||||
// with the background.
|
|
||||||
if (uniforms.min_contrast > 1.0f && input.mode == MODE_FG) {
|
|
||||||
float4 bg_color = float4(input.bg_color) / 255.0f;
|
|
||||||
out.color = contrasted_color(uniforms.min_contrast, out.color, bg_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MODE_STRIKETHROUGH: {
|
|
||||||
// Strikethrough Y value is just our thickness
|
|
||||||
float2 strikethrough_size = float2(cell_size_scaled.x, uniforms.strikethrough_thickness);
|
|
||||||
|
|
||||||
// Position the strikethrough where we are told to
|
|
||||||
float2 strikethrough_offset = float2(cell_size_scaled.x, uniforms.strikethrough_position);
|
|
||||||
|
|
||||||
// Go to the bottom of the cell, take away the size of the
|
|
||||||
// strikethrough, and that is our position. We also float it slightly
|
|
||||||
// above the bottom.
|
|
||||||
cell_pos = cell_pos + strikethrough_offset - (strikethrough_size * position);
|
|
||||||
|
|
||||||
out.position = uniforms.projection_matrix * float4(cell_pos, 0.0f, 1.0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment float4 uber_fragment(
|
fragment float4 uber_fragment(VertexOut in [[stage_in]],
|
||||||
VertexOut in [[ stage_in ]],
|
texture2d<float> textureGreyscale [[texture(0)]],
|
||||||
texture2d<float> textureGreyscale [[ texture(0) ]],
|
texture2d<float> textureColor [[texture(1)]]) {
|
||||||
texture2d<float> textureColor [[ texture(1) ]]
|
|
||||||
) {
|
|
||||||
constexpr sampler textureSampler(address::clamp_to_edge, filter::linear);
|
constexpr sampler textureSampler(address::clamp_to_edge, filter::linear);
|
||||||
|
|
||||||
switch (in.mode) {
|
switch (in.mode) {
|
||||||
case MODE_BG:
|
case MODE_BG:
|
||||||
return in.color;
|
return in.color;
|
||||||
|
|
||||||
case MODE_FG_CONSTRAINED:
|
case MODE_FG_CONSTRAINED:
|
||||||
case MODE_FG: {
|
case MODE_FG: {
|
||||||
// Normalize the texture coordinates to [0,1]
|
// Normalize the texture coordinates to [0,1]
|
||||||
float2 size = float2(textureGreyscale.get_width(), textureGreyscale.get_height());
|
float2 size =
|
||||||
float2 coord = in.tex_coord / size;
|
float2(textureGreyscale.get_width(), textureGreyscale.get_height());
|
||||||
|
float2 coord = in.tex_coord / size;
|
||||||
|
|
||||||
// We premult the alpha to our whole color since our blend function
|
// We premult the alpha to our whole color since our blend function
|
||||||
// uses One/OneMinusSourceAlpha to avoid blurry edges.
|
// uses One/OneMinusSourceAlpha to avoid blurry edges.
|
||||||
// We first premult our given color.
|
// We first premult our given color.
|
||||||
float4 premult = float4(in.color.rgb * in.color.a, in.color.a);
|
float4 premult = float4(in.color.rgb * in.color.a, in.color.a);
|
||||||
// Then premult the texture color
|
// Then premult the texture color
|
||||||
float a = textureGreyscale.sample(textureSampler, coord).r;
|
float a = textureGreyscale.sample(textureSampler, coord).r;
|
||||||
premult = premult * a;
|
premult = premult * a;
|
||||||
return premult;
|
return premult;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MODE_FG_COLOR: {
|
case MODE_FG_COLOR: {
|
||||||
// Normalize the texture coordinates to [0,1]
|
// Normalize the texture coordinates to [0,1]
|
||||||
float2 size = float2(textureColor.get_width(), textureColor.get_height());
|
float2 size = float2(textureColor.get_width(), textureColor.get_height());
|
||||||
float2 coord = in.tex_coord / size;
|
float2 coord = in.tex_coord / size;
|
||||||
return textureColor.sample(textureSampler, coord);
|
return textureColor.sample(textureSampler, coord);
|
||||||
}
|
}
|
||||||
|
|
||||||
case MODE_STRIKETHROUGH:
|
|
||||||
return in.color;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,30 +239,28 @@ fragment float4 uber_fragment(
|
|||||||
struct ImageVertexIn {
|
struct ImageVertexIn {
|
||||||
// The grid coordinates (x, y) where x < columns and y < rows where
|
// The grid coordinates (x, y) where x < columns and y < rows where
|
||||||
// the image will be rendered. It will be rendered from the top left.
|
// the image will be rendered. It will be rendered from the top left.
|
||||||
float2 grid_pos [[ attribute(1) ]];
|
float2 grid_pos [[attribute(1)]];
|
||||||
|
|
||||||
// Offset in pixels from the top-left of the cell to make the top-left
|
// Offset in pixels from the top-left of the cell to make the top-left
|
||||||
// corner of the image.
|
// corner of the image.
|
||||||
float2 cell_offset [[ attribute(2) ]];
|
float2 cell_offset [[attribute(2)]];
|
||||||
|
|
||||||
// The source rectangle of the texture to sample from.
|
// The source rectangle of the texture to sample from.
|
||||||
float4 source_rect [[ attribute(3) ]];
|
float4 source_rect [[attribute(3)]];
|
||||||
|
|
||||||
// The final width/height of the image in pixels.
|
// The final width/height of the image in pixels.
|
||||||
float2 dest_size [[ attribute(4) ]];
|
float2 dest_size [[attribute(4)]];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ImageVertexOut {
|
struct ImageVertexOut {
|
||||||
float4 position [[ position ]];
|
float4 position [[position]];
|
||||||
float2 tex_coord;
|
float2 tex_coord;
|
||||||
};
|
};
|
||||||
|
|
||||||
vertex ImageVertexOut image_vertex(
|
vertex ImageVertexOut image_vertex(unsigned int vid [[vertex_id]],
|
||||||
unsigned int vid [[ vertex_id ]],
|
ImageVertexIn input [[stage_in]],
|
||||||
ImageVertexIn input [[ stage_in ]],
|
texture2d<uint> image [[texture(0)]],
|
||||||
texture2d<uint> image [[ texture(0) ]],
|
constant Uniforms& uniforms [[buffer(1)]]) {
|
||||||
constant Uniforms &uniforms [[ buffer(1) ]]
|
|
||||||
) {
|
|
||||||
// The size of the image in pixels
|
// The size of the image in pixels
|
||||||
float2 image_size = float2(image.get_width(), image.get_height());
|
float2 image_size = float2(image.get_width(), image.get_height());
|
||||||
|
|
||||||
@ -315,15 +291,14 @@ vertex ImageVertexOut image_vertex(
|
|||||||
float2 image_pos = (uniforms.cell_size * input.grid_pos) + input.cell_offset;
|
float2 image_pos = (uniforms.cell_size * input.grid_pos) + input.cell_offset;
|
||||||
image_pos += input.dest_size * position;
|
image_pos += input.dest_size * position;
|
||||||
|
|
||||||
out.position = uniforms.projection_matrix * float4(image_pos.x, image_pos.y, 0.0f, 1.0f);
|
out.position =
|
||||||
|
uniforms.projection_matrix * float4(image_pos.x, image_pos.y, 0.0f, 1.0f);
|
||||||
out.tex_coord = tex_coord;
|
out.tex_coord = tex_coord;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment float4 image_fragment(
|
fragment float4 image_fragment(ImageVertexOut in [[stage_in]],
|
||||||
ImageVertexOut in [[ stage_in ]],
|
texture2d<uint> image [[texture(0)]]) {
|
||||||
texture2d<uint> image [[ texture(0) ]]
|
|
||||||
) {
|
|
||||||
constexpr sampler textureSampler(address::clamp_to_edge, filter::linear);
|
constexpr sampler textureSampler(address::clamp_to_edge, filter::linear);
|
||||||
|
|
||||||
// Ehhhhh our texture is in RGBA8Uint but our color attachment is
|
// Ehhhhh our texture is in RGBA8Uint but our color attachment is
|
||||||
@ -344,13 +319,13 @@ fragment float4 image_fragment(
|
|||||||
#pragma mark - Post Shader
|
#pragma mark - Post Shader
|
||||||
|
|
||||||
struct PostVertexOut {
|
struct PostVertexOut {
|
||||||
float4 position [[ position ]];
|
float4 position [[position]];
|
||||||
};
|
};
|
||||||
|
|
||||||
constant float2 post_pos[4] = { {-1,-1}, {1,-1}, {-1,1}, {1,1 } };
|
constant float2 post_pos[4] = {{-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
|
||||||
|
|
||||||
vertex PostVertexOut post_vertex(uint id [[ vertex_id ]]) {
|
vertex PostVertexOut post_vertex(uint id [[vertex_id]]) {
|
||||||
PostVertexOut out;
|
PostVertexOut out;
|
||||||
out.position = float4(post_pos[id], 0, 1);
|
out.position = float4(post_pos[id], 0, 1);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ const uint MODE_BG = 1u;
|
|||||||
const uint MODE_FG = 2u;
|
const uint MODE_FG = 2u;
|
||||||
const uint MODE_FG_CONSTRAINED = 3u;
|
const uint MODE_FG_CONSTRAINED = 3u;
|
||||||
const uint MODE_FG_COLOR = 7u;
|
const uint MODE_FG_COLOR = 7u;
|
||||||
const uint MODE_STRIKETHROUGH = 8u;
|
|
||||||
|
|
||||||
// The grid coordinates (x, y) where x < columns and y < rows
|
// The grid coordinates (x, y) where x < columns and y < rows
|
||||||
layout (location = 0) in vec2 grid_coord;
|
layout (location = 0) in vec2 grid_coord;
|
||||||
@ -57,8 +56,6 @@ uniform sampler2D text;
|
|||||||
uniform sampler2D text_color;
|
uniform sampler2D text_color;
|
||||||
uniform vec2 cell_size;
|
uniform vec2 cell_size;
|
||||||
uniform mat4 projection;
|
uniform mat4 projection;
|
||||||
uniform float strikethrough_position;
|
|
||||||
uniform float strikethrough_thickness;
|
|
||||||
uniform float min_contrast;
|
uniform float min_contrast;
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
@ -233,21 +230,5 @@ void main() {
|
|||||||
}
|
}
|
||||||
color = color_final;
|
color = color_final;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MODE_STRIKETHROUGH:
|
|
||||||
// Strikethrough Y value is just our thickness
|
|
||||||
vec2 strikethrough_size = vec2(cell_size_scaled.x, strikethrough_thickness);
|
|
||||||
|
|
||||||
// Position the strikethrough where we are told to
|
|
||||||
vec2 strikethrough_offset = vec2(cell_size_scaled.x, strikethrough_position) ;
|
|
||||||
|
|
||||||
// Go to the bottom of the cell, take away the size of the
|
|
||||||
// strikethrough, and that is our position. We also float it slightly
|
|
||||||
// above the bottom.
|
|
||||||
cell_pos = cell_pos + strikethrough_offset - (strikethrough_size * position);
|
|
||||||
|
|
||||||
gl_Position = projection * vec4(cell_pos, cell_z, 1.0);
|
|
||||||
color = color_in / 255.0;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user