mirror of
https://github.com/wez/wezterm.git
synced 2024-11-23 23:21:08 +03:00
window+gui: enable dual source blending
This replaces the slightly gnarly subpixel enabled blending in the shader with Dual Source Blending, which is a technique where the fragment shader can specify both the primary color (RGBA) as well as an additional per-channel mask that can be used to alpha blend with the destination. This enables artifact-free alpha blending when used together with a transparent window background. refs: https://github.com/wez/wezterm/issues/932
This commit is contained in:
parent
1d94fe5fda
commit
ee71d478c4
18
Cargo.lock
generated
18
Cargo.lock
generated
@ -922,7 +922,7 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"memoffset 0.6.4",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
@ -1759,15 +1759,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "glium"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7eeec7b733d14519e2541f4cc8a1230de9143d4ec439dd51b6c048d8ec991759"
|
||||
version = "0.30.1"
|
||||
source = "git+https://github.com/glium/glium.git?rev=aed95270f0714036003589d6e52de196e7ff75d1#aed95270f0714036003589d6e52de196e7ff75d1"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"fnv",
|
||||
"gl_generator",
|
||||
"lazy_static",
|
||||
"memoffset 0.5.6",
|
||||
"memoffset",
|
||||
"smallvec 1.6.1",
|
||||
"takeable-option",
|
||||
]
|
||||
@ -2371,15 +2370,6 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.4"
|
||||
|
@ -1,6 +1,6 @@
|
||||
// This is the Glyph fragment shader.
|
||||
// It is responsible for laying down the glyph graphics on top of the other layers.
|
||||
|
||||
#extension GL_EXT_blend_func_extended: enable
|
||||
precision highp float;
|
||||
|
||||
in float o_has_color;
|
||||
@ -9,29 +9,16 @@ in vec3 o_hsv;
|
||||
in vec4 o_fg_color;
|
||||
in vec4 o_bg_color;
|
||||
|
||||
out vec4 color;
|
||||
// The color + alpha
|
||||
layout(location=0, index=0) out vec4 color;
|
||||
// Individual alpha channels for RGBA in color, used for subpixel
|
||||
// antialiasing blending
|
||||
layout(location=0, index=1) out vec4 colorMask;
|
||||
|
||||
uniform vec3 foreground_text_hsb;
|
||||
uniform sampler2D atlas_nearest_sampler;
|
||||
uniform sampler2D atlas_linear_sampler;
|
||||
|
||||
|
||||
float multiply_one(float src, float dst, float inv_dst_alpha, float inv_src_alpha) {
|
||||
return (src * dst) + (src * (inv_dst_alpha)) + (dst * (inv_src_alpha));
|
||||
}
|
||||
|
||||
// Alpha-regulated multiply to colorize the glyph bitmap.
|
||||
vec4 multiply(vec4 src, vec4 dst) {
|
||||
float inv_src_alpha = 1.0 - src.a;
|
||||
float inv_dst_alpha = 1.0 - dst.a;
|
||||
|
||||
return vec4(
|
||||
multiply_one(src.r, dst.r, inv_dst_alpha, inv_src_alpha),
|
||||
multiply_one(src.g, dst.g, inv_dst_alpha, inv_src_alpha),
|
||||
multiply_one(src.b, dst.b, inv_dst_alpha, inv_src_alpha),
|
||||
dst.a);
|
||||
}
|
||||
|
||||
vec3 rgb2hsv(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||
@ -61,41 +48,6 @@ vec4 apply_hsv(vec4 c, vec3 transform)
|
||||
return vec4(hsv2rgb(hsv).rgb, c.a);
|
||||
}
|
||||
|
||||
// Given glyph, the greyscale rgba value computed by freetype,
|
||||
// and color, the desired color, compute the resultant pixel
|
||||
// value for rendering over the top of the given background
|
||||
// color.
|
||||
//
|
||||
// The freetype glyph is greyscale (R=G=B=A) when font_antialias=Greyscale,
|
||||
// where each channel holds the brightness of the pixel.
|
||||
// It holds separate intensity values for the R, G and B channels when
|
||||
// subpixel anti-aliasing is in use, with an approximated A value
|
||||
// derived from the R, G, B values.
|
||||
//
|
||||
// In sub-pixel mode we don't want to look at glyph.a as we effective
|
||||
// have per-channel alpha. In greyscale mode, glyph.a is the same
|
||||
// as the other channels, so this routine ignores glyph.a when
|
||||
// computing the blend, but does include that value for the returned
|
||||
// alpha value.
|
||||
//
|
||||
// See also: https://www.puredevsoftware.com/blog/2019/01/22/sub-pixel-gamma-correct-font-rendering/
|
||||
vec4 colorize(vec4 glyph, vec4 color, vec4 background) {
|
||||
float r = glyph.r * color.r + (1.0 - glyph.r) * background.r;
|
||||
float g = glyph.g * color.g + (1.0 - glyph.g) * background.g;
|
||||
float b = glyph.b * color.b + (1.0 - glyph.b) * background.b;
|
||||
|
||||
return vec4(r, g, b, glyph.a);
|
||||
}
|
||||
|
||||
// Like colorize, but for when the background color is unknown.
|
||||
// This isn't "good" because it generally results in dark fringes.
|
||||
// Ideally we wouldn't need to know the background and we could
|
||||
// use dual source blending instead of colorize and colorize2.
|
||||
// <https://github.com/wez/wezterm/issues/932>
|
||||
vec4 colorize2(vec4 glyph, vec4 color) {
|
||||
return vec4(glyph.rgb * color.rgb, glyph.a);
|
||||
}
|
||||
|
||||
vec4 from_linear(vec4 v) {
|
||||
return pow(v, vec4(2.2));
|
||||
}
|
||||
@ -120,18 +72,22 @@ void main() {
|
||||
if (o_has_color == 3.0) {
|
||||
// Solid color block
|
||||
color = o_fg_color;
|
||||
colorMask = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
} else if (o_has_color == 2.0) {
|
||||
// The window background attachment
|
||||
color = sample_texture(atlas_linear_sampler, o_tex);
|
||||
// Apply window_background_image_opacity to the background image
|
||||
color.a = o_fg_color.a;
|
||||
colorMask = o_fg_color.aaaa;
|
||||
} else {
|
||||
color = sample_texture(atlas_nearest_sampler, o_tex);
|
||||
if (o_has_color == 0.0) {
|
||||
// if it's not a color emoji it will be grayscale
|
||||
// and we need to tint with the fg_color
|
||||
color = colorize(color, o_fg_color, o_bg_color);
|
||||
colorMask = color;
|
||||
color = o_fg_color;
|
||||
color = apply_hsv(color, foreground_text_hsb);
|
||||
} else {
|
||||
colorMask = color.aaaa;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,7 +152,7 @@ impl RenderState {
|
||||
fragment_shader: fn(&str) -> (String, String),
|
||||
) -> anyhow::Result<glium::Program> {
|
||||
let mut errors = vec![];
|
||||
for version in &["330", "300 es"] {
|
||||
for version in &["330 core", "330", "320 es", "300 es"] {
|
||||
let (vertex_shader, fragment_shader) = fragment_shader(version);
|
||||
let source = glium::program::ProgramCreationInput::SourceCode {
|
||||
vertex_shader: &vertex_shader,
|
||||
@ -164,12 +164,11 @@ impl RenderState {
|
||||
uses_point_size: false,
|
||||
geometry_shader: None,
|
||||
};
|
||||
log::trace!("compiling a prog with version {}", version);
|
||||
match glium::Program::new(context, source) {
|
||||
Ok(prog) => {
|
||||
return Ok(prog);
|
||||
}
|
||||
Err(err) => errors.push(err.to_string()),
|
||||
Err(err) => errors.push(format!("shader version: {}: {:#}", version, err)),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -467,12 +467,12 @@ impl super::TermWindow {
|
||||
let alpha_blending = glium::DrawParameters {
|
||||
blend: glium::Blend {
|
||||
color: BlendingFunction::Addition {
|
||||
source: LinearBlendingFactor::SourceAlpha,
|
||||
destination: LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
source: LinearBlendingFactor::SourceOneColor,
|
||||
destination: LinearBlendingFactor::OneMinusSourceOneColor,
|
||||
},
|
||||
alpha: BlendingFunction::Addition {
|
||||
source: LinearBlendingFactor::One,
|
||||
destination: LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
source: LinearBlendingFactor::SourceOneColor,
|
||||
destination: LinearBlendingFactor::OneMinusSourceOneColor,
|
||||
},
|
||||
constant_value: (0.0, 0.0, 0.0, 0.0),
|
||||
},
|
||||
|
@ -42,7 +42,7 @@ raw-window-handle = "0.3"
|
||||
resize = "0.5"
|
||||
serde = {version="1.0", features = ["rc", "derive"]}
|
||||
tiny-skia = "0.5"
|
||||
glium = { version = "0.28", default-features = false}
|
||||
glium = { version = "0.30", default-features = false, git = "https://github.com/glium/glium.git", rev="aed95270f0714036003589d6e52de196e7ff75d1" }
|
||||
wezterm-font = { path = "../wezterm-font" }
|
||||
wezterm-input-types = { path = "../wezterm-input-types" }
|
||||
|
||||
|
@ -54,6 +54,7 @@ impl std::fmt::Debug for EglWrapper {
|
||||
pub struct GlConnection {
|
||||
egl: EglWrapper,
|
||||
display: ffi::types::EGLDisplay,
|
||||
is_opengl: bool,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for GlConnection {
|
||||
@ -490,9 +491,22 @@ impl GlState {
|
||||
let (major, minor) = egl.initialize_and_get_version(egl_display)?;
|
||||
log::trace!("initialized EGL version {}.{}", major, minor);
|
||||
|
||||
let is_opengl = unsafe {
|
||||
if egl.egl.BindAPI(ffi::OPENGL_API) != 0 {
|
||||
log::trace!("using OpenGL");
|
||||
true
|
||||
} else if egl.egl.BindAPI(ffi::OPENGL_ES_API) != 0 {
|
||||
log::trace!("using GLES");
|
||||
false
|
||||
} else {
|
||||
anyhow::bail!("Unable to bind to OpenGL or GL ES!?");
|
||||
}
|
||||
};
|
||||
|
||||
let connection = Rc::new(GlConnection {
|
||||
display: egl_display,
|
||||
egl,
|
||||
is_opengl,
|
||||
});
|
||||
|
||||
Self::create_with_existing_connection(&connection, window)
|
||||
@ -539,9 +553,17 @@ impl GlState {
|
||||
ffi::DEPTH_SIZE,
|
||||
24,
|
||||
ffi::CONFORMANT,
|
||||
ffi::OPENGL_ES3_BIT,
|
||||
if connection.is_opengl {
|
||||
ffi::OPENGL_BIT
|
||||
} else {
|
||||
ffi::OPENGL_ES3_BIT
|
||||
},
|
||||
ffi::RENDERABLE_TYPE,
|
||||
ffi::OPENGL_ES3_BIT,
|
||||
if connection.is_opengl {
|
||||
ffi::OPENGL_BIT
|
||||
} else {
|
||||
ffi::OPENGL_ES3_BIT
|
||||
},
|
||||
// Wayland EGL doesn't give us a working context if we request
|
||||
// PBUFFER|PIXMAP. We don't appear to require these for X11,
|
||||
// so we're just asking for a WINDOW capable context
|
||||
|
Loading…
Reference in New Issue
Block a user