diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/renderer/shaders/glsl | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (diff) | |
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/renderer/shaders/glsl')
10 files changed, 776 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/renderer/shaders/glsl/bg_color.f.glsl b/vendor/libghostty-vt/src/renderer/shaders/glsl/bg_color.f.glsl new file mode 100644 index 00000000..616c44b8 --- /dev/null +++ b/vendor/libghostty-vt/src/renderer/shaders/glsl/bg_color.f.glsl @@ -0,0 +1,13 @@ +#include "common.glsl" + +// Must declare this output for some versions of OpenGL. +layout(location = 0) out vec4 out_FragColor; + +void main() { + bool use_linear_blending = (bools & USE_LINEAR_BLENDING) != 0; + + out_FragColor = load_color( + unpack4u8(bg_color_packed_4u8), + use_linear_blending + ); +} diff --git a/vendor/libghostty-vt/src/renderer/shaders/glsl/bg_image.f.glsl b/vendor/libghostty-vt/src/renderer/shaders/glsl/bg_image.f.glsl new file mode 100644 index 00000000..ee1195ef --- /dev/null +++ b/vendor/libghostty-vt/src/renderer/shaders/glsl/bg_image.f.glsl @@ -0,0 +1,63 @@ +#include "common.glsl" + +// Position the FragCoord origin to the upper left +// so as to align with our texture's directionality. +layout(origin_upper_left) in vec4 gl_FragCoord; + +layout(binding = 0) uniform sampler2D image; + +flat in vec4 bg_color; +flat in vec2 offset; +flat in vec2 scale; +flat in float opacity; +flat in uint repeat; + +layout(location = 0) out vec4 out_FragColor; + +void main() { + bool use_linear_blending = (bools & USE_LINEAR_BLENDING) != 0; + + // Our texture coordinate is based on the screen position, offset by the + // dest rect origin, and scaled by the ratio between the dest rect size + // and the original texture size, which effectively scales the original + // size of the texture to the dest rect size. + vec2 tex_coord = (gl_FragCoord.xy - offset) * scale; + + vec2 tex_size = textureSize(image, 0); + + // If we need to repeat the texture, wrap the coordinates. + if (repeat != 0) { + tex_coord = mod(mod(tex_coord, tex_size) + tex_size, tex_size); + } + + vec4 rgba; + // If we're out of bounds, we have no color, + // otherwise we sample the texture for it. + if (any(lessThan(tex_coord, vec2(0.0))) || + any(greaterThan(tex_coord, tex_size))) + { + rgba = vec4(0.0); + } else { + // We divide by the texture size to normalize for sampling. + rgba = texture(image, tex_coord / tex_size); + + if (!use_linear_blending) { + rgba = unlinearize(rgba); + } + + rgba.rgb *= rgba.a; + } + + // Multiply it by the configured opacity, but cap it at + // the value that will make it fully opaque relative to + // the background color alpha, so it isn't overexposed. + rgba *= min(opacity, 1.0 / bg_color.a); + + // Blend it on to a fully opaque version of the background color. + rgba += max(vec4(0.0), vec4(bg_color.rgb, 1.0) * vec4(1.0 - rgba.a)); + + // Multiply everything by the background color alpha. + rgba *= bg_color.a; + + out_FragColor = rgba; +} diff --git a/vendor/libghostty-vt/src/renderer/shaders/glsl/bg_image.v.glsl b/vendor/libghostty-vt/src/renderer/shaders/glsl/bg_image.v.glsl new file mode 100644 index 00000000..d55aa174 --- /dev/null +++ b/vendor/libghostty-vt/src/renderer/shaders/glsl/bg_image.v.glsl @@ -0,0 +1,145 @@ +#include "common.glsl" + +layout(binding = 0) uniform sampler2D image; + +layout(location = 0) in float in_opacity; +layout(location = 1) in uint info; + +// 4 bits of info. +const uint BG_IMAGE_POSITION = 15u; +const uint BG_IMAGE_TL = 0u; +const uint BG_IMAGE_TC = 1u; +const uint BG_IMAGE_TR = 2u; +const uint BG_IMAGE_ML = 3u; +const uint BG_IMAGE_MC = 4u; +const uint BG_IMAGE_MR = 5u; +const uint BG_IMAGE_BL = 6u; +const uint BG_IMAGE_BC = 7u; +const uint BG_IMAGE_BR = 8u; + +// 2 bits of info shifted 4. +const uint BG_IMAGE_FIT = 3u << 4; +const uint BG_IMAGE_CONTAIN = 0u << 4; +const uint BG_IMAGE_COVER = 1u << 4; +const uint BG_IMAGE_STRETCH = 2u << 4; +const uint BG_IMAGE_NO_FIT = 3u << 4; + +// 1 bit of info shifted 6. +const uint BG_IMAGE_REPEAT = 1u << 6; + +flat out vec4 bg_color; +flat out vec2 offset; +flat out vec2 scale; +flat out float opacity; +// We use a uint to pass the repeat value because +// bools aren't allowed for vertex outputs in OpenGL. +flat out uint repeat; + +void main() { + bool use_linear_blending = (bools & USE_LINEAR_BLENDING) != 0; + + vec4 position; + position.x = (gl_VertexID == 2) ? 3.0 : -1.0; + position.y = (gl_VertexID == 0) ? -3.0 : 1.0; + position.z = 1.0; + position.w = 1.0; + + // Single triangle is clipped to viewport. + // + // X <- vid == 0: (-1, -3) + // |\ + // | \ + // | \ + // |###\ + // |#+# \ `+` is (0, 0). `#`s are viewport area. + // |### \ + // X------X <- vid == 2: (3, 1) + // ^ + // vid == 1: (-1, 1) + + gl_Position = position; + + opacity = in_opacity; + + repeat = info & BG_IMAGE_REPEAT; + + vec2 screen_size = screen_size; + vec2 tex_size = textureSize(image, 0); + + vec2 dest_size = tex_size; + switch (info & BG_IMAGE_FIT) { + // For `contain` we scale by a factor that makes the image + // width match the screen width or makes the image height + // match the screen height, whichever is smaller. + case BG_IMAGE_CONTAIN: { + float scale = min(screen_size.x / tex_size.x, screen_size.y / tex_size.y); + dest_size = tex_size * scale; + } break; + + // For `cover` we scale by a factor that makes the image + // width match the screen width or makes the image height + // match the screen height, whichever is larger. + case BG_IMAGE_COVER: { + float scale = max(screen_size.x / tex_size.x, screen_size.y / tex_size.y); + dest_size = tex_size * scale; + } break; + + // For `stretch` we stretch the image to the size of + // the screen without worrying about aspect ratio. + case BG_IMAGE_STRETCH: { + dest_size = screen_size; + } break; + + // For `none` we just use the original texture size. + case BG_IMAGE_NO_FIT: { + dest_size = tex_size; + } break; + } + + vec2 start = vec2(0.0); + vec2 mid = (screen_size - dest_size) / vec2(2.0); + vec2 end = screen_size - dest_size; + + vec2 dest_offset = mid; + switch (info & BG_IMAGE_POSITION) { + case BG_IMAGE_TL: { + dest_offset = vec2(start.x, start.y); + } break; + case BG_IMAGE_TC: { + dest_offset = vec2(mid.x, start.y); + } break; + case BG_IMAGE_TR: { + dest_offset = vec2(end.x, start.y); + } break; + case BG_IMAGE_ML: { + dest_offset = vec2(start.x, mid.y); + } break; + case BG_IMAGE_MC: { + dest_offset = vec2(mid.x, mid.y); + } break; + case BG_IMAGE_MR: { + dest_offset = vec2(end.x, mid.y); + } break; + case BG_IMAGE_BL: { + dest_offset = vec2(start.x, end.y); + } break; + case BG_IMAGE_BC: { + dest_offset = vec2(mid.x, end.y); + } break; + case BG_IMAGE_BR: { + dest_offset = vec2(end.x, end.y); + } break; + } + + offset = dest_offset; + scale = tex_size / dest_size; + + // We load a fully opaque version of the bg color and combine it with + // the alpha separately, because we need these as separate values in + // the framgment shader. + uvec4 u_bg_color = unpack4u8(bg_color_packed_4u8); + bg_color = vec4(load_color( + uvec4(u_bg_color.rgb, 255), + use_linear_blending + ).rgb, float(u_bg_color.a) / 255.0); +} diff --git a/vendor/libghostty-vt/src/renderer/shaders/glsl/cell_bg.f.glsl b/vendor/libghostty-vt/src/renderer/shaders/glsl/cell_bg.f.glsl new file mode 100644 index 00000000..fa48c673 --- /dev/null +++ b/vendor/libghostty-vt/src/renderer/shaders/glsl/cell_bg.f.glsl @@ -0,0 +1,61 @@ +#include "common.glsl" + +// Position the origin to the upper left +layout(origin_upper_left) in vec4 gl_FragCoord; + +// Must declare this output for some versions of OpenGL. +layout(location = 0) out vec4 out_FragColor; + +layout(binding = 1, std430) readonly buffer bg_cells { + uint cells[]; +}; + +vec4 cell_bg() { + uvec2 grid_size = unpack2u16(grid_size_packed_2u16); + ivec2 grid_pos = ivec2(floor((gl_FragCoord.xy - grid_padding.wx) / cell_size)); + bool use_linear_blending = (bools & USE_LINEAR_BLENDING) != 0; + + vec4 bg = vec4(0.0); + + // Clamp x position, extends edge bg colors in to padding on sides. + if (grid_pos.x < 0) { + if ((padding_extend & EXTEND_LEFT) != 0) { + grid_pos.x = 0; + } else { + return bg; + } + } else if (grid_pos.x > grid_size.x - 1) { + if ((padding_extend & EXTEND_RIGHT) != 0) { + grid_pos.x = int(grid_size.x) - 1; + } else { + return bg; + } + } + + // Clamp y position if we should extend, otherwise discard if out of bounds. + if (grid_pos.y < 0) { + if ((padding_extend & EXTEND_UP) != 0) { + grid_pos.y = 0; + } else { + return bg; + } + } else if (grid_pos.y > grid_size.y - 1) { + if ((padding_extend & EXTEND_DOWN) != 0) { + grid_pos.y = int(grid_size.y) - 1; + } else { + return bg; + } + } + + // Load the color for the cell. + vec4 cell_color = load_color( + unpack4u8(cells[grid_pos.y * grid_size.x + grid_pos.x]), + use_linear_blending + ); + + return cell_color; +} + +void main() { + out_FragColor = cell_bg(); +} diff --git a/vendor/libghostty-vt/src/renderer/shaders/glsl/cell_text.f.glsl b/vendor/libghostty-vt/src/renderer/shaders/glsl/cell_text.f.glsl new file mode 100644 index 00000000..176efcbd --- /dev/null +++ b/vendor/libghostty-vt/src/renderer/shaders/glsl/cell_text.f.glsl @@ -0,0 +1,100 @@ +#include "common.glsl" + +layout(binding = 0) uniform sampler2DRect atlas_grayscale; +layout(binding = 1) uniform sampler2DRect atlas_color; + +in CellTextVertexOut { + flat uint atlas; + flat vec4 color; + flat vec4 bg_color; + vec2 tex_coord; +} in_data; + +// Values `atlas` can take. +const uint ATLAS_GRAYSCALE = 0u; +const uint ATLAS_COLOR = 1u; + +// Must declare this output for some versions of OpenGL. +layout(location = 0) out vec4 out_FragColor; + +void main() { + bool use_linear_blending = (bools & USE_LINEAR_BLENDING) != 0; + bool use_linear_correction = (bools & USE_LINEAR_CORRECTION) != 0; + + switch (in_data.atlas) { + default: + case ATLAS_GRAYSCALE: + { + // Our input color is always linear. + vec4 color = in_data.color; + + // If we're not doing linear blending, then we need to + // re-apply the gamma encoding to our color manually. + // + // Since the alpha is premultiplied, we need to divide + // it out before unlinearizing and re-multiply it after. + if (!use_linear_blending) { + color.rgb /= vec3(color.a); + color = unlinearize(color); + color.rgb *= vec3(color.a); + } + + // Fetch our alpha mask for this pixel. + float a = texture(atlas_grayscale, in_data.tex_coord).r; + + // Linear blending weight correction corrects the alpha value to + // produce blending results which match gamma-incorrect blending. + if (use_linear_correction) { + // Short explanation of how this works: + // + // We get the luminances of the foreground and background colors, + // and then unlinearize them and perform blending on them. This + // gives us our desired luminance, which we derive our new alpha + // value from by mapping the range [bg_l, fg_l] to [0, 1], since + // our final blend will be a linear interpolation from bg to fg. + // + // This yields virtually identical results for grayscale blending, + // and very similar but non-identical results for color blending. + vec4 bg = in_data.bg_color; + float fg_l = luminance(color.rgb); + float bg_l = luminance(bg.rgb); + // To avoid numbers going haywire, we don't apply correction + // when the bg and fg luminances are within 0.001 of each other. + if (abs(fg_l - bg_l) > 0.001) { + float blend_l = linearize(unlinearize(fg_l) * a + unlinearize(bg_l) * (1.0 - a)); + a = clamp((blend_l - bg_l) / (fg_l - bg_l), 0.0, 1.0); + } + } + + // Multiply our whole color by the alpha mask. + // Since we use premultiplied alpha, this is + // the correct way to apply the mask. + color *= a; + + out_FragColor = color; + return; + } + + case ATLAS_COLOR: + { + // For now, we assume that color glyphs + // are already premultiplied linear colors. + vec4 color = texture(atlas_color, in_data.tex_coord); + + // If we are doing linear blending, we can return this right away. + if (use_linear_blending) { + out_FragColor = color; + return; + } + + // Otherwise we need to unlinearize the color. Since the alpha is + // premultiplied, we need to divide it out before unlinearizing. + color.rgb /= vec3(color.a); + color = unlinearize(color); + color.rgb *= vec3(color.a); + + out_FragColor = color; + return; + } + } +} diff --git a/vendor/libghostty-vt/src/renderer/shaders/glsl/cell_text.v.glsl b/vendor/libghostty-vt/src/renderer/shaders/glsl/cell_text.v.glsl new file mode 100644 index 00000000..7e38e2f0 --- /dev/null +++ b/vendor/libghostty-vt/src/renderer/shaders/glsl/cell_text.v.glsl @@ -0,0 +1,146 @@ +#include "common.glsl" + +// The position of the glyph in the texture (x, y) +layout(location = 0) in uvec2 glyph_pos; + +// The size of the glyph in the texture (w, h) +layout(location = 1) in uvec2 glyph_size; + +// The left and top bearings for the glyph (x, y) +layout(location = 2) in ivec2 bearings; + +// The grid coordinates (x, y) where x < columns and y < rows +layout(location = 3) in uvec2 grid_pos; + +// The color of the rendered text glyph. +layout(location = 4) in uvec4 color; + +// Which atlas this glyph is in. +layout(location = 5) in uint atlas; + +// Misc glyph properties. +layout(location = 6) in uint glyph_bools; + +// Values `atlas` can take. +const uint ATLAS_GRAYSCALE = 0u; +const uint ATLAS_COLOR = 1u; + +// Masks for the `glyph_bools` attribute +const uint NO_MIN_CONTRAST = 1u; +const uint IS_CURSOR_GLYPH = 2u; + +out CellTextVertexOut { + flat uint atlas; + flat vec4 color; + flat vec4 bg_color; + vec2 tex_coord; +} out_data; + +layout(binding = 1, std430) readonly buffer bg_cells { + uint bg_colors[]; +}; + +void main() { + uvec2 grid_size = unpack2u16(grid_size_packed_2u16); + uvec2 cursor_pos = unpack2u16(cursor_pos_packed_2u16); + bool cursor_wide = (bools & CURSOR_WIDE) != 0; + bool use_linear_blending = (bools & USE_LINEAR_BLENDING) != 0; + + // Convert the grid x, y into world space x, y by accounting for cell size + vec2 cell_pos = cell_size * vec2(grid_pos); + + int vid = gl_VertexID; + + // We use a triangle strip with 4 vertices to render quads, + // so we determine which corner of the cell this vertex is in + // based on the vertex ID. + // + // 0 --> 1 + // | .'| + // | / | + // | L | + // 2 --> 3 + // + // 0 = top-left (0, 0) + // 1 = top-right (1, 0) + // 2 = bot-left (0, 1) + // 3 = bot-right (1, 1) + vec2 corner; + corner.x = float(vid == 1 || vid == 3); + corner.y = float(vid == 2 || vid == 3); + + out_data.atlas = atlas; + + // === Grid Cell === + // +X + // 0,0--...-> + // | + // . offset.x = bearings.x + // +Y. .|. + // . | | + // | cell_pos -> +-------+ _. + // v ._| |_. _|- offset.y = cell_size.y - bearings.y + // | | .###. | | + // | | #...# | | + // glyph_size.y -+ | ##### | | + // | | #.... | +- bearings.y + // |_| .#### | | + // | |_| + // +-------+ + // |_._| + // | + // glyph_size.x + // + // In order to get the top left of the glyph, we compute an offset based on + // the bearings. The Y bearing is the distance from the bottom of the cell + // to the top of the glyph, so we subtract it from the cell height to get + // the y offset. The X bearing is the distance from the left of the cell + // to the left of the glyph, so it works as the x offset directly. + + vec2 size = vec2(glyph_size); + vec2 offset = vec2(bearings); + + offset.y = cell_size.y - offset.y; + + // 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 + size * corner + offset; + gl_Position = projection_matrix * vec4(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 does not need to be, since the texture will + // be sampled with pixel coordinate mode. + out_data.tex_coord = vec2(glyph_pos) + vec2(glyph_size) * corner; + + // Get our color. We always fetch a linearized version to + // make it easier to handle minimum contrast calculations. + out_data.color = load_color(color, true); + // Get the BG color + out_data.bg_color = load_color( + unpack4u8(bg_colors[grid_pos.y * grid_size.x + grid_pos.x]), + true + ); + // Blend it with the global bg color + vec4 global_bg = load_color( + unpack4u8(bg_color_packed_4u8), + true + ); + out_data.bg_color += global_bg * vec4(1.0 - out_data.bg_color.a); + + // 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 (min_contrast > 1.0f && (glyph_bools & NO_MIN_CONTRAST) == 0) { + // Ensure our minimum contrast + out_data.color = contrasted_color(min_contrast, out_data.color, out_data.bg_color); + } + + // Check if current position is under cursor (including wide cursor) + bool is_cursor_pos = ((grid_pos.x == cursor_pos.x) || (cursor_wide && (grid_pos.x == (cursor_pos.x + 1)))) && (grid_pos.y == cursor_pos.y); + + // If this cell is the cursor cell, but we're not processing + // the cursor glyph itself, then we need to change the color. + if ((glyph_bools & IS_CURSOR_GLYPH) == 0 && is_cursor_pos) { + out_data.color = load_color(unpack4u8(cursor_color_packed_4u8), use_linear_blending); + } +} diff --git a/vendor/libghostty-vt/src/renderer/shaders/glsl/common.glsl b/vendor/libghostty-vt/src/renderer/shaders/glsl/common.glsl new file mode 100644 index 00000000..1a7ea03a --- /dev/null +++ b/vendor/libghostty-vt/src/renderer/shaders/glsl/common.glsl @@ -0,0 +1,156 @@ +#version 430 core + +// These are common definitions to be shared across shaders, the first +// line of any shader that needs these should be `#include "common.glsl"`. +// +// Included in this file are: +// - The interface block for the global uniforms. +// - Functions for unpacking values. +// - Functions for working with colors. + +//----------------------------------------------------------------------------// +// Global Uniforms +//----------------------------------------------------------------------------// +layout(binding = 1, std140) uniform Globals { + uniform mat4 projection_matrix; + uniform vec2 screen_size; + uniform vec2 cell_size; + uniform uint grid_size_packed_2u16; + uniform vec4 grid_padding; + uniform uint padding_extend; + uniform float min_contrast; + uniform uint cursor_pos_packed_2u16; + uniform uint cursor_color_packed_4u8; + uniform uint bg_color_packed_4u8; + uniform uint bools; +}; + +// Bools +const uint CURSOR_WIDE = 1u; +const uint USE_DISPLAY_P3 = 2u; +const uint USE_LINEAR_BLENDING = 4u; +const uint USE_LINEAR_CORRECTION = 8u; + +// Padding extend enum +const uint EXTEND_LEFT = 1u; +const uint EXTEND_RIGHT = 2u; +const uint EXTEND_UP = 4u; +const uint EXTEND_DOWN = 8u; + +//----------------------------------------------------------------------------// +// Functions for Unpacking Values +//----------------------------------------------------------------------------// +// NOTE: These unpack functions assume little-endian. +// If this ever becomes a problem... oh dear! + +uvec4 unpack4u8(uint packed_value) { + return uvec4( + uint(packed_value >> 0) & uint(0xFF), + uint(packed_value >> 8) & uint(0xFF), + uint(packed_value >> 16) & uint(0xFF), + uint(packed_value >> 24) & uint(0xFF) + ); +} + +uvec2 unpack2u16(uint packed_value) { + return uvec2( + uint(packed_value >> 0) & uint(0xFFFF), + uint(packed_value >> 16) & uint(0xFFFF) + ); +} + +ivec2 unpack2i16(int packed_value) { + return ivec2( + (packed_value << 16) >> 16, + (packed_value << 0) >> 16 + ); +} + +//----------------------------------------------------------------------------// +// Color Functions +//----------------------------------------------------------------------------// + +// Compute the luminance of the provided color. +// +// Takes colors in linear RGB space. If your colors are gamma +// encoded, linearize them before using them with this function. +float luminance(vec3 color) { + return dot(color, vec3(0.2126f, 0.7152f, 0.0722f)); +} + +// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef +// +// Takes colors in linear RGB space. If your colors are gamma +// encoded, linearize them before using them with this function. +float contrast_ratio(vec3 color1, vec3 color2) { + float luminance1 = luminance(color1) + 0.05; + float luminance2 = luminance(color2) + 0.05; + return max(luminance1, luminance2) / min(luminance1, luminance2); +} + +// Return the fg if the contrast ratio is greater than min, otherwise +// return a color that satisfies the contrast ratio. Currently, the color +// is always white or black, whichever has the highest contrast ratio. +// +// Takes colors in linear RGB space. If your colors are gamma +// encoded, linearize them before using them with this function. +vec4 contrasted_color(float min_ratio, vec4 fg, vec4 bg) { + float ratio = contrast_ratio(fg.rgb, bg.rgb); + if (ratio < min_ratio) { + float white_ratio = contrast_ratio(vec3(1.0, 1.0, 1.0), bg.rgb); + float black_ratio = contrast_ratio(vec3(0.0, 0.0, 0.0), bg.rgb); + if (white_ratio > black_ratio) { + return vec4(1.0); + } else { + return vec4(0.0, 0.0, 0.0, 1.0); + } + } + + return fg; +} + +// Converts a color from sRGB gamma encoding to linear. +vec4 linearize(vec4 srgb) { + bvec3 cutoff = lessThanEqual(srgb.rgb, vec3(0.04045)); + vec3 higher = pow((srgb.rgb + vec3(0.055)) / vec3(1.055), vec3(2.4)); + vec3 lower = srgb.rgb / vec3(12.92); + + return vec4(mix(higher, lower, cutoff), srgb.a); +} +float linearize(float v) { + return v <= 0.04045 ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4); +} + +// Converts a color from linear to sRGB gamma encoding. +vec4 unlinearize(vec4 linear) { + bvec3 cutoff = lessThanEqual(linear.rgb, vec3(0.0031308)); + vec3 higher = pow(linear.rgb, vec3(1.0 / 2.4)) * vec3(1.055) - vec3(0.055); + vec3 lower = linear.rgb * vec3(12.92); + + return vec4(mix(higher, lower, cutoff), linear.a); +} +float unlinearize(float v) { + return v <= 0.0031308 ? v * 12.92 : pow(v, 1.0 / 2.4) * 1.055 - 0.055; +} + +// Load a 4 byte RGBA non-premultiplied color and linearize +// and convert it as necessary depending on the provided info. +// +// `linear` controls whether the returned color is linear or gamma encoded. +vec4 load_color( + uvec4 in_color, + bool linear +) { + // 0 .. 255 -> 0.0 .. 1.0 + vec4 color = vec4(in_color) / vec4(255.0f); + + // Linearize if necessary. + if (linear) color = linearize(color); + + // Premultiply our color by its alpha. + color.rgb *= color.a; + + return color; +} + +//----------------------------------------------------------------------------// diff --git a/vendor/libghostty-vt/src/renderer/shaders/glsl/full_screen.v.glsl b/vendor/libghostty-vt/src/renderer/shaders/glsl/full_screen.v.glsl new file mode 100644 index 00000000..b89cedfa --- /dev/null +++ b/vendor/libghostty-vt/src/renderer/shaders/glsl/full_screen.v.glsl @@ -0,0 +1,24 @@ +#version 330 core + +void main() { + vec4 position; + position.x = (gl_VertexID == 2) ? 3.0 : -1.0; + position.y = (gl_VertexID == 0) ? -3.0 : 1.0; + position.z = 1.0; + position.w = 1.0; + + // Single triangle is clipped to viewport. + // + // X <- vid == 0: (-1, -3) + // |\ + // | \ + // | \ + // |###\ + // |#+# \ `+` is (0, 0). `#`s are viewport area. + // |### \ + // X------X <- vid == 2: (3, 1) + // ^ + // vid == 1: (-1, 1) + + gl_Position = position; +} diff --git a/vendor/libghostty-vt/src/renderer/shaders/glsl/image.f.glsl b/vendor/libghostty-vt/src/renderer/shaders/glsl/image.f.glsl new file mode 100644 index 00000000..4f89d7a7 --- /dev/null +++ b/vendor/libghostty-vt/src/renderer/shaders/glsl/image.f.glsl @@ -0,0 +1,21 @@ +#include "common.glsl" + +layout(binding = 0) uniform sampler2D image; + +in vec2 tex_coord; + +layout(location = 0) out vec4 out_FragColor; + +void main() { + bool use_linear_blending = (bools & USE_LINEAR_BLENDING) != 0; + + vec4 rgba = texture(image, tex_coord); + + if (!use_linear_blending) { + rgba = unlinearize(rgba); + } + + rgba.rgb *= vec3(rgba.a); + + out_FragColor = rgba; +} diff --git a/vendor/libghostty-vt/src/renderer/shaders/glsl/image.v.glsl b/vendor/libghostty-vt/src/renderer/shaders/glsl/image.v.glsl new file mode 100644 index 00000000..779fae32 --- /dev/null +++ b/vendor/libghostty-vt/src/renderer/shaders/glsl/image.v.glsl @@ -0,0 +1,47 @@ +#include "common.glsl" + +layout(binding = 0) uniform sampler2D image; + +layout(location = 0) in vec2 grid_pos; +layout(location = 1) in vec2 cell_offset; +layout(location = 2) in vec4 source_rect; +layout(location = 3) in vec2 dest_size; + +out vec2 tex_coord; + +void main() { + int vid = gl_VertexID; + + // We use a triangle strip with 4 vertices to render quads, + // so we determine which corner of the cell this vertex is in + // based on the vertex ID. + // + // 0 --> 1 + // | .'| + // | / | + // | L | + // 2 --> 3 + // + // 0 = top-left (0, 0) + // 1 = top-right (1, 0) + // 2 = bot-left (0, 1) + // 3 = bot-right (1, 1) + vec2 corner; + corner.x = float(vid == 1 || vid == 3); + corner.y = float(vid == 2 || vid == 3); + + // The texture coordinates start at our source x/y + // and add the width/height depending on the corner. + tex_coord = source_rect.xy; + tex_coord += source_rect.zw * corner; + + // Normalize the coordinates. + tex_coord /= textureSize(image, 0); + + // The position of our image starts at the top-left of the grid cell and + // adds the source rect width/height components. + vec2 image_pos = (cell_size * grid_pos) + cell_offset; + image_pos += dest_size * corner; + + gl_Position = projection_matrix * vec4(image_pos.xy, 1.0, 1.0); +} |
