diff --git a/Cargo.toml b/Cargo.toml index 6bc7021e..37b2a486 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ license = "GPL-2.0-only" [build-dependencies] chrono = "0.4" +png = "0.17" serde_json = "1.0" serde_yaml = "0.9" diff --git a/assets/background.png b/assets/background.png new file mode 100644 index 00000000..a67e9e7e Binary files /dev/null and b/assets/background.png differ diff --git a/build.rs b/build.rs index 30bca73d..221ff484 100644 --- a/build.rs +++ b/build.rs @@ -16,12 +16,14 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. use std::env::var_os; -use std::fs::{read_to_string, write}; +use std::fs::{read_to_string, write, File}; +use std::io::Write; use std::path::Path; use std::process::Command; use std::time::SystemTime; use chrono::prelude::Utc; +use png::{BitDepth, ColorType, Decoder}; fn generate_openapi_include() { let cargo_dir = { @@ -100,8 +102,70 @@ fn generate_build_date() { println!("cargo:rustc-env=BUILD_TIMESTAMP={}", timestamp); } +fn decode_png(path: &Path) -> Vec<(u8, u8, u8)> { + let mut reader = Decoder::new(File::open(path).unwrap()).read_info().unwrap(); + + let mut buf = vec![0; reader.output_buffer_size()]; + let info = reader.next_frame(&mut buf).unwrap(); + + let width = info.width as usize; + let height = info.height as usize; + + let bytes_per_pixel = match (info.bit_depth, info.color_type) { + (BitDepth::Eight, ColorType::Rgb) => 3, + (BitDepth::Eight, ColorType::Rgba) => 4, + _ => unimplemented!(), + }; + + let mut pixels = vec![(0, 0, 0); width * height]; + + for y in 0..height { + for x in 0..width { + let idx = y * width + x; + + pixels[idx] = ( + buf[idx * bytes_per_pixel], + buf[idx * bytes_per_pixel + 1], + buf[idx * bytes_per_pixel + 2], + ); + } + } + + pixels +} + +fn generate_background() { + let cargo_dir = { + let dir = var_os("CARGO_MANIFEST_DIR").unwrap(); + Path::new(&dir).to_path_buf() + }; + + let out_dir = { + let dir = var_os("OUT_DIR").unwrap(); + Path::new(&dir).to_path_buf() + }; + + let mut output = { + let output_path = out_dir.join("background.rs"); + File::create(output_path).unwrap() + }; + + println!("cargo:rerun-if-changed=assets/background.png"); + + let pixels = decode_png(&cargo_dir.join("assets/background.png")); + + writeln!(&mut output, "&[").unwrap(); + + for (r, g, b) in pixels { + writeln!(&mut output, " ({}, {}, {}),", r, g, b).unwrap(); + } + + writeln!(&mut output, "]").unwrap(); +} + fn main() { generate_openapi_include(); generate_version_string(); generate_build_date(); + generate_background(); } diff --git a/src/ui/display.rs b/src/ui/display.rs index fbb58532..a91816e9 100644 --- a/src/ui/display.rs +++ b/src/ui/display.rs @@ -29,7 +29,7 @@ mod backend { pub device: (), pub var_screen_info: VarScreeninfo, pub fix_screen_info: FixScreeninfo, - pub frame: [u8; 240 * 240 * 2], + pub frame: [u8; 240 * 240 * 4], } impl Framebuffer { @@ -37,16 +37,16 @@ mod backend { Ok(Self { device: (), var_screen_info: VarScreeninfo { - bits_per_pixel: 16, + bits_per_pixel: 32, xres: 240, yres: 240, ..Default::default() }, fix_screen_info: FixScreeninfo { - line_length: 480, + line_length: 240 * 4, ..Default::default() }, - frame: [0; 240 * 240 * 2], + frame: [0; 240 * 240 * 4], }) } @@ -63,6 +63,8 @@ mod backend { use backend::Framebuffer; +const BACKGROUND: &[(u8, u8, u8)] = include!(concat!(env!("OUT_DIR"), "/background.rs")); + pub struct DisplayExclusive(Framebuffer); pub struct Display { @@ -93,7 +95,7 @@ impl Display { } pub fn clear(&self) { - self.with_lock(|target| target.0.frame.iter_mut().for_each(|p| *p = 0x00)); + self.with_lock(|target| target.clear(BinaryColor::Off).unwrap()); } pub fn screenshooter(&self) -> ScreenShooter { @@ -108,14 +110,21 @@ impl ScreenShooter { let (image, xres, yres) = { let fb = &self.inner.lock().unwrap().0; - let bpp = (fb.var_screen_info.bits_per_pixel / 8) as usize; - let xres = fb.var_screen_info.xres; - let yres = fb.var_screen_info.yres; - let res = (xres as usize) * (yres as usize); + assert!(fb.var_screen_info.bits_per_pixel == 32); + let xres = fb.var_screen_info.xres as usize; + let yres = fb.var_screen_info.yres as usize; + + let mut image = vec![0; xres * yres * 3]; - let image: Vec = (0..res) - .map(|i| if fb.frame[i * bpp] != 0 { 0xff } else { 0 }) - .collect(); + for y in 0..yres { + for x in 0..xres { + let idx = y * xres + x; + + image[idx * 3] = fb.frame[idx * 4 + 2]; + image[idx * 3 + 1] = fb.frame[idx * 4 + 1]; + image[idx * 3 + 2] = fb.frame[idx * 4]; + } + } (image, xres, yres) }; @@ -123,8 +132,8 @@ impl ScreenShooter { let mut dst = Cursor::new(Vec::new()); let mut writer = { - let mut enc = Encoder::new(&mut dst, xres, yres); - enc.set_color(ColorType::Grayscale); + let mut enc = Encoder::new(&mut dst, xres as u32, yres as u32); + enc.set_color(ColorType::Rgb); enc.set_depth(BitDepth::Eight); enc.write_header().unwrap() }; @@ -144,7 +153,7 @@ impl DrawTarget for DisplayExclusive { where I: IntoIterator>, { - let bpp = self.0.var_screen_info.bits_per_pixel / 8; + assert!(self.0.var_screen_info.bits_per_pixel == 32); let xres = self.0.var_screen_info.xres; let yres = self.0.var_screen_info.yres; let line_length = self.0.fix_screen_info.line_length; @@ -157,14 +166,17 @@ impl DrawTarget for DisplayExclusive { continue; } - let offset = line_length * y + bpp * x; + let offset_bg = (y * xres + x) as usize; + let offset_fb = (line_length * y + 4 * x) as usize; - for b in 0..bpp { - self.0.frame[(offset + b) as usize] = match color { - BinaryColor::Off => 0x00, - BinaryColor::On => 0xff, - } - } + let rgb = match color { + BinaryColor::Off => BACKGROUND[offset_bg], + BinaryColor::On => (255, 255, 255), + }; + + self.0.frame[offset_fb] = rgb.2; + self.0.frame[offset_fb + 1] = rgb.1; + self.0.frame[offset_fb + 2] = rgb.0; } Ok(())