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 struct Position {
|
||||||
pub x: u16,
|
pub x: i16,
|
||||||
pub y: u16,
|
pub y: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
pub fn new(x: u16, y: u16) -> Position {
|
pub fn new(x: i16, y: i16) -> Position {
|
||||||
Position { x, y }
|
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) {
|
pub fn set_window(&mut self, pos0: Position, pos1: Position) {
|
||||||
self.set_columns(pos0.x, pos1.x);
|
self.set_columns(pos0.x.max(0) as u16, pos1.x.max(0) as u16);
|
||||||
self.set_rows(pos0.y, pos1.y);
|
self.set_rows(pos0.y.min(240) as u16, pos1.y.min(240) as u16);
|
||||||
self.write(Writeable::Command(Command::RamWR));
|
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) {
|
pub fn draw_rect(&mut self, pos0: Position, pos1: Position, color: Color) {
|
||||||
self.set_window(pos0, pos1);
|
self.set_window(pos0, pos1);
|
||||||
let width = pos1.x - pos0.x;
|
let width = pos1.x as u16 - pos0.x as u16;
|
||||||
let height = pos1.y - pos0.y;
|
let height = pos1.y as u16 - pos0.y as u16;
|
||||||
let pixels = width * height;
|
let pixels = width * height;
|
||||||
let chunks = pixels / 256;
|
let chunks = pixels / 256;
|
||||||
let mut full_buf = [0; 512];
|
let mut full_buf = [0; 512];
|
||||||
|
|||||||
81
src/font.rs
81
src/font.rs
@ -1,6 +1,6 @@
|
|||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
use esp_hal::DriverMode;
|
use esp_hal::DriverMode;
|
||||||
use rusttype::{Font, Point, Scale};
|
use rusttype::{Font, Point, PositionedGlyph, Scale};
|
||||||
|
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec;
|
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");
|
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> {
|
pub struct FontRenderer<'a> {
|
||||||
font: Font<'a>,
|
font: Font<'a>,
|
||||||
height: u32,
|
height: u32,
|
||||||
@ -17,6 +29,11 @@ pub struct FontRenderer<'a> {
|
|||||||
offset: Point<f32>,
|
offset: Point<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RawRenderData<'a> {
|
||||||
|
glyphs: Vec<PositionedGlyph<'a>>,
|
||||||
|
pixel_width: usize,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> FontRenderer<'a> {
|
impl<'a> FontRenderer<'a> {
|
||||||
pub fn create(size: u32) -> FontRenderer<'a> {
|
pub fn create(size: u32) -> FontRenderer<'a> {
|
||||||
let font = Font::try_from_bytes(OPEN_SANS).unwrap();
|
let font = Font::try_from_bytes(OPEN_SANS).unwrap();
|
||||||
@ -42,19 +59,21 @@ impl<'a> FontRenderer<'a> {
|
|||||||
&self,
|
&self,
|
||||||
display: &mut Display<'d, DM, Delay>,
|
display: &mut Display<'d, DM, Delay>,
|
||||||
text: T,
|
text: T,
|
||||||
pos: Position,
|
position: Position,
|
||||||
|
h_align: HorizontalAlignment,
|
||||||
|
v_align: VerticalAlignment,
|
||||||
) {
|
) {
|
||||||
if pos.x > 240 || pos.y > 240 {
|
let data = self.prepare(text.into());
|
||||||
// Everything would be out-of-bounds
|
self.raw_render(display, data, position);
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
pub fn prepare(&self, text: String) -> RawRenderData<'a> {
|
||||||
let glyphs = self
|
let glyphs = self
|
||||||
.font
|
.font
|
||||||
.layout(&text.into(), self.scale, self.offset)
|
.layout(&text, self.scale, self.offset)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let text_width = libm::ceil(
|
let pixel_width = libm::ceil(
|
||||||
glyphs
|
glyphs
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
@ -63,16 +82,41 @@ impl<'a> FontRenderer<'a> {
|
|||||||
.unwrap_or(0.0) as f64,
|
.unwrap_or(0.0) as f64,
|
||||||
) as usize;
|
) as usize;
|
||||||
|
|
||||||
let width = (text_width + pos.x as usize).min(240) - pos.x as usize;
|
RawRenderData {
|
||||||
let height = (self.height + pos.y as u32).min(240) - pos.y as u32;
|
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() {
|
if let Some(bb) = g.pixel_bounding_box() {
|
||||||
g.draw(|x, y, cov| {
|
g.draw(|x, y, cov| {
|
||||||
let x = bb.min.x + x as i32;
|
let x = bb.min.x + x as i32 - clip_left as i32;
|
||||||
let y = bb.min.y + y 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 {
|
if x >= 0 && y >= 0 && x < width as i32 && y < height as i32 {
|
||||||
let col = 255 - (255. * cov) as u8;
|
let col = 255 - (255. * cov) as u8;
|
||||||
pixel_data[(x + y * width as i32) as usize] = Rgb565(col, col, col);
|
pixel_data[(x + y * width as i32) as usize] = Rgb565(col, col, col);
|
||||||
@ -82,10 +126,13 @@ impl<'a> FontRenderer<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
display.set_window(
|
display.set_window(
|
||||||
pos,
|
|
||||||
Position {
|
Position {
|
||||||
x: pos.x + width as u16 - 1,
|
x: start_x,
|
||||||
y: pos.y + height as u16 - 1,
|
y: start_y,
|
||||||
|
},
|
||||||
|
Position {
|
||||||
|
x: end_x - 1,
|
||||||
|
y: end_y - 1,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
display.write(display::Writeable::Data(
|
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::{
|
use esp_hal::{
|
||||||
clock::CpuClock,
|
clock::CpuClock,
|
||||||
delay::Delay,
|
delay::Delay,
|
||||||
gpio::{Level, Output, OutputConfig},
|
gpio::{Input, InputConfig, Level, Output, OutputConfig},
|
||||||
main,
|
main,
|
||||||
spi::master::{Config, Spi},
|
spi::master::{Config, Spi},
|
||||||
time::{Duration, Instant, Rate},
|
time::{Duration, Instant, Rate},
|
||||||
@ -27,7 +27,7 @@ use esp_backtrace as _;
|
|||||||
use crate::{
|
use crate::{
|
||||||
at_commands::ATCommands,
|
at_commands::ATCommands,
|
||||||
display::{Display, Position, Rgb565, SetAddressMode},
|
display::{Display, Position, Rgb565, SetAddressMode},
|
||||||
font::FontRenderer,
|
font::{FontRenderer, HorizontalAlignment, VerticalAlignment},
|
||||||
};
|
};
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
@ -111,7 +111,13 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let font_renderer = FontRenderer::create(30);
|
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_rst = Output::new(peripherals.GPIO15, Level::High, OutputConfig::default());
|
||||||
let sim_pwr_key = Output::new(peripherals.GPIO33, Level::High, OutputConfig::default());
|
let sim_pwr_key = Output::new(peripherals.GPIO33, Level::High, OutputConfig::default());
|
||||||
@ -151,11 +157,35 @@ fn main() -> ! {
|
|||||||
// )
|
// )
|
||||||
// );
|
// );
|
||||||
|
|
||||||
loop {
|
let pull_down_cfg = InputConfig::default().with_pull(esp_hal::gpio::Pull::None);
|
||||||
let delay_start = Instant::now();
|
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) {}
|
// let input_1 = Input::new(peripherals.GPIO4, pull_up_cfg);
|
||||||
// test_delay.delay_millis(1);
|
// 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
|
// 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