//! Hand-rolled drawing of unicode [box drawing](http://www.unicode.org/charts/PDF/U2500.pdf)
//! and [block elements](https://www.unicode.org/charts/PDF/U2580.pdf).
use std::{cmp, mem, ops};
use crossfont::{BitmapBuffer, Metrics, RasterizedGlyph};
use crate::config::ui_config::Delta;
// Colors which are used for filling shade variants.
const COLOR_FILL_ALPHA_STEP_1: Pixel = Pixel { _r: 192, _g: 192, _b: 192 };
const COLOR_FILL_ALPHA_STEP_2: Pixel = Pixel { _r: 128, _g: 128, _b: 128 };
const COLOR_FILL_ALPHA_STEP_3: Pixel = Pixel { _r: 64, _g: 64, _b: 64 };
/// Default color used for filling.
const COLOR_FILL: Pixel = Pixel { _r: 255, _g: 255, _b: 255 };
/// Returns the rasterized glyph if the character is part of the built-in font.
pub fn builtin_glyph(
character: char,
metrics: &Metrics,
offset: &Delta<i8>,
glyph_offset: &Delta<i8>,
) -> Option<RasterizedGlyph> {
let mut glyph = match character {
// Box drawing characters and block elements.
'\u{2500}'..='\u{259f}' => box_drawing(character, metrics, offset),
_ => return None,
};
// Since we want to ignore `glyph_offset` for the built-in font, subtract it to compensate its
// addition when loading glyphs in the renderer.
glyph.left -= glyph_offset.x as i32;
glyph.top -= glyph_offset.y as i32;
Some(glyph)
}
fn box_drawing(character: char, metrics: &Metrics, offset: &Delta<i8>) -> RasterizedGlyph {
// Ensure that width and height is at least one.
let height = (metrics.line_height as i32 + offset.y as i32).max(1) as usize;
let width = (metrics.average_advance as i32 + offset.x as i32).max(1) as usize;
// Use one eight of the cell width, since this is used as a step size for block elemenets.
let stroke_size = cmp::max((width as f32 / 8.).round() as usize, 1);
let heavy_stroke_size = stroke_size * 2;
// Certain symbols require larger canvas than the cell itself, since for proper contiguous
// lines they require drawing on neighbour cells. So treat them specially early on and handle
// 'normal' characters later.
let mut canvas = match character {
// Diagonals: '╱', '╲', '╳'.
'\u{2571}'..='\u{2573}' => {
// Last coordinates.
let x_end = width as f32;
let mut y_end = height as f32;
let top = height as i32 + metrics.descent as i32 + stroke_size as i32;
let height = height + 2 * stroke_size;
let mut canvas = Canvas::new(width, height + 2 * stroke_size);
// The offset that we should take into account when drawing, since we've enlarged
// buffer vertically by twice of that amount.
let y_offset = stroke_size as f32;
y_end += y_offset;
let k = y_end / x_end;
let f_x = |x: f32, h: f32| -> f32 { -1. * k * x + h + y_offset };
let g_x = |x: f32, h: f32| -> f32 { k * x + h + y_offset };
let from_x = 0.;
let to_x = x_end + 1.;
for stroke_size in 0..2 * stroke_size {
let stroke_size = stroke_size as f32 / 2.;
if character == '\u{2571}' || character == '\u{2573}' {
let h = y_end - stroke_size;
let from_y = f_x(from_x, h);
let to_y = f_x(to_x, h);
canvas.draw_line(from_x, from_y, to_x, to_y);
}
if character == '\u{2572}' || character == '\u{2573}' {
let from_y = g_x(from_x, stroke_size);
let to_y = g_x(to_x, stroke_size);
canvas.draw_line(from_x, from_y, to_x, to_y);
}
}
let buffer = BitmapBuffer::Rgb(canvas.into_raw());
return RasterizedGlyph {
character,
top,
left: 0,
height: height as i32,
width: width as i32,
buffer,
advance: (width as i32, height as