Files
killpaper/src/epaper.rs
Oliver Walter b1b4356686 include display driver,
and use rust_tls to make crosscompile easy
2025-09-03 03:47:52 +02:00

274 lines
8.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use embedded_graphics::{
image::{Image, ImageRaw}, mono_font::{MonoTextStyleBuilder},prelude::*, text::{Baseline, Text, TextStyleBuilder}
};
use crate::epd7in5_v2::Epd7in5;
use crate::epd7in5_v2::Display7in5;
use crate::graphics::Display;
use linux_embedded_hal::{
spidev::{self, SpidevOptions}, Delay, SPIError, SpidevDevice
};
use crate::traits::{*};
use crate::color::Color;
use embedded_hal::digital::OutputPin;
use embedded_hal::digital::PinState;
use crate::killinfo::KillInfo;
use image::RgbImage;
pub struct EPaper {
spi : SpidevDevice,
epd: Epd7in5<SpidevDevice, gpiocdev_embedded_hal::InputPin, gpiocdev_embedded_hal::OutputPin, gpiocdev_embedded_hal::OutputPin, Delay>,
display: Display<800, 480, false, 48000 , Color>,
delay :Delay,
}
impl EPaper {
pub fn new() -> Result<EPaper, SPIError> {
let mut spi = SpidevDevice::open("/dev/spidev0.0").expect("spidev directory");
let options = SpidevOptions::new()
.bits_per_word(8)
.max_speed_hz(10_000_000)
.mode(spidev::SpiModeFlags::SPI_MODE_0)
.build();
spi.configure(&options).expect("spi configuration");
// setup display pins
let mut cs = gpiocdev_embedded_hal::OutputPin::new("/dev/gpiochip0", 26, PinState::High).unwrap();
cs.set_high().unwrap();
let busy = gpiocdev_embedded_hal::InputPin::new("/dev/gpiochip0", 24).unwrap();
let dc = gpiocdev_embedded_hal::OutputPin::new("/dev/gpiochip0", 25, PinState::High).unwrap();
let rst = gpiocdev_embedded_hal::OutputPin::new("/dev/gpiochip0", 17, PinState::High).unwrap();
let mut delay = Delay {};
println!("START");
let epd7in5 = Epd7in5::new(&mut spi, busy, dc, rst, &mut delay, None).expect("epd new");
let display = Display7in5::default();
println!("Device successfully initialized!");
return Ok(EPaper { spi, epd: epd7in5, display, delay : Delay {}});
}
pub fn flush(&mut self) -> Result<(), SPIError> {
self.epd.update_and_display_frame(&mut self.spi, self.display.buffer(), &mut self.delay)?;
Ok(())
}
pub fn sleep(&mut self) -> Result<(), SPIError> {
self.epd.sleep(&mut self.spi, &mut self.delay)?;
Ok(())
}
pub fn clear(&mut self, on: bool) {
let color = if on {
Color::Black
} else {
Color::White
};
self.display.clear(color).ok();
}
pub fn draw_text(&mut self, x: i32, y: i32, text: &str) {
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_8X13)
.text_color(Color::White)
.background_color(Color::Black)
.build();
let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build();
let _ = Text::with_text_style(text, Point::new(x, y), style, text_style).draw(&mut self.display);
}
pub fn draw_kill_info(&mut self, kill: &KillInfo, x: i32, y: i32) {
let mut current_y = y;
// Draw Alliance logo (64x64) if present
if let Some(alliance) = &kill.victim.alliance {
let bw_vec = rgb_to_bw_dithered(&alliance.logo);
let logo_raw = ImageRaw::<Color>::new(bw_vec.as_slice(), alliance.logo.width() as u32);
Image::new(&logo_raw, Point::new(x, y)).draw(&mut self.display).ok();
}
// Draw Ship icon (64x64) if present
if let Some(ship) = &kill.victim.ship {
let bw_vec = rgb_to_bw_dithered(&ship.icon);
let width = ship.icon.width() as u32;
let raw_image: ImageRaw<'_, Color> = ImageRaw::<Color>::new(&bw_vec, width);
Image::new(&raw_image, Point::new(x + 64 + 4, current_y)).draw(&mut self.display).ok();
}
let text_x = x + 64 + 64 + 8;
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_8X13)
.text_color(Color::White)
.background_color(Color::Black)
.build();
current_y += 8;
// Character name + Alliance short
let alliance_short = kill
.victim
.alliance
.as_ref()
.map(|a| a.short.as_str())
.unwrap_or("");
let char_name = kill
.victim
.character
.as_ref()
.map(|a| a.name.as_str())
.unwrap_or("");
let char_line = format!("{} [{}]", char_name, alliance_short);
Text::new(&char_line, Point::new(text_x, current_y), style).draw(&mut self.display).ok();
current_y += 16;
// Ship name + total value
let ship_name = kill.victim.ship.as_ref().map(|s| s.name.as_str()).unwrap_or("Unknown");
let value_line = format!("{} - {:.2}M ISK", ship_name, kill.total_value / 1_000_000f64);
Text::new(&value_line, Point::new(text_x, current_y), style).draw(&mut self.display).ok();
current_y += 16;
// System name
Text::new(&kill.system_name, Point::new(text_x, current_y), style).draw(&mut self.display).ok();
}
pub fn draw_loss_info(&mut self, kill: &KillInfo, x: i32, y: i32) {
let mut current_y = y;
// Draw Alliance logo (64x64) if present
if let Some(character) = &kill.victim.character {
let bw_vec = rgb_to_bw_dithered(&character.portrait);
let logo_raw = ImageRaw::<Color>::new(&bw_vec, character.portrait.width() as u32);
Image::new(&logo_raw, Point::new(x, y)).draw(&mut self.display).ok();
}
// Draw Ship icon (64x64) if present
if let Some(ship) = &kill.victim.ship {
let bw_vec = rgb_to_bw_dithered(&ship.icon);
let logo_raw = ImageRaw::<Color>::new(&bw_vec, ship.icon.width() as u32);
Image::new(&logo_raw, Point::new(x + 64 + 4, current_y)).draw(&mut self.display).ok();
}
let text_x = x + 64 + 64 + 8;
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_8X13)
.text_color(Color::Black)
.background_color(Color::White)
.build();
current_y += 8;
// Character name + Alliance short
let alliance_short = kill
.victim
.alliance
.as_ref()
.map(|a| a.short.as_str())
.unwrap_or("");
let char_name = kill
.victim
.character
.as_ref()
.map(|a| a.name.as_str())
.unwrap_or("");
let char_line = format!("{} [{}]", char_name, alliance_short);
Text::new(&char_line, Point::new(text_x, current_y), style).draw(&mut self.display).ok();
current_y += 16;
// Ship name + total value
let ship_name = kill.victim.ship.as_ref().map(|s| s.name.as_str()).unwrap_or("Unknown");
let value_line = format!("{} - {:.2}M ISK", ship_name, kill.total_value / 1_000_000f64);
Text::new(&value_line, Point::new(text_x, current_y), style).draw(&mut self.display).ok();
current_y += 16;
// System name
Text::new(&kill.system_name, Point::new(text_x, current_y), style).draw(&mut self.display).ok();
}
}
fn rgb_to_bw_dithered(rgb: &RgbImage) -> Vec<u8> {
let w = rgb.width() as usize;
let h = rgb.height() as usize;
// Convert to grayscale f32 buffer
let mut gray: Vec<f32> = rgb
.pixels()
.map(|p| {
let [r, g, b] = p.0;
0.299 * r as f32 + 0.587 * g as f32 + 0.114 * b as f32
})
.collect();
let mut bits = Vec::new();
let mut byte = 0u8;
let mut bit_idx = 7;
for y in 0..h {
for x in 0..w {
let idx = y * w + x;
let old = gray[idx];
let new = if old >= 128.0 { 255.0 } else { 0.0 };
let error = old - new;
// Quantize to B/W
let bw = new > 127.0;
if bw {
byte |= 1 << bit_idx;
}
bit_idx -= 1;
if bit_idx == -1 {
bits.push(byte);
byte = 0;
bit_idx = 7;
}
// Distribute error (FloydSteinberg)
if x + 1 < w {
gray[idx + 1] += error * 7.0 / 16.0;
}
if y + 1 < h {
if x > 0 {
gray[idx + w - 1] += error * 3.0 / 16.0;
}
gray[idx + w] += error * 5.0 / 16.0;
if x + 1 < w {
gray[idx + w + 1] += error * 1.0 / 16.0;
}
}
}
}
if bit_idx > 0 {
bits.push(byte);
}
bits
}