274 lines
8.6 KiB
Rust
274 lines
8.6 KiB
Rust
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 (Floyd–Steinberg)
|
||
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
|
||
} |