Skip to content

Commit

Permalink
fix(tui): ascii too narrow (#63), fixes #61
Browse files Browse the repository at this point in the history
  • Loading branch information
7sDream authored Nov 14, 2023
1 parent 2040e1c commit 9ec9e76
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 107 deletions.
2 changes: 1 addition & 1 deletion src/preview/terminal/render/mono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ impl Render for MonoRender {
type Pixel = bool;

fn render_pixel(&self, _up: u8, _left: u8, gray: u8, _right: u8, _down: u8) -> Self::Pixel {
gray == u8::MAX
gray >= 128
}
}
4 changes: 2 additions & 2 deletions src/preview/terminal/ui/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ pub static CHAR_RENDERS: Lazy<HashMap<RenderType, BoxedRender<char>>> = Lazy::ne
renders
});

pub static MONO_RENDER: Lazy<BoxedRender<bool>> = Lazy::new(|| Box::<MonoRender>::default());
pub static MONO_RENDER: Lazy<MonoRender> = Lazy::new(MonoRender::default);

#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct CacheKey {
pub index: usize,
pub rt: RenderType,
pub height: u32,
pub width: u32,
pub height: u32,
}

pub enum GlyphCache {
Expand Down
25 changes: 11 additions & 14 deletions src/preview/terminal/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use tui::{
};

use self::{
cache::{GlyphCache, GlyphCanvasShape, RenderType},
cache::{GlyphCache, GlyphCanvasShape},
event::{TerminalEvent, TerminalEventStream},
state::State,
};
Expand Down Expand Up @@ -80,7 +80,7 @@ impl<'a: 'a> UI<'a> {
}

fn draw_preview_canvas(&self, area: Rect, f: &mut Frame<'_>, shape: &GlyphCanvasShape) {
let (canvas_width, canvas_height) = self.state.get_char_pixel_cell();
let (canvas_width, canvas_height) = self.state.get_canvas_size_by_pixel();
let canvas_width = f64::from(canvas_width);
let canvas_height = f64::from(canvas_height);
let canvas = Canvas::default()
Expand All @@ -98,8 +98,13 @@ impl<'a: 'a> UI<'a> {
I: IntoIterator<Item = &'s str>,
I::IntoIter: ExactSizeIterator,
{
let (_, height) = self.state.get_canvas_size_by_char();

let iter = paragraph.into_iter();
let padding = (area.height as usize).saturating_sub(2).saturating_sub(iter.len());

// saturating_sub here makes padding zero instead of overflow to a huge number
// if render result taller then preview area
let padding = (height as usize).saturating_sub(iter.len());
let mut lines = vec![Line::from(""); padding / 2];

for line in iter {
Expand Down Expand Up @@ -249,17 +254,9 @@ impl<'a: 'a> UI<'a> {
let list = main[0];
let canvas = main[1];

let mut width = u32::from(canvas.width.saturating_sub(2));
let mut height = u32::from(canvas.height.saturating_sub(2));
let rt = self.state.get_render_type();
if rt == &RenderType::Moon {
width /= 2;
} else if rt == &RenderType::Mono {
width = width.saturating_mul(2);
height = height.saturating_mul(4);
}

self.state.update_char_pixel_cell(width, height);
let width = u32::from(canvas.width.saturating_sub(2));
let height = u32::from(canvas.height.saturating_sub(2));
self.state.update_canvas_size_by_char(width, height);

self.draw_list(list, f);
self.draw_preview(canvas, f);
Expand Down
63 changes: 38 additions & 25 deletions src/preview/terminal/ui/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ use super::cache::{CacheKey, GlyphCache, GlyphCanvasShape, RenderType, CHAR_REND
use crate::{
family::Family,
loader::{FaceInfo, DATABASE},
preview::terminal::ui::cache::GlyphParagraph,
rasterizer::{Bitmap, FontFace, PixelFormat},
preview::terminal::{render::Render, ui::cache::GlyphParagraph},
rasterizer::{Bitmap, Rasterizer},
};

pub struct State<'a> {
Expand Down Expand Up @@ -67,44 +67,53 @@ impl<'a> State<'a> {
}
}

fn cache_key(&self) -> CacheKey {
CacheKey {
index: self.index(),
rt: self.rt,
height: self.height.get(),
width: self.width.get(),
}
fn cache_key(&self, width: u32, height: u32) -> CacheKey {
CacheKey { index: self.index(), rt: self.rt, width, height }
}

pub fn render(&self) -> Rc<Result<GlyphCache, &'static str>> {
let key = self.cache_key();
self.cache.borrow_mut().entry(key).or_insert_with(|| Rc::new(self.real_render())).clone()
let (width, height) = match self.rt {
RenderType::Mono => self.get_canvas_size_by_pixel(),
_ => self.get_canvas_size_by_char(),
};

let key = self.cache_key(width, height);
self.cache
.borrow_mut()
.entry(key)
.or_insert_with(|| Rc::new(self.real_render(width, height)))
.clone()
}

fn rasterize(&self) -> Result<Bitmap, &'static str> {
fn rasterize(&self, _width: u32, height: u32) -> Result<Bitmap, &'static str> {
let info = self.font_faces_info[self.index()];

let scale = if matches!(self.rt, RenderType::AsciiLevel10 | RenderType::AsciiLevel70) {
Some(2.0)
} else {
None
};

DATABASE
.with_face_data(info.id, |data, index| -> Option<Bitmap> {
let mut face = FontFace::new(data, index).ok()?;
face.set_size(self.height.get(), self.width.get());
face.load_glyph(info.gid.0, match self.rt {
RenderType::Mono => PixelFormat::Monochrome,
_ => PixelFormat::Gray,
})
let mut r = Rasterizer::new(data, index).ok()?;
r.set_pixel_height(height);
if let Some(scale) = scale {
r.set_hscale(scale);
}
r.rasterize(info.gid.0)
})
.ok_or("Can't read this font file")?
.ok_or("Can't get glyph from this font")
}

fn real_render(&self) -> Result<GlyphCache, &'static str> {
let bitmap = self.rasterize()?;

fn real_render(&self, width: u32, height: u32) -> Result<GlyphCache, &'static str> {
let bitmap = self.rasterize(width, height)?;
let cache = match self.rt {
RenderType::Mono => GlyphCache::Canvas(GlyphCanvasShape::new(
MONO_RENDER.render(&bitmap),
self.width.get() as f64,
self.height.get() as f64,
width as f64,
height as f64,
)),
rt => GlyphCache::Paragraph(GlyphParagraph::new(
CHAR_RENDERS.get(&rt).expect("all render must be exist").render(&bitmap),
Expand Down Expand Up @@ -169,12 +178,16 @@ impl<'a> State<'a> {
}
}

pub fn update_char_pixel_cell(&self, width: u32, height: u32) {
pub fn update_canvas_size_by_char(&self, width: u32, height: u32) {
self.width.replace(width);
self.height.replace(height);
}

pub fn get_char_pixel_cell(&self) -> (u32, u32) {
pub fn get_canvas_size_by_char(&self) -> (u32, u32) {
(self.width.get(), self.height.get())
}

pub fn get_canvas_size_by_pixel(&self) -> (u32, u32) {
(self.width.get() * 2, self.height.get() * 4)
}
}
20 changes: 2 additions & 18 deletions src/rasterizer/bitmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,13 @@ pub struct Metrics {
pub width: usize,
}

#[derive(Copy, Clone)]
pub enum PixelFormat {
Gray,
Monochrome,
}

pub struct Bitmap {
metrics: Metrics,
bitmap: Grid<u8>,
}

impl Bitmap {
pub fn new(curves: &OutlinedGlyph, format: PixelFormat) -> Self {
pub fn new(curves: &OutlinedGlyph) -> Self {
let bound = curves.px_bounds();

let metrics = Metrics {
Expand All @@ -51,17 +45,7 @@ impl Bitmap {
let mut bitmap = Grid::new(metrics.height, metrics.width);

curves.draw(|x, y, c| {
let value = match format {
PixelFormat::Gray => (c * 255.0).round() as u8,
PixelFormat::Monochrome => {
if c <= 0.5 {
u8::MIN
} else {
u8::MAX
}
}
};

let value = (c * 255.0).round() as u8;
bitmap[y as usize][x as usize] = value
});

Expand Down
44 changes: 0 additions & 44 deletions src/rasterizer/font_face.rs

This file was deleted.

35 changes: 32 additions & 3 deletions src/rasterizer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,36 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

mod bitmap;
mod font_face;

pub use bitmap::{Bitmap, Metrics, PixelFormat};
pub use font_face::FontFace;
use ab_glyph::{Font, FontRef, GlyphId, InvalidFont, PxScale};

pub use self::bitmap::{Bitmap, Metrics};

pub struct Rasterizer<'a> {
face: FontRef<'a>,
height: u32,
hscale: f32,
}

impl<'a> Rasterizer<'a> {
pub fn new(data: &'a [u8], index: u32) -> Result<Self, InvalidFont> {
let face = FontRef::try_from_slice_and_index(data, index)?;
Ok(Self { face, height: 0, hscale: 1.0 })
}

pub fn set_pixel_height(&mut self, height: u32) {
self.height = height;
}

pub fn set_hscale(&mut self, scale: f32) {
self.hscale = scale
}

pub fn rasterize(self, gid: u16) -> Option<Bitmap> {
let glyph_id = GlyphId(gid);
let glyph = glyph_id
.with_scale(PxScale { x: self.height as f32 * self.hscale, y: self.height as f32 });
let curve = self.face.outline_glyph(glyph)?;
Some(Bitmap::new(&curve))
}
}

0 comments on commit 9ec9e76

Please sign in to comment.