hmm
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Cargo.lock | 399 | ||||
| -rw-r--r-- | Cargo.toml | 10 | ||||
| -rw-r--r-- | src/cell.rs | 34 | ||||
| -rw-r--r-- | src/lib.rs | 203 |
5 files changed, 647 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..02b5076 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,399 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "atools" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b30259e2e64cabf195f13dc5fd322b3adfef150546a2a216cf2272746bf8d5" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytemuck" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "clipline" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c033212f55b799c43650c2fb12866ba8fe873e5786e7e649810c4dc9a76561" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "dsb" +version = "0.1.0" +dependencies = [ + "atools", + "fimg", + "implicit-fn", + "swash", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fer" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02dba6a60cd31533cf16561ced53239686d18f1464bff49579dd320fcea081" + +[[package]] +name = "fimg" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6739c38489a0264e04537f68b822f4cb7abb732b41eca5229a91685fcc998939" +dependencies = [ + "atools", + "clipline", + "fer", + "hinted", + "libc", + "lower", + "mattr", + "png", + "qwant", + "umath", + "vecto", + "windows", +] + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "font-types" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02a596f5713680923a2080d86de50fe472fb290693cf0f701187a1c8b36996b7" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "hinted" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c488b6122f67ca2749a801d562c8c952e1778c42910c43ef537a6f5a46b524f2" + +[[package]] +name = "implicit-fn" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "837f3904739ddc022e28e667c09a2118bd7480c79a771e90f79d56d1db7b36cd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "lower" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913cca45d9edb04b7ce3334ad0e551e29b2e8f3eea7f0c9c1f799dc2f33bc80e" +dependencies = [ + "lower-macros", +] + +[[package]] +name = "lower-macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1fe6a5008813f4afca3ab15d0485fc3ea47d572e7128e325a22057cb863b11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "mattr" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f63dc7ec862e5d146c89d104d437548fef5216a6a653f4afc4b87c581970677" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "qwant" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebdf6119f17fb69ae8e79fef54f50c17f5760a4dd94013f5943b0731b6714bea" +dependencies = [ + "atools", +] + +[[package]] +name = "read-fonts" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04ca636dac446b5664bd16c069c00a9621806895b8bb02c2dc68542b23b8f25d" +dependencies = [ + "bytemuck", + "font-types", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "skrifa" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbeb4ca4399663735553a09dd17ce7e49a0a0203f03b706b39628c4d913a8607" +dependencies = [ + "bytemuck", + "read-fonts", +] + +[[package]] +name = "swash" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f745de914febc7c9ab4388dfaf94bbc87e69f57bb41133a9b0c84d4be49856f3" +dependencies = [ + "skrifa", + "yazi", + "zeno", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "umath" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f74eb7957e3a63fa27bfa53c3d361e7ce3871e66f2518292a011eb8e2c00cc" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "vecto" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9f1339978a8a20d3ca67ea5d6cbb13e5d06bfac55f5a2a05338fb13e36ab75d" +dependencies = [ + "umath", +] + +[[package]] +name = "windows" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "yazi" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" + +[[package]] +name = "zeno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3c70a4d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "dsb" +version = "0.1.0" +edition = "2024" + +[dependencies] +atools = "0.1.6" +fimg = "0.4.45" +implicit-fn = "0.1.0" +swash = "0.2.5" diff --git a/src/cell.rs b/src/cell.rs new file mode 100644 index 0000000..c4d81d3 --- /dev/null +++ b/src/cell.rs @@ -0,0 +1,34 @@ +#[derive(Clone, Copy, Debug, Default)] +pub struct Style { + pub bg: [u8; 3], + pub color: [u8; 3], + // one of [Style::BOLD].. + pub flags: u8, +} +use std::default::Default::default; +use std::fmt::Debug; +impl Style { + pub const BOLD: u8 = 1; + pub const DIM: u8 = 1 << 1; + pub const ITALIC: u8 = 1 << 2; + pub const UNDERLINE: u8 = 1 << 3; + pub const STRIKETHROUGH: u8 = 1 << 4; +} +#[derive(Clone, Copy, Default)] +pub struct Cell { + pub style: Style, + pub letter: Option<char>, +} +impl Debug for Cell { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.letter.unwrap_or(' ')) + } +} +impl Cell { + pub fn basic(c: char) -> Self { + Self { + letter: Some(c), + ..default() + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..db47456 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,203 @@ +#![allow(incomplete_features)] +#![feature( + super_let, + debug_closure_helpers, + const_trait_impl, + anonymous_lifetime_in_impl_trait, + deref_patterns, + generic_const_exprs, + guard_patterns, + impl_trait_in_bindings, + if_let_guard, + import_trait_associated_functions +)] +use std::iter::zip; +mod cell; +use atools::Join; +use swash::FontRef; +use swash::scale::{Render, ScaleContext, Source}; +use swash::shape::ShapeContext; +use swash::shape::cluster::Glyph; +use swash::text::cluster::{CharCluster, Parser, Token}; +use swash::text::{Codepoint, Script}; +use swash::zeno::Format; + +pub struct Fonts<'a, 'b, 'c, 'd> { + regular: FontRef<'a>, + bold: FontRef<'b>, + italic: FontRef<'c>, + bold_italic: FontRef<'d>, +} +impl<'a, 'b, 'c, 'd> Fonts<'a, 'b, 'c, 'd> { + pub fn new( + regular: impl Into<FontRef<'a>>, + bold: impl Into<FontRef<'b>>, + italic: impl Into<FontRef<'c>>, + bold_italic: impl Into<FontRef<'d>>, + ) -> Self { + Self { + regular: regular.into(), + bold: bold.into(), + italic: italic.into(), + bold_italic: bold_italic.into(), + } + } +} +#[implicit_fn::implicit_fn] +pub unsafe fn render( + cells: &[Cell], + (w, h): (usize, usize), + ppem: f32, + bgcolor: [u8; 3], + fonts: Fonts, +) -> Image<Box<[u8]>, 3> { + let m = fonts.regular.metrics(&[]); + let sz = ppem * (m.max_width / m.units_per_em as f32); + let mut i = Image::build(w as _, h as _).fill(bgcolor); + if w < 60 || h < 60 { + return i; + } + + for (col, k) in cells.chunks_exact(w as _).zip(0..) { + for (&cell, j) in zip(col, 0..) { + if cell.style.bg != bgcolor { + let cell = Image::<_, 4>::build(sz.ceil() as u32, (ppem * 1.25).ceil() as u32) + .fill(cell.style.bg.join(255)); + unsafe { + i.as_mut().overlay_at( + &cell, + 4 + (j as f32 * sz) as u32, + (k as f32 * (ppem * 1.25)) as u32 - 20, + ) + }; + } + } + } + let mut characters: Vec<Glyph> = vec![Glyph::default(); w]; + + for (col, k) in cells.chunks_exact(w as _).zip(0..) { + let tokenized = col.iter().enumerate().map(|(i, cell)| { + let ch = cell.letter.unwrap_or(' '); + ( + Token { + ch, + offset: i as u32, + len: ch.len_utf8() as u8, + info: ch.properties().into(), + data: i as u32, + }, + cell.style.flags, + ) + }); + + macro_rules! input { + ($rule:expr, $font:expr) => { + let mut scx = ShapeContext::new(); + let mut shaper = scx + .builder($font) + .size(ppem) + .script(Script::Latin) + .features([ + ("ss19", 1), + ("ss01", 1), + ("ss20", 1), + ("liga", 1), + ("rlig", 1), + ]) + .build(); + + let mut cluster = CharCluster::new(); + + let mut parser = + Parser::new(Script::Latin, tokenized.clone().filter($rule).map(|x| x.0)); + while parser.next(&mut cluster) { + cluster.map(|ch| $font.charmap().map(ch)); + shaper.add_cluster(&cluster); + } + shaper.shape_with(|x| { + x.glyphs.into_iter().for_each(|x| { + characters[x.data as usize] = *x; + }) + }); + }; + } + input!(|x| x.1 & Style::ITALIC == 0, fonts.regular); + input!(|x| x.1 & Style::ITALIC != 0, fonts.bold_italic); // bifont is an instance of ifont + + for (&cell, glyph) in characters.iter().map(|x| (&col[x.data as usize], x)) { + let j = glyph.data as usize; + let mut color = cell.style.color; + if (cell.style.flags & Style::DIM) != 0 { + color = color.map(|x| x / 2); + } + if (cell.style.flags & Style::UNDERLINE) != 0 { + unsafe { + i.as_mut().overlay_at( + &Image::<_, 4>::build(sz.ceil() as u32, 2).fill(color.join(255)), + 4 + (j as f32 * sz) as u32, + (k as f32 * (ppem * 1.25)) as u32 + 5, + ) + }; + } + if (cell.style.flags & Style::STRIKETHROUGH) != 0 { + unsafe { + i.as_mut().overlay_at( + &Image::<_, 4>::build(sz.ceil() as u32, 2).fill(color.join(255)), + 4 + (j as f32 * sz) as u32, + (k as f32 * (ppem * 1.25)) as u32 - 5, + ) + }; + } + if let Some(_) = cell.letter { + let f = match cell.style.flags { + f if f & (Style::BOLD | Style::ITALIC) != 0 => fonts.bold_italic, + f if f & Style::BOLD != 0 => fonts.bold, + f if f & Style::ITALIC != 0 => fonts.italic, + _ => fonts.regular, + }; + let id = glyph.id; + let mut scbd = ScaleContext::new(); + let mut scbd = scbd.builder(f); + scbd = scbd.size(ppem); + + let x = Render::new(&[Source::Outline]) + .format(Format::Alpha) + .render(&mut scbd.build(), id) + .unwrap(); + unsafe { + if x.placement.width == 0 { + continue; + } + let item = Image::<_, 1>::build(x.placement.width, x.placement.height) + .buf_unchecked(x.data); + i.as_mut().blend_alpha_and_color_at( + &item.as_ref(), + color, + 4 + ((j as f32 * sz + glyph.x) + x.placement.left as f32).round() as u32, + ((k as f32 * (ppem * 1.25)) as u32).saturating_sub(x.placement.top as u32), + ); + } + } + } + } + + // if x.view_o == Some(x.cells.row) || x.view_o.is_none() { + // let cell = + // Image::<_, 4>::build(3, (ppem * 1.25).ceil() as u32).fill([0xFF, 0xCC, 0x66, 255]); + // unsafe { + // i.as_mut().overlay_at( + // &cell, + // 4 + ((x.cursor.0 - 1) as f32 * sz) as u32, + // (x.cursor.1 as f32 * (ppem * 1.25)) as u32 - 20, + // ) + // }; + // } + i +} + +pub fn dims(font: &FontRef, ppem: f32) -> (f32, f32) { + let m = font.metrics(&[]); + (ppem * (m.max_width / m.units_per_em as f32), ppem * 1.25) +} +use crate::cell::{Cell, Style}; +use fimg::{Image, OverlayAt}; |