use crate::color;
use crate::glyph;
use crate::img;
use crate::mat;
use crate::mat::Matrix;
pub trait Typesetter {
fn compose(&self, glyphs: glyph::GlyphsRefMut, img: img::ImgRef);
fn glyph_size(&self) -> mat::Size;
#[inline]
fn fix_aspect_ratio(&self, size: mat::Size, glyph_ratio: f32) -> mat::Size {
let factor =
self.glyph_size().width as f32 / (self.glyph_size().height as f32 * glyph_ratio);
mat::Size {
width: (size.width as f32 * factor) as usize,
height: size.height,
}
}
#[inline]
#[deprecated(note = "use `Typesetter::image_size` instead")]
fn size_as_image(&self, glyphs_size: mat::Size) -> mat::Size {
self.image_size(glyphs_size)
}
#[inline]
fn image_size(&self, glyphs_size: mat::Size) -> mat::Size {
mat::Size {
width: glyphs_size.width * self.glyph_size().width,
height: glyphs_size.height * self.glyph_size().height,
}
}
#[inline]
#[deprecated(note = "use `Typesetter::buffer_size` instead")]
fn size_as_glyphs(&self, rgb_size: mat::Size) -> mat::Size {
self.buffer_size(rgb_size)
}
#[inline]
fn buffer_size(&self, rgb_size: mat::Size) -> mat::Size {
mat::Size {
width: rgb_size.width / self.glyph_size().width,
height: rgb_size.height / self.glyph_size().height,
}
}
#[inline]
fn workloads<'a>(
&'a self,
glyphs: glyph::GlyphsRefMut<'a>,
img: img::ImgRef<'a>,
jobs: usize,
) -> Workloads<'a, Self>
where
Self: Sized + Sync,
{
Workloads::new(self, glyphs, img, jobs)
}
}
impl mat::Size {
#[inline]
pub fn to_image_size<T: Typesetter>(&self, typesetter: &T) -> mat::Size {
typesetter.image_size(*self)
}
#[inline]
pub fn to_buffer_size<T: Typesetter>(&self, typesetter: &T) -> mat::Size {
typesetter.buffer_size(*self)
}
}
trait TypesetterImpl<const WIDTH: usize, const HEIGHT: usize> {
fn find_best_match(pattern: u32) -> Option<&'static Template>;
#[inline]
fn glyph_size_impl(&self) -> mat::Size {
(WIDTH, HEIGHT).into()
}
const DEFAULT: Template = Template::new('?', 0, false);
#[inline]
fn compose_impl(&self, mut glyphs: glyph::GlyphsRefMut, img: img::ImgRef) {
let mut cell = [[Default::default(); WIDTH]; HEIGHT];
let in_width = img.width() / WIDTH;
let out_width = std::cmp::min(in_width, glyphs.width());
let in_height = img.height() / HEIGHT;
let out_height = std::cmp::min(in_height, glyphs.height());
for row in 0..out_height {
for col in 0..out_width {
let mut gray_sum = 0u16;
let col_rgb = col * WIDTH;
let row_rgb = row * HEIGHT;
for i in 0..HEIGHT {
for j in 0..WIDTH {
let px = img[((col_rgb + j), (row_rgb + i))];
let gray = px.luminosity();
gray_sum += u16::from(gray);
cell[i][j] = (gray, px);
}
}
let mut pattern = 0;
let gray_avg = (gray_sum / (WIDTH * HEIGHT) as u16) as u8;
for cell_row in cell {
for (gray, _) in cell_row {
pattern <<= 1;
if gray > gray_avg {
pattern |= 1;
}
}
}
let best_match = Self::find_best_match(pattern).unwrap_or(&Self::DEFAULT);
let mut fg_avg = color::Average::new();
let mut bg_avg = color::Average::new();
let mut mask = 1 << ((HEIGHT * WIDTH) - 1);
for cell_row in cell {
for (_, px) in cell_row {
if (best_match.pattern & mask) == 0 {
bg_avg.add(&px);
} else {
fg_avg.add(&px);
}
mask >>= 1;
}
}
let (fg, bg) = if best_match.is_inverted {
(bg_avg.get(), fg_avg.get())
} else {
(fg_avg.get(), bg_avg.get())
};
glyphs[(col, row)] = glyph::Glyph {
char: best_match.char,
foreground: fg,
background: bg,
};
}
}
}
}
pub struct Template {
char: char,
pattern: u32,
is_inverted: bool,
}
impl Template {
#[inline]
const fn new(char: char, pattern: u32, is_inverted: bool) -> Template {
Template {
char,
pattern,
is_inverted,
}
}
}
pub(crate) mod block {
use super::Template;
use super::TypesetterImpl;
use crate::glyph;
use crate::img;
use crate::mat;
pub struct Block;
impl super::Typesetter for Block {
#[inline]
fn compose(&self, glyphs: glyph::GlyphsRefMut, img: img::ImgRef) {
self.compose_impl(glyphs, img);
}
#[inline]
fn glyph_size(&self) -> mat::Size {
self.glyph_size_impl()
}
}
impl super::TypesetterImpl<1, 1> for Block {
#[inline]
fn find_best_match(_: u32) -> Option<&'static Template> {
Some(&TEMPLATE)
}
}
const TEMPLATE: Template = Template::new(' ', 0b0, false);
}
pub(crate) mod half {
use super::Template;
use super::TypesetterImpl;
use crate::glyph;
use crate::img;
use crate::mat;
pub struct Half;
impl super::Typesetter for Half {
#[inline]
fn compose(&self, glyphs: glyph::GlyphsRefMut, img: img::ImgRef) {
self.compose_impl(glyphs, img);
}
#[inline]
fn glyph_size(&self) -> mat::Size {
self.glyph_size_impl()
}
}
impl super::TypesetterImpl<1, 2> for Half {
#[inline]
fn find_best_match(pattern: u32) -> Option<&'static Template> {
TEMPLATES.get(pattern as usize)
}
}
const TEMPLATES: [Template; 4] = [
Template::new(' ', 0b00, false),
Template::new('▄', 0b01, false),
Template::new('▀', 0b10, false),
Template::new(' ', 0b11, true),
];
}
pub(crate) mod quadrant {
use super::Template;
use super::TypesetterImpl;
use crate::glyph;
use crate::img;
use crate::mat;
pub struct Quadrant;
impl super::Typesetter for Quadrant {
#[inline]
fn compose(&self, glyphs: glyph::GlyphsRefMut, img: img::ImgRef) {
self.compose_impl(glyphs, img);
}
#[inline]
fn glyph_size(&self) -> mat::Size {
self.glyph_size_impl()
}
}
impl super::TypesetterImpl<2, 2> for Quadrant {
#[inline]
fn find_best_match(pattern: u32) -> Option<&'static Template> {
TEMPLATES.get(pattern as usize)
}
}
const TEMPLATES: [Template; 16] = [
Template::new(' ', 0b0000, false),
Template::new('▗', 0b0001, false),
Template::new('▖', 0b0010, false),
Template::new('▄', 0b0011, false),
Template::new('▝', 0b0100, false),
Template::new('▐', 0b0101, false),
Template::new('▞', 0b0110, false),
Template::new('▟', 0b0111, false),
Template::new('▘', 0b1000, false),
Template::new('▚', 0b1001, false),
Template::new('▌', 0b1010, false),
Template::new('▙', 0b1011, false),
Template::new('▀', 0b1100, false),
Template::new('▜', 0b1101, false),
Template::new('▛', 0b1110, false),
Template::new(' ', 0b1111, true),
];
}
pub(crate) mod sextant {
use super::Template;
use super::TypesetterImpl;
use crate::glyph;
use crate::img;
use crate::mat;
pub struct Sextant;
impl super::Typesetter for Sextant {
#[inline]
fn compose(&self, glyphs: glyph::GlyphsRefMut, img: img::ImgRef) {
self.compose_impl(glyphs, img);
}
#[inline]
fn glyph_size(&self) -> mat::Size {
self.glyph_size_impl()
}
}
impl super::TypesetterImpl<2, 3> for Sextant {
#[inline]
fn find_best_match(pattern: u32) -> Option<&'static Template> {
TEMPLATES.get(pattern as usize)
}
}
const TEMPLATES: [Template; 64] = [
Template::new(' ', 0b00_00_00, false),
Template::new('🬞', 0b00_00_01, false),
Template::new('🬏', 0b00_00_10, false),
Template::new('🬭', 0b00_00_11, false),
Template::new('🬇', 0b00_01_00, false),
Template::new('🬦', 0b00_01_01, false),
Template::new('🬖', 0b00_01_10, false),
Template::new('🬵', 0b00_01_11, false),
Template::new('🬃', 0b00_10_00, false),
Template::new('🬢', 0b00_10_01, false),
Template::new('🬓', 0b00_10_10, false),
Template::new('🬱', 0b00_10_11, false),
Template::new('🬋', 0b00_11_00, false),
Template::new('🬩', 0b00_11_01, false),
Template::new('🬚', 0b00_11_10, false),
Template::new('🬹', 0b00_11_11, false),
Template::new('🬁', 0b01_00_00, false),
Template::new('🬠', 0b01_00_01, false),
Template::new('🬑', 0b01_00_10, false),
Template::new('🬯', 0b01_00_11, false),
Template::new('🬉', 0b01_01_00, false),
Template::new('▐', 0b01_01_01, false),
Template::new('🬘', 0b01_01_10, false),
Template::new('🬷', 0b01_01_11, false),
Template::new('🬅', 0b01_10_00, false),
Template::new('🬤', 0b01_10_01, false),
Template::new('🬔', 0b01_10_10, false),
Template::new('🬳', 0b01_10_11, false),
Template::new('🬍', 0b01_11_00, false),
Template::new('🬫', 0b01_11_01, false),
Template::new('🬜', 0b01_11_10, false),
Template::new('🬻', 0b01_11_11, false),
Template::new('🬀', 0b10_00_00, false),
Template::new('🬟', 0b10_00_01, false),
Template::new('🬐', 0b10_00_10, false),
Template::new('🬮', 0b10_00_11, false),
Template::new('🬈', 0b10_01_00, false),
Template::new('🬧', 0b10_01_01, false),
Template::new('🬗', 0b10_01_10, false),
Template::new('🬶', 0b10_01_11, false),
Template::new('🬄', 0b10_10_00, false),
Template::new('🬣', 0b10_10_01, false),
Template::new('▌', 0b10_10_10, false),
Template::new('🬲', 0b10_10_11, false),
Template::new('🬌', 0b10_11_00, false),
Template::new('🬪', 0b10_11_01, false),
Template::new('🬛', 0b10_11_10, false),
Template::new('🬺', 0b10_11_11, false),
Template::new('🬂', 0b11_00_00, false),
Template::new('🬡', 0b11_00_01, false),
Template::new('🬒', 0b11_00_10, false),
Template::new('🬰', 0b11_00_11, false),
Template::new('🬊', 0b11_01_00, false),
Template::new('🬨', 0b11_01_01, false),
Template::new('🬙', 0b11_01_10, false),
Template::new('🬸', 0b11_01_11, false),
Template::new('🬆', 0b11_10_00, false),
Template::new('🬥', 0b11_10_01, false),
Template::new('🬕', 0b11_10_10, false),
Template::new('🬴', 0b11_10_11, false),
Template::new('🬎', 0b11_11_00, false),
Template::new('🬬', 0b11_11_01, false),
Template::new('🬝', 0b11_11_10, false),
Template::new(' ', 0b11_11_11, true),
];
}
pub(crate) mod smooth {
use super::Template;
use super::TypesetterImpl;
use crate::glyph;
use crate::img;
use crate::mat;
pub struct Smooth;
impl super::Typesetter for Smooth {
#[inline]
fn compose(&self, glyphs: glyph::GlyphsRefMut, img: img::ImgRef) {
self.compose_impl(glyphs, img);
}
#[inline]
fn glyph_size(&self) -> mat::Size {
self.glyph_size_impl()
}
}
impl super::TypesetterImpl<2, 3> for Smooth {
#[inline]
fn find_best_match(pattern: u32) -> Option<&'static Template> {
TEMPLATES.get(pattern as usize)
}
}
const TEMPLATES: [Template; 64] = [
Template::new(' ', 0b00_00_00, false),
Template::new('🭇', 0b00_00_01, false),
Template::new('🬼', 0b00_00_10, false),
Template::new('🬭', 0b00_00_11, false),
Template::new('🬇', 0b00_01_00, false),
Template::new('🭉', 0b00_01_01, false),
Template::new('🬖', 0b00_01_10, false),
Template::new('🭆', 0b00_01_11, false),
Template::new('🬃', 0b00_10_00, false),
Template::new('🬢', 0b00_10_01, false),
Template::new('🬾', 0b00_10_10, false),
Template::new('🭑', 0b00_10_11, false),
Template::new('🬋', 0b00_11_00, false),
Template::new('🬩', 0b00_11_01, false),
Template::new('🬚', 0b00_11_10, false),
Template::new('🬹', 0b00_11_11, false),
Template::new('🭢', 0b01_00_00, false),
Template::new('🬠', 0b01_00_01, false),
Template::new('🬑', 0b01_00_10, false),
Template::new('🬯', 0b01_00_11, false),
Template::new('🭤', 0b01_01_00, false),
Template::new('▐', 0b01_01_01, false),
Template::new('🬘', 0b01_01_10, false),
Template::new('🭃', 0b01_01_11, false),
Template::new('🬅', 0b01_10_00, false),
Template::new('🬤', 0b01_10_01, false),
Template::new('🬔', 0b01_10_10, false),
Template::new('🬳', 0b01_10_11, false),
Template::new('🬍', 0b01_11_00, false),
Template::new('🬫', 0b01_11_01, false),
Template::new('🬜', 0b01_11_10, false),
Template::new('🭁', 0b01_11_11, false),
Template::new('🭗', 0b10_00_00, false),
Template::new('🬟', 0b10_00_01, false),
Template::new('🬐', 0b10_00_10, false),
Template::new('🬮', 0b10_00_11, false),
Template::new('🬈', 0b10_01_00, false),
Template::new('🬧', 0b10_01_01, false),
Template::new('🬗', 0b10_01_10, false),
Template::new('🬶', 0b10_01_11, false),
Template::new('🭙', 0b10_10_00, false),
Template::new('🬣', 0b10_10_01, false),
Template::new('▌', 0b10_10_10, false),
Template::new('🭎', 0b10_10_11, false),
Template::new('🬌', 0b10_11_00, false),
Template::new('🬪', 0b10_11_01, false),
Template::new('🬛', 0b10_11_10, false),
Template::new('🭌', 0b10_11_11, false),
Template::new('🬂', 0b11_00_00, false),
Template::new('🬡', 0b11_00_01, false),
Template::new('🬒', 0b11_00_10, false),
Template::new('🬰', 0b11_00_11, false),
Template::new('🭧', 0b11_01_00, false),
Template::new('🭔', 0b11_01_01, false),
Template::new('🬙', 0b11_01_10, false),
Template::new('🬸', 0b11_01_11, false),
Template::new('🭜', 0b11_10_00, false),
Template::new('🬥', 0b11_10_01, false),
Template::new('🭟', 0b11_10_10, false),
Template::new('🬴', 0b11_10_11, false),
Template::new('🬎', 0b11_11_00, false),
Template::new('🭒', 0b11_11_01, false),
Template::new('🭝', 0b11_11_10, false),
Template::new(' ', 0b11_11_11, true),
];
}
pub(crate) mod asymmetric {
use super::Template;
use super::TypesetterImpl;
use crate::glyph;
use crate::img;
use crate::mat;
pub struct Asymmetric;
impl super::Typesetter for Asymmetric {
#[inline]
fn compose(&self, glyphs: glyph::GlyphsRefMut, img: img::ImgRef) {
self.compose_impl(glyphs, img);
}
#[inline]
fn glyph_size(&self) -> mat::Size {
self.glyph_size_impl()
}
}
impl super::TypesetterImpl<4, 8> for Asymmetric {
#[inline]
fn find_best_match(pattern: u32) -> Option<&'static Template> {
let mut best_match = None;
let mut smallest_diff = 32;
for template in &TEMPLATES {
if pattern == template.pattern {
best_match = Some(template);
break;
}
let diff = (template.pattern ^ pattern).count_ones();
if diff < smallest_diff {
best_match = Some(template);
smallest_diff = diff;
}
}
best_match
}
}
const TEMPLATES: [Template; 52] = [
Template::new(' ', 0x0000_0000, false),
Template::new('▁', 0x0000_000f, false),
Template::new('▂', 0x0000_00ff, false),
Template::new('▃', 0x0000_0fff, false),
Template::new('▄', 0x0000_ffff, false),
Template::new('▅', 0x000f_ffff, false),
Template::new('▆', 0x00ff_ffff, false),
Template::new('▇', 0x0fff_ffff, false),
Template::new(' ', 0xffff_ffff, true),
Template::new('▁', 0xffff_fff0, true),
Template::new('▂', 0xffff_ff00, true),
Template::new('▃', 0xffff_f000, true),
Template::new('▄', 0xffff_0000, true),
Template::new('▅', 0xfff0_0000, true),
Template::new('▆', 0xff00_0000, true),
Template::new('▇', 0xf000_0000, true),
Template::new('▊', 0xeeee_eeee, false),
Template::new('▌', 0xcccc_cccc, false),
Template::new('▎', 0x8888_8888, false),
Template::new('▎', 0x7777_7777, true),
Template::new('▌', 0x3333_3333, true),
Template::new('▊', 0x1111_1111, true),
Template::new('▗', 0x0000_3333, false),
Template::new('▖', 0x0000_cccc, false),
Template::new('▝', 0x3333_0000, false),
Template::new('▘', 0xcccc_0000, false),
Template::new('▞', 0x3333_cccc, false),
Template::new('▚', 0xcccc_3333, false),
Template::new('▟', 0x3333_ffff, false),
Template::new('▙', 0xcccc_ffff, false),
Template::new('▜', 0xffff_3333, false),
Template::new('▛', 0xffff_cccc, false),
Template::new('▒', 0xa5a5_a5a5, false),
Template::new('▒', 0x5a5a_5a5a, false),
Template::new('■', 0x0006_6000, false),
Template::new('■', 0xfff9_9fff, true),
Template::new('▬', 0x000f_f000, false),
Template::new('━', 0x000f_0000, false),
Template::new('━', 0x0000_f000, false),
Template::new('▬', 0xfff0_0fff, true),
Template::new('━', 0xfff0_ffff, true),
Template::new('━', 0xffff_0fff, true),
Template::new('┃', 0x6666_6666, false),
Template::new('│', 0x4444_4444, false),
Template::new('│', 0x2222_2222, false),
Template::new('┃', 0x9999_9999, true),
Template::new('│', 0xdddd_dddd, true),
Template::new('│', 0xbbbb_bbbb, true),
Template::new('╸', 0x000c_c000, false),
Template::new('╺', 0x0003_3000, false),
Template::new('╹', 0x6666_0000, false),
Template::new('╻', 0x0000_6666, false),
];
}
pub struct Workload<'a, T> {
typesetter: &'a T,
glyphs: glyph::GlyphsRefMut<'a>,
img: img::ImgRef<'a>,
}
impl<'a, T> Workload<'a, T>
where
T: Typesetter + Sync,
{
#[inline]
pub fn run(self) {
self.typesetter.compose(self.glyphs, self.img);
}
}
pub struct Workloads<'a, T> {
glyph_splits: mat::SplitsMut<'a, glyph::Glyph>,
img_splits: mat::Splits<'a, color::Rgb>,
typesetter: &'a T,
}
impl<'a, T> Workloads<'a, T> {
#[inline]
pub fn new(
typesetter: &'a T,
glyphs: glyph::GlyphsRefMut<'a>,
img: img::ImgRef<'a>,
jobs: usize,
) -> Workloads<'a, T>
where
T: Typesetter + Sync,
{
let in_size = img.size().to_buffer_size(typesetter);
let height = std::cmp::min(in_size.height, glyphs.height());
assert_ne!(jobs, 0, "`jobs` must be non-zero");
let rows = (height + (jobs - 1)) / jobs;
let glyph_size = typesetter.glyph_size();
let img_splits = img.splits(rows * glyph_size.height);
let glyph_splits = glyphs.splits_move(rows);
debug_assert_eq!(img_splits.size_hint(), glyph_splits.size_hint());
Workloads {
glyph_splits,
img_splits,
typesetter,
}
}
}
impl<'a, T> Iterator for Workloads<'a, T> {
type Item = Workload<'a, T>;
#[inline]
fn next(&mut self) -> Option<Workload<'a, T>> {
let workload = Workload {
typesetter: self.typesetter,
glyphs: self.glyph_splits.next()?,
img: self.img_splits.next()?,
};
Some(workload)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
debug_assert_eq!(self.img_splits.size_hint(), self.glyph_splits.size_hint());
self.img_splits.size_hint()
}
}
impl<'a, T> ExactSizeIterator for Workloads<'a, T> {}