Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs203
1 files changed, 203 insertions, 0 deletions
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};