undercurl
bendn 7 weeks ago
parent 0bcb86a · commit 66f2377
-rw-r--r--Cargo.lock28
-rw-r--r--src/cell.rs1
-rw-r--r--src/lib.rs125
3 files changed, 112 insertions, 42 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7c45f47..7764872 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -122,8 +122,8 @@ checksum = "6a02dba6a60cd31533cf16561ced53239686d18f1464bff49579dd320fcea081"
[[package]]
name = "fimg"
-version = "0.4.50"
-source = "git+https://github.com/bend-n/fimg#4ef37597ee44dff03d6fc55393c4e97baa080196"
+version = "0.4.51"
+source = "git+https://github.com/bend-n/fimg#e72d81b7d072e6ad53dc4b9eeefcac04f5cbdd18"
dependencies = [
"array_chunks",
"atools 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -188,9 +188,9 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.177"
+version = "0.2.179"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
+checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f"
[[package]]
name = "linked-hash-map"
@@ -209,9 +209,9 @@ dependencies = [
[[package]]
name = "lower-macros"
-version = "0.2.10"
+version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2996a18b899178abb5e49750d670a908ffb67b13f15b95e4fc5c40a1dffbd76"
+checksum = "a1685e573ae55817b3a8169edb1a9541b4f9e2ce166d7248e813078d2665b0f2"
dependencies = [
"proc-macro2",
"quote",
@@ -258,18 +258,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.103"
+version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
+checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.42"
+version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
+checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
dependencies = [
"proc-macro2",
]
@@ -296,9 +296,9 @@ dependencies = [
[[package]]
name = "simd-adler32"
-version = "0.3.7"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
[[package]]
name = "skrifa"
@@ -329,9 +329,9 @@ checksum = "9cd32a3a5640a1e8ba59937591c61be77cae746e7c0fa081b24deebb4f7ece4c"
[[package]]
name = "syn"
-version = "2.0.111"
+version = "2.0.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
+checksum = "678faa00651c9eb72dd2020cbdf275d92eccb2400d568e419efdd64838145cb4"
dependencies = [
"proc-macro2",
"quote",
diff --git a/src/cell.rs b/src/cell.rs
index e532b84..27f4d58 100644
--- a/src/cell.rs
+++ b/src/cell.rs
@@ -63,6 +63,7 @@ impl Style {
pub const ITALIC: u8 = 1 << 2;
pub const UNDERLINE: u8 = 1 << 3;
pub const STRIKETHROUGH: u8 = 1 << 4;
+ pub const UNDERCURL: u8 = 1 << 5;
}
#[derive(Clone, Copy, Default, PartialEq, Eq)]
pub struct Cell {
diff --git a/src/lib.rs b/src/lib.rs
index 4532930..fc0ceb5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -106,7 +106,7 @@ pub unsafe fn render_owned(
subpixel: bool,
) -> Image<Box<[u8]>, 3> {
let (w, h) = size(&fonts.regular, ppem, line_spacing, (c, r));
- let mut i = Image::build(w as _, h as _).fill([255; 3]);
+ let mut i = Image::build(w as u32, h as u32).fill([255; 3]);
render(
cells,
(c, r),
@@ -281,6 +281,33 @@ pub unsafe fn render(
)
};
}
+ if (cell.style.flags & Style::UNDERCURL) != 0 {
+ let mut buffer =
+ Image::build(fw.ceil() as u32, fh.ceil() as u32)
+ .fill([0u8]);
+
+ undercurl(
+ fw.ceil() as u32,
+ fh.ceil() as u32,
+ 1 + ((met.ascent - met.underline_offset) * fac) as u32,
+ 2,
+ |x, y, v| {
+ let [p] = buffer.pixel_mut(x, y);
+ *p = p.saturating_add(v);
+ },
+ );
+ unsafe {
+ i.blend_alpha_and_color_at(
+ &buffer.as_ref(),
+ color,
+ (j as f32 * fw).floor() as u32 // _
+ + offset_x,
+ (k as f32 * (fh + line_spacing * fac)).floor()
+ as u32
+ + offset_y,
+ );
+ }
+ }
// if (cell.style.flags & Style::STRIKETHROUGH) != 0 {
// unsafe {
// i.as_mut().overlay_at(
@@ -386,6 +413,61 @@ pub unsafe fn render(
// }
}
+// https://github.com/kovidgoyal/kitty/blob/df17142ea4fdaa31a54015b7baab6a451a593433/kitty/decorations.c#L147
+fn undercurl(
+ fw: u32,
+ fh: u32,
+ position: u32,
+ thickness: u32,
+ mut f: impl FnMut(u32, u32, u8),
+) {
+ let max_x = fw - 1;
+ let max_y = fh - 1;
+ let xfactor = /* | 2 */ 4.0 * std::f32::consts::PI / max_x as f32;
+
+ let mut position = position.min(
+ fh.saturating_sub(thickness / 2 + thickness % 2 /*q+rem*/),
+ );
+ let thickness =
+ 1u32.max(thickness.min(fh.saturating_sub(position + 1)));
+
+ let max_height = fh - position.saturating_sub(thickness / 2);
+ let half_height = 1.max(max_height / 4);
+
+ let thickness = if false {
+ half_height.max(thickness)
+ } else {
+ 1.max(thickness) - if thickness < 3 { 1 } else { 2 }
+ };
+
+ position += half_height * 2;
+ if position + half_height > max_y {
+ position = max_y - half_height;
+ };
+
+ // let mut miny = fh;
+ // let mut maxy = 0u32;
+ let mut spx = |x, y: i32, val| {
+ let y = ((y + position as i32).max(0) as u32).min(max_y);
+ f(x, y, val);
+ y
+ };
+ for x in 0..fw {
+ let y = half_height as f32 * ((x as f32) * xfactor).cos();
+ let y1 = (y.floor() - thickness as f32) as i32;
+ let y2 = y.ceil() as i32;
+ let i2 = (255.0 * (y - y.floor()).abs()) as u32;
+ let i1 = 255 - i2 as u8;
+ let _yc = spx(x, y1, i1);
+ // if i1 != 0 { miny = miny.min(yc); maxy = maxy.max(yc); }
+ let _yc = spx(x, y2, i2 as u8);
+ // if i2 != 0 { miny = miny.min(yc); maxy = maxy.max(yc); }
+ for t in 1..=thickness {
+ spx(x, y1 + t as i32, 255);
+ }
+ }
+}
+
fn blend(m: [u8; 3], c: [u8; 3], to: &mut [u8; 3]) {
*to = [
((c[0] as u16 * m[2] as u16 + (255 - m[2] as u16) * to[0] as u16)
@@ -466,34 +548,21 @@ pub unsafe fn fill_in(
}
let from = y1 * iw + x1;
let p = image.buffer_mut().as_mut_ptr();
- match w {
- 12 => {
- let n = w as usize * 3;
- let from = p.add(from as usize * 3);
-
- for y in y1 + 1..(y1 + h).min(image.height()) {
- core::ptr::copy(from, p.add(((y * iw + x1) * 3) as _), n);
- // image.buffer_mut().copy_within(from.clone(), ((y * iw + x1)*3) as _);
- }
- }
- 13 => {
+ macro_rules! d_ {
+ () => {{
let n = w as usize * 3;
let from = p.add(from as usize * 3);
for y in y1 + 1..(y1 + h).min(image.height()) {
- core::ptr::copy(from, p.add(((y * iw + x1) * 3) as _), n);
+ core::ptr::copy_nonoverlapping(from, p.add(((y * iw + x1) * 3) as _), n);
// image.buffer_mut().copy_within(from.clone(), ((y * iw + x1)*3) as _);
}
- }
- _ => {
- let n = w as usize * 3;
- let from = p.add(from as usize * 3);
-
- for y in y1 + 1..(y1 + h).min(image.height()) {
- core::ptr::copy(from, p.add(((y * iw + x1) * 3) as _), n);
- // image.buffer_mut().copy_within(from.clone(), ((y * iw + x1)*3) as _);
- }
- }
+ }};
+ }
+ match w {
+ 13 => d_!(),
+ 12 => d_!(),
+ _ => d_!(),
}
}
@@ -522,7 +591,7 @@ fn x() {
style: Style {
bg: [31, 36, 48],
color: [255, 255, 255],
- flags: Style::UNDERLINE,
+ flags: Style::UNDERCURL,
},
letter: Some('['),
},
@@ -530,7 +599,7 @@ fn x() {
style: Style {
bg: [31, 36, 48],
color: [255, 173, 102],
- flags: Style::UNDERLINE,
+ flags: Style::UNDERCURL,
},
letter: Some('='),
},
@@ -538,7 +607,7 @@ fn x() {
style: Style {
bg: [31, 36, 48],
color: [204, 202, 194],
- flags: Style::UNDERLINE,
+ flags: Style::UNDERCURL,
},
letter: Some('s'),
},
@@ -546,7 +615,7 @@ fn x() {
style: Style {
bg: [31, 36, 48],
color: [255, 173, 102],
- flags: Style::UNDERLINE,
+ flags: Style::UNDERCURL,
},
letter: Some('>'),
},
@@ -554,7 +623,7 @@ fn x() {
style: Style {
bg: [31, 36, 48],
color: [255, 255, 255],
- flags: 5,
+ flags: Style::UNDERCURL,
},
letter: Some(']'),
},