Improve text rendering capabilities
This commit is contained in:
parent
325d707685
commit
5438f09820
@ -64,14 +64,14 @@ impl Mul<f32> for Rgb565 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct Position {
|
||||
pub x: u16,
|
||||
pub y: u16,
|
||||
pub x: i16,
|
||||
pub y: i16,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn new(x: u16, y: u16) -> Position {
|
||||
pub fn new(x: i16, y: i16) -> Position {
|
||||
Position { x, y }
|
||||
}
|
||||
}
|
||||
@ -334,8 +334,8 @@ impl<'d, DM: DriverMode, T: DelayNs> Display<'d, DM, T> {
|
||||
}
|
||||
|
||||
pub fn set_window(&mut self, pos0: Position, pos1: Position) {
|
||||
self.set_columns(pos0.x, pos1.x);
|
||||
self.set_rows(pos0.y, pos1.y);
|
||||
self.set_columns(pos0.x.max(0) as u16, pos1.x.max(0) as u16);
|
||||
self.set_rows(pos0.y.min(240) as u16, pos1.y.min(240) as u16);
|
||||
self.write(Writeable::Command(Command::RamWR));
|
||||
}
|
||||
|
||||
@ -346,8 +346,8 @@ impl<'d, DM: DriverMode, T: DelayNs> Display<'d, DM, T> {
|
||||
|
||||
pub fn draw_rect(&mut self, pos0: Position, pos1: Position, color: Color) {
|
||||
self.set_window(pos0, pos1);
|
||||
let width = pos1.x - pos0.x;
|
||||
let height = pos1.y - pos0.y;
|
||||
let width = pos1.x as u16 - pos0.x as u16;
|
||||
let height = pos1.y as u16 - pos0.y as u16;
|
||||
let pixels = width * height;
|
||||
let chunks = pixels / 256;
|
||||
let mut full_buf = [0; 512];
|
||||
|
||||
79
src/font.rs
79
src/font.rs
@ -1,6 +1,6 @@
|
||||
use embedded_hal::delay::DelayNs;
|
||||
use esp_hal::DriverMode;
|
||||
use rusttype::{Font, Point, Scale};
|
||||
use rusttype::{Font, Point, PositionedGlyph, Scale};
|
||||
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
@ -10,6 +10,18 @@ use crate::display::{self, Display, Position, Rgb565};
|
||||
|
||||
static OPEN_SANS: &'static [u8] = include_bytes!("./OpenSans_Condensed-Regular.ttf");
|
||||
|
||||
pub enum HorizontalAlignment {
|
||||
RightToLeft,
|
||||
Center,
|
||||
LeftToRight,
|
||||
}
|
||||
|
||||
pub enum VerticalAlignment {
|
||||
TopToBottom,
|
||||
Center,
|
||||
BottomToTop,
|
||||
}
|
||||
|
||||
pub struct FontRenderer<'a> {
|
||||
font: Font<'a>,
|
||||
height: u32,
|
||||
@ -17,6 +29,11 @@ pub struct FontRenderer<'a> {
|
||||
offset: Point<f32>,
|
||||
}
|
||||
|
||||
pub struct RawRenderData<'a> {
|
||||
glyphs: Vec<PositionedGlyph<'a>>,
|
||||
pixel_width: usize,
|
||||
}
|
||||
|
||||
impl<'a> FontRenderer<'a> {
|
||||
pub fn create(size: u32) -> FontRenderer<'a> {
|
||||
let font = Font::try_from_bytes(OPEN_SANS).unwrap();
|
||||
@ -42,19 +59,21 @@ impl<'a> FontRenderer<'a> {
|
||||
&self,
|
||||
display: &mut Display<'d, DM, Delay>,
|
||||
text: T,
|
||||
pos: Position,
|
||||
position: Position,
|
||||
h_align: HorizontalAlignment,
|
||||
v_align: VerticalAlignment,
|
||||
) {
|
||||
if pos.x > 240 || pos.y > 240 {
|
||||
// Everything would be out-of-bounds
|
||||
return;
|
||||
let data = self.prepare(text.into());
|
||||
self.raw_render(display, data, position);
|
||||
}
|
||||
|
||||
pub fn prepare(&self, text: String) -> RawRenderData<'a> {
|
||||
let glyphs = self
|
||||
.font
|
||||
.layout(&text.into(), self.scale, self.offset)
|
||||
.layout(&text, self.scale, self.offset)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let text_width = libm::ceil(
|
||||
let pixel_width = libm::ceil(
|
||||
glyphs
|
||||
.iter()
|
||||
.rev()
|
||||
@ -63,16 +82,41 @@ impl<'a> FontRenderer<'a> {
|
||||
.unwrap_or(0.0) as f64,
|
||||
) as usize;
|
||||
|
||||
let width = (text_width + pos.x as usize).min(240) - pos.x as usize;
|
||||
let height = (self.height + pos.y as u32).min(240) - pos.y as u32;
|
||||
RawRenderData {
|
||||
glyphs,
|
||||
pixel_width,
|
||||
}
|
||||
}
|
||||
|
||||
let mut pixel_data = vec![Rgb565::white(); height as usize * width];
|
||||
pub fn raw_render<'d, DM: DriverMode, Delay: DelayNs>(
|
||||
&self,
|
||||
display: &mut Display<'d, DM, Delay>,
|
||||
data: RawRenderData<'a>,
|
||||
pos: Position,
|
||||
) {
|
||||
let start_x = pos.x.clamp(0, 240);
|
||||
let start_y = pos.y.clamp(0, 240);
|
||||
let end_x = (pos.x + data.pixel_width as i16).clamp(0, 240);
|
||||
let end_y = (pos.y + self.height as i16).clamp(0, 240);
|
||||
|
||||
for g in glyphs {
|
||||
if start_x == end_x || start_y == end_y {
|
||||
// Nothing to draw
|
||||
return;
|
||||
}
|
||||
|
||||
let clip_left = if pos.x < 0 { -pos.x } else { 0 };
|
||||
let clip_top = if pos.y < 0 { -pos.y } else { 0 };
|
||||
|
||||
let width = end_x - start_x;
|
||||
let height = end_y - start_y;
|
||||
|
||||
let mut pixel_data = vec![Rgb565::white(); height as usize * width as usize];
|
||||
|
||||
for g in data.glyphs {
|
||||
if let Some(bb) = g.pixel_bounding_box() {
|
||||
g.draw(|x, y, cov| {
|
||||
let x = bb.min.x + x as i32;
|
||||
let y = bb.min.y + y as i32;
|
||||
let x = bb.min.x + x as i32 - clip_left as i32;
|
||||
let y = bb.min.y + y as i32 - clip_top as i32;
|
||||
if x >= 0 && y >= 0 && x < width as i32 && y < height as i32 {
|
||||
let col = 255 - (255. * cov) as u8;
|
||||
pixel_data[(x + y * width as i32) as usize] = Rgb565(col, col, col);
|
||||
@ -82,10 +126,13 @@ impl<'a> FontRenderer<'a> {
|
||||
}
|
||||
|
||||
display.set_window(
|
||||
pos,
|
||||
Position {
|
||||
x: pos.x + width as u16 - 1,
|
||||
y: pos.y + height as u16 - 1,
|
||||
x: start_x,
|
||||
y: start_y,
|
||||
},
|
||||
Position {
|
||||
x: end_x - 1,
|
||||
y: end_y - 1,
|
||||
},
|
||||
);
|
||||
display.write(display::Writeable::Data(
|
||||
|
||||
44
src/main.rs
44
src/main.rs
@ -15,7 +15,7 @@ use esp_alloc::export::enumset::EnumSet;
|
||||
use esp_hal::{
|
||||
clock::CpuClock,
|
||||
delay::Delay,
|
||||
gpio::{Level, Output, OutputConfig},
|
||||
gpio::{Input, InputConfig, Level, Output, OutputConfig},
|
||||
main,
|
||||
spi::master::{Config, Spi},
|
||||
time::{Duration, Instant, Rate},
|
||||
@ -27,7 +27,7 @@ use esp_backtrace as _;
|
||||
use crate::{
|
||||
at_commands::ATCommands,
|
||||
display::{Display, Position, Rgb565, SetAddressMode},
|
||||
font::FontRenderer,
|
||||
font::{FontRenderer, HorizontalAlignment, VerticalAlignment},
|
||||
};
|
||||
|
||||
extern crate alloc;
|
||||
@ -111,7 +111,13 @@ fn main() -> ! {
|
||||
|
||||
let font_renderer = FontRenderer::create(30);
|
||||
|
||||
font_renderer.render(&mut display, "Hello World!", Position::new(70, 220));
|
||||
font_renderer.render(
|
||||
&mut display,
|
||||
"Hello World!",
|
||||
Position::new(0, 0),
|
||||
HorizontalAlignment::LeftToRight,
|
||||
VerticalAlignment::TopToBottom,
|
||||
);
|
||||
|
||||
let sim_rst = Output::new(peripherals.GPIO15, Level::High, OutputConfig::default());
|
||||
let sim_pwr_key = Output::new(peripherals.GPIO33, Level::High, OutputConfig::default());
|
||||
@ -151,11 +157,35 @@ fn main() -> ! {
|
||||
// )
|
||||
// );
|
||||
|
||||
loop {
|
||||
let delay_start = Instant::now();
|
||||
let pull_down_cfg = InputConfig::default().with_pull(esp_hal::gpio::Pull::None);
|
||||
let pull_up_cfg = InputConfig::default().with_pull(esp_hal::gpio::Pull::None);
|
||||
// let mut input_1 = Output::new(peripherals.GPIO4, Level::High, OutputConfig::default());
|
||||
// let mut input_2 = Output::new(peripherals.GPIO36, Level::High, OutputConfig::default());
|
||||
// let mut input_3 = Output::new(peripherals.GPIO22, Level::High, OutputConfig::default());
|
||||
// let mut input_4 = Output::new(peripherals.GPIO12, Level::High, OutputConfig::default());
|
||||
// let mut input_5 = Output::new(peripherals.GPIO13, Level::High, OutputConfig::default());
|
||||
// let mut input_6 = Output::new(peripherals.GPIO26, Level::High, OutputConfig::default());
|
||||
// let mut input_7 = Output::new(peripherals.GPIO27, Level::High, OutputConfig::default());
|
||||
|
||||
while delay_start.elapsed() < Duration::from_millis(100) {}
|
||||
// test_delay.delay_millis(1);
|
||||
// let input_1 = Input::new(peripherals.GPIO4, pull_up_cfg);
|
||||
// let input_2 = Input::new(peripherals.GPIO36, pull_up_cfg);
|
||||
// let input_3 = Input::new(peripherals.GPIO22, pull_up_cfg);
|
||||
// let input_4 = Input::new(peripherals.GPIO12, pull_up_cfg);
|
||||
// let input_5 = Input::new(peripherals.GPIO13, pull_up_cfg);
|
||||
// let input_6 = Input::new(peripherals.GPIO26, pull_up_cfg);
|
||||
// let input_7 = Input::new(peripherals.GPIO27, pull_up_cfg);
|
||||
|
||||
let mut test_delay = Delay::new();
|
||||
loop {
|
||||
// log::info!("Input 1: {}", input_1.is_high());
|
||||
// log::info!("Input 2: {}", input_2.is_high());
|
||||
// log::info!("Input 3: {}", input_3.is_high());
|
||||
// log::info!("Input 4: {}", input_4.is_high());
|
||||
// log::info!("Input 5: {}", input_5.is_high());
|
||||
// log::info!("Input 6: {}", input_6.is_high());
|
||||
// log::info!("Input 7: {}", input_7.is_high());
|
||||
// input_7.toggle();
|
||||
test_delay.delay_millis(100);
|
||||
}
|
||||
|
||||
// for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.1.0/examples
|
||||
|
||||
Loading…
Reference in New Issue
Block a user