-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock399
-rw-r--r--Cargo.toml10
-rw-r--r--src/cell.rs34
-rw-r--r--src/lib.rs203
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};