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, display: Display<800, 480, false, 48000 , Color>, delay :Delay, } impl EPaper { pub fn new() -> Result { 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::::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::::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::::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::::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 { let w = rgb.width() as usize; let h = rgb.height() as usize; // Convert to grayscale f32 buffer let mut gray: Vec = 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 }