1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-23 15:04:36 +03:00

zeno -> tiny_skia

Mostly so that we can turn off anti-aliasing for smaller sizes,
but it also happens to be more complete anyway.
This commit is contained in:
Wez Furlong 2021-06-25 22:55:42 -07:00
parent fad09eddda
commit 4c0d789ac3
4 changed files with 109 additions and 66 deletions

37
Cargo.lock generated
View File

@ -120,6 +120,12 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "arrayref"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.5.2" version = "0.5.2"
@ -3654,6 +3660,15 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "safe_arch"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -4301,6 +4316,20 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "tiny-skia"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf81f2900d2e235220e6f31ec9f63ade6a7f59090c556d74fe949bb3b15e9fe"
dependencies = [
"arrayref",
"arrayvec",
"bytemuck",
"cfg-if 1.0.0",
"png",
"safe_arch",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.2.0" version = "1.2.0"
@ -4915,6 +4944,7 @@ dependencies = [
"termwiz", "termwiz",
"textwrap 0.14.0", "textwrap 0.14.0",
"thiserror", "thiserror",
"tiny-skia",
"umask", "umask",
"unicode-normalization", "unicode-normalization",
"unicode-segmentation", "unicode-segmentation",
@ -4931,7 +4961,6 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
"window", "window",
"windows", "windows",
"zeno",
] ]
[[package]] [[package]]
@ -5421,12 +5450,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "zeno"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea708573d4a67f939793b0d3f923d105bfdf06bbd6acb3f9cd02ce1bd905f6c8"
[[package]] [[package]]
name = "zstd" name = "zstd"
version = "0.6.1+zstd.1.4.9" version = "0.6.1+zstd.1.4.9"

View File

@ -57,6 +57,7 @@ terminfo = "0.7"
termwiz = { path = "../termwiz" } termwiz = { path = "../termwiz" }
textwrap = "0.14" textwrap = "0.14"
thiserror = "1.0" thiserror = "1.0"
tiny-skia = "0.5"
umask = { path = "../umask" } umask = { path = "../umask" }
unicode-normalization = "0.1" unicode-normalization = "0.1"
unicode-segmentation = "1.7" unicode-segmentation = "1.7"
@ -71,7 +72,6 @@ wezterm-ssh = { path = "../wezterm-ssh" }
wezterm-term = { path = "../term", features=["use_serde"] } wezterm-term = { path = "../term", features=["use_serde"] }
wezterm-toast-notification = { path = "../wezterm-toast-notification" } wezterm-toast-notification = { path = "../wezterm-toast-notification" }
window = { path = "../window" } window = { path = "../window" }
zeno = "0.2"
[target."cfg(windows)".dependencies] [target."cfg(windows)".dependencies]
shared_library = "0.1" shared_library = "0.1"

View File

@ -18,10 +18,10 @@ use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use termwiz::image::ImageData; use termwiz::image::ImageData;
use tiny_skia::{FillRule, Paint, Path, PathBuilder, PixmapMut, Stroke, Transform};
use wezterm_font::units::*; use wezterm_font::units::*;
use wezterm_font::{FontConfiguration, GlyphInfo}; use wezterm_font::{FontConfiguration, GlyphInfo};
use wezterm_term::Underline; use wezterm_term::Underline;
use zeno::{Command, Fill, Format, Join, Mask, PathBuilder, Stroke, Style, Vector};
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GlyphKey { pub struct GlyphKey {
@ -262,27 +262,26 @@ pub enum PolyCommand {
} }
impl PolyCommand { impl PolyCommand {
fn to_zeno( fn to_skia(&self, width: usize, height: usize, underline_height: f32, pb: &mut PathBuilder) {
&self, match self {
width: usize, Self::MoveTo(x, y) => pb.move_to(
height: usize,
underline_height: f32,
sink: &mut impl PathBuilder,
) {
let coord = |x: &BlockCoord, y: &BlockCoord| {
Vector::new(
x.to_pixel(width, underline_height), x.to_pixel(width, underline_height),
y.to_pixel(height, underline_height), y.to_pixel(height, underline_height),
) ),
}; Self::LineTo(x, y) => pb.line_to(
x.to_pixel(width, underline_height),
let point = |(x, y): &BlockPoint| coord(x, y); y.to_pixel(height, underline_height),
),
match self { Self::QuadTo {
Self::MoveTo(x, y) => sink.move_to(coord(x, y)), control: (x1, y1),
Self::LineTo(x, y) => sink.line_to(coord(x, y)), to: (x, y),
Self::QuadTo { control, to } => sink.quad_to(point(control), point(to)), } => pb.quad_to(
Self::Close => sink.close(), x1.to_pixel(width, underline_height),
y1.to_pixel(height, underline_height),
x.to_pixel(width, underline_height),
y.to_pixel(height, underline_height),
),
Self::Close => pb.close(),
}; };
} }
} }
@ -297,11 +296,20 @@ pub enum PolyStyle {
} }
impl PolyStyle { impl PolyStyle {
fn to_zeno(self, width: f32) -> Style<'static> { fn apply(self, width: f32, paint: &Paint, path: &Path, pixmap: &mut PixmapMut) {
match self { match self {
Self::Fill => Style::default(), PolyStyle::Fill => {
Self::Outline => Style::Stroke(*Stroke::new(width).join(Join::Miter)), pixmap.fill_path(path, paint, FillRule::Winding, Transform::identity(), None);
Self::OutlineHeavy => Style::Stroke(*Stroke::new(2. * width).join(Join::Miter)), }
PolyStyle::Outline | PolyStyle::OutlineHeavy => {
let mut stroke = Stroke::default();
stroke.width = width;
if self == PolyStyle::OutlineHeavy {
stroke.width *= 2.0;
}
pixmap.stroke_path(path, paint, &stroke, Transform::identity(), None);
}
} }
} }
} }
@ -2611,23 +2619,28 @@ impl<T: Texture2d> GlyphCache<T> {
// Fill a rectangular region described by the x and y ranges // Fill a rectangular region described by the x and y ranges
let fill_rect = |buffer: &mut Image, x: Range<usize>, y: Range<usize>| { let fill_rect = |buffer: &mut Image, x: Range<usize>, y: Range<usize>| {
let (width, height) = buffer.image_dimensions(); let (width, height) = buffer.image_dimensions();
let mut pixmap =
PixmapMut::from_bytes(buffer.pixel_data_slice_mut(), width as u32, height as u32)
.expect("make pixmap from existing bitmap");
let x = x.start as f32..x.end as f32; let x = x.start as f32..x.end as f32;
let y = y.start as f32..y.end as f32; let y = y.start as f32..y.end as f32;
let mut path: Vec<Command> = vec![];
path.add_rect([x.start, y.start], x.end - x.start, y.end - y.start); let path = PathBuilder::from_rect(
let (alpha, _placement) = Mask::new(&path) tiny_skia::Rect::from_xywh(x.start, y.start, x.end - x.start, y.end - y.start)
.format(Format::Alpha) .expect("valid rect"),
.size(width as u32, height as u32) );
.style(Fill::NonZero)
.render();
for (alpha, dest) in alpha.into_iter().zip(buffer.pixels_mut()) { let mut paint = Paint::default();
let alpha = alpha as u32; paint.set_color(tiny_skia::Color::WHITE);
// If existing pixel was blank, we want to replace it.
// If alpha is blank then we don't want to replace existing non-blank. pixmap.fill_path(
*dest |= alpha << 24 | alpha << 16 | alpha << 8 | alpha; &path,
} &paint,
FillRule::Winding,
Transform::identity(),
None,
);
}; };
match block { match block {
@ -2716,35 +2729,34 @@ impl<T: Texture2d> GlyphCache<T> {
} }
BlockKey::Poly(polys) => { BlockKey::Poly(polys) => {
let (width, height) = buffer.image_dimensions(); let (width, height) = buffer.image_dimensions();
let mut pixmap = PixmapMut::from_bytes(
buffer.pixel_data_slice_mut(),
width as u32,
height as u32,
)
.expect("make pixmap from existing bitmap");
for Poly { for Poly {
path, path,
intensity, intensity,
style, style,
} in polys } in polys
{ {
let intensity = intensity.to_scale(); let intensity = (intensity.to_scale() * 255.) as u8;
let mut cmd = vec![]; let mut paint = Paint::default();
paint.set_color_rgba8(intensity, intensity, intensity, intensity);
paint.anti_alias = false; // explicitly do not want AA for small sizes
let mut pb = PathBuilder::new();
for item in path.iter() { for item in path.iter() {
item.to_zeno( item.to_skia(width, height, self.metrics.underline_height as f32, &mut pb);
width,
height,
self.metrics.underline_height as f32,
&mut cmd,
);
}
let (alpha, _placement) = Mask::new(&cmd)
.format(Format::Alpha)
.style(style.to_zeno(self.metrics.underline_height as f32))
.size(width as u32, height as u32)
.render();
for (alpha, dest) in alpha.into_iter().zip(buffer.pixels_mut()) {
let alpha = (intensity * (alpha as f32)) as u32;
// If existing pixel was blank, we want to replace it.
// If alpha is blank then we don't want to replace existing non-blank.
*dest |= alpha << 24 | alpha << 16 | alpha << 8 | alpha;
} }
let path = pb.finish().expect("poly path to be valid");
style.apply(
self.metrics.underline_height as f32,
&paint,
&path,
&mut pixmap,
);
} }
} }
} }

View File

@ -95,6 +95,14 @@ pub trait BitmapImage {
/// Return the pair (width, height) of the image, measured in pixels /// Return the pair (width, height) of the image, measured in pixels
fn image_dimensions(&self) -> (usize, usize); fn image_dimensions(&self) -> (usize, usize);
fn pixel_data_slice_mut(&mut self) -> &mut [u8] {
let (width, height) = self.image_dimensions();
unsafe {
let first = self.pixel_data_mut();
std::slice::from_raw_parts_mut(first, width * height * 4)
}
}
#[inline] #[inline]
fn pixels(&self) -> &[u32] { fn pixels(&self) -> &[u32] {
let (width, height) = self.image_dimensions(); let (width, height) = self.image_dimensions();