diff --git a/config/src/background.rs b/config/src/background.rs index 8f66d1978..338d952be 100644 --- a/config/src/background.rs +++ b/config/src/background.rs @@ -31,6 +31,11 @@ impl Default for BlendMode { pub enum GradientOrientation { Horizontal, Vertical, + Radial { + radius: Option, + cx: Option, + cy: Option, + }, } impl Default for GradientOrientation { diff --git a/docs/config/appearance.md b/docs/config/appearance.md index 11cd76ec4..4337d4f7c 100644 --- a/docs/config/appearance.md +++ b/docs/config/appearance.md @@ -327,6 +327,15 @@ return { See [Styling Inactive Panes](#style-inactive-panes) for more information on hue, saturation, brigthness transformations. +## Window Background Gradient + +*Since: nightly builds only* + + + +See [window_background_gradient](lua/config/window_background_gradient.md) +for configuration information on gradients. + ## Window Background Opacity *since: 20201031-154415-9614e117* diff --git a/docs/config/lua/config/window_background_gradient.md b/docs/config/lua/config/window_background_gradient.md index 731505c64..dbd74ec16 100644 --- a/docs/config/lua/config/window_background_gradient.md +++ b/docs/config/lua/config/window_background_gradient.md @@ -13,7 +13,8 @@ return { window_background_gradient = { -- Can be "Vertical" or "Horizontal". Specifies the direction -- in which the color gradient varies. The default is "Horizontal", - -- with the gradient going from left-to-right + -- with the gradient going from left-to-right. + -- Radial gradients are also supported; see the other example below orientation = "Vertical", -- Specifies the set of colors that are interpolated in the gradient. @@ -56,7 +57,44 @@ return { } ``` + + Gradients are implemented using the `colorgrad` crate. Take a look at for some usage examples and additional information about gradients. +## Radial gradient: + +Radial gradients are implemented using a notional perfect circle that is +subsequently stretched to fill the dimensions of the window. + +```lua +return { + color_scheme = "Github", + window_background_gradient = { + colors = {"deeppink", "gold"}, + orientation = { + Radial={ + -- Specifies the x coordinate of the center of the circle, + -- in the range 0.0 through 1.0. The default is 0.5 which + -- is centered in the X dimension. + cx = 0.75, + + -- Specifies the y coordinate of the center of the circle, + -- in the range 0.0 through 1.0. The default is 0.5 which + -- is centered in the Y dimension. + cy = 0.75, + + -- Specifies the radius of the notional circle. + -- The default is 0.5, which combined with the default cx + -- and cy values places the circle in the center of the + -- window, with the edges touching the window edges. + -- Values larger than 1 are possible. + radius = 1.25, + } + }, + } +} +``` + + diff --git a/docs/screenshots/radial-gradient.png b/docs/screenshots/radial-gradient.png new file mode 100644 index 000000000..24d236ec8 Binary files /dev/null and b/docs/screenshots/radial-gradient.png differ diff --git a/docs/screenshots/vertical-gradient.png b/docs/screenshots/vertical-gradient.png new file mode 100644 index 000000000..b503973c2 Binary files /dev/null and b/docs/screenshots/vertical-gradient.png differ diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 01bba413b..824a5bc2f 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -333,11 +333,20 @@ fn load_background_image(config: &ConfigHandle, dimensions: &Dimensions) -> Opti match &config.window_background_gradient { Some(g) => match g.build() { Ok(grad) => { - let width = dimensions.pixel_width as u32; - let height = dimensions.pixel_height as u32; + let mut width = dimensions.pixel_width as u32; + let mut height = dimensions.pixel_height as u32; + + if matches!(g.orientation, GradientOrientation::Radial { .. }) { + // To simplify the math, we compute a perfect circle + // for the radial gradient, and let the texture sampler + // perturb it to fill the window + width = width.min(height); + height = height.min(width); + } + let mut imgbuf = image::RgbaImage::new(width, height); - let fw = dimensions.pixel_width as f64; - let fh = dimensions.pixel_height as f64; + let fw = width as f64; + let fh = height as f64; fn to_pixel(c: colorgrad::Color) -> image::Rgba { let (r, g, b, a) = c.rgba_u8(); @@ -358,7 +367,14 @@ fn load_background_image(config: &ConfigHandle, dimensions: &Dimensions) -> Opti // visible color banding. The default 64 was selected // because it it was the smallest value on my mac where // the banding wasn't obvious. - let noise_amount = g.noise.unwrap_or(64); + let noise_amount = g.noise.unwrap_or_else(|| { + if matches!(g.orientation, GradientOrientation::Radial { .. }) { + 16 + } else { + 64 + } + }); + fn noise(rng: &fastrand::Rng, noise_amount: usize) -> f64 { if noise_amount == 0 { 0. @@ -390,6 +406,21 @@ fn load_background_image(config: &ConfigHandle, dimensions: &Dimensions) -> Opti ))); } } + GradientOrientation::Radial { radius, cx, cy } => { + let radius = fw * radius.unwrap_or(0.5); + let cx = fw * cx.unwrap_or(0.5); + let cy = fh * cy.unwrap_or(0.5); + + for (x, y, pixel) in imgbuf.enumerate_pixels_mut() { + let nx = noise(&rng, noise_amount); + let ny = noise(&rng, noise_amount); + + let t = (nx + (x as f64 - cx).powi(2) + (ny + y as f64 - cy).powi(2)) + .sqrt() + / radius; + *pixel = to_pixel(grad.at(t)); + } + } } let data = imgbuf.into_vec();