undercurl
| -rw-r--r-- | Cargo.lock | 28 | ||||
| -rw-r--r-- | src/cell.rs | 1 | ||||
| -rw-r--r-- | src/lib.rs | 125 |
3 files changed, 112 insertions, 42 deletions
@@ -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 { @@ -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(']'), }, |