Add graphics crate

This commit is contained in:
2025-11-18 13:47:51 +01:00
parent 30ca6301e8
commit 9d265da31b
3 changed files with 220 additions and 0 deletions

83
Cargo.lock generated
View File

@@ -2,6 +2,65 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "az"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "embedded-graphics"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0649998afacf6d575d126d83e68b78c0ab0e00ca2ac7e9b3db11b4cbe8274ef0"
dependencies = [
"az",
"byteorder",
"embedded-graphics-core",
"float-cmp",
"micromath",
]
[[package]]
name = "embedded-graphics-core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba9ecd261f991856250d2207f6d8376946cd9f412a2165d3b75bc87a0bc7a044"
dependencies = [
"az",
"byteorder",
]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
dependencies = [
"num-traits",
]
[[package]]
name = "graphics"
version = "0.1.0"
dependencies = [
"embedded-graphics",
"embedded-graphics-core",
"memmap2",
]
[[package]]
name = "itoa"
version = "1.0.15"
@@ -14,6 +73,21 @@ version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "memmap2"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490"
dependencies = [
"libc",
]
[[package]]
name = "micromath"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815"
[[package]]
name = "minapk"
version = "0.1.0"
@@ -51,6 +125,15 @@ dependencies = [
"syn",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "oc2r-core"
version = "0.1.0"

View File

@@ -0,0 +1,18 @@
[package]
name = "graphics"
version = "0.1.0"
authors.workspace = true
edition.workspace = true
repository.workspace = true
license.workspace = true
keywords.workspace = true
categories.workspace = true
[lints]
workspace = true
[dependencies]
memmap2 = "0.9"
embedded-graphics = "0.8"
embedded-graphics-core = "0.4"

119
crates/graphics/src/main.rs Normal file
View File

@@ -0,0 +1,119 @@
#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_possible_wrap
)]
use std::{convert::Infallible, fs::OpenOptions, io, thread, time::Duration};
use embedded_graphics::{
geometry::Size,
mono_font::{MonoTextStyle, ascii::FONT_6X10},
pixelcolor::Rgb565,
prelude::*,
text::{Baseline, Text},
};
use memmap2::{MmapMut, MmapOptions};
const FB_PATH: &str = "/dev/fb0";
const WIDTH: u32 = 640;
const HEIGHT: u32 = 480;
const BYTES_PER_PIXEL: usize = 2;
struct FbDisplay {
fb: MmapMut,
}
impl FbDisplay {
fn new() -> io::Result<Self> {
println!("Opening framebuffer device at {FB_PATH}");
let file = OpenOptions::new().read(true).write(true).open(FB_PATH)?;
let fb_size = (WIDTH as usize) * (HEIGHT as usize) * BYTES_PER_PIXEL;
// Safety: map /dev/fb0 as writable memory
let fb = unsafe { MmapOptions::new().len(fb_size).map_mut(&file)? };
println!(
"Mapped framebuffer: {}x{} at {} bytes ({} bytes per pixel)",
WIDTH,
HEIGHT,
fb.len(),
BYTES_PER_PIXEL
);
Ok(Self { fb })
}
fn clear(&mut self) {
println!("Clearing framebuffer to black");
for chunk in self.fb.chunks_exact_mut(BYTES_PER_PIXEL) {
chunk.copy_from_slice(&[0x00, 0x00]); // RGB565 black
}
}
fn put_pixel(&mut self, x: u32, y: u32, color: Rgb565) {
if x >= WIDTH || y >= HEIGHT {
return;
}
let idx = ((y * WIDTH + x) as usize) * BYTES_PER_PIXEL;
if idx + 1 >= self.fb.len() {
return;
}
// Rgb565 stores 16-bit value; little endian in memory.
let raw: u16 = color.into_storage();
let bytes = raw.to_le_bytes();
self.fb[idx] = bytes[0];
self.fb[idx + 1] = bytes[1];
}
}
impl DrawTarget for FbDisplay {
type Color = Rgb565;
type Error = Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for Pixel(coord, color) in pixels {
let x = coord.x;
let y = coord.y;
if x >= 0 && y >= 0 {
self.put_pixel(x as u32, y as u32, color);
}
}
Ok(())
}
}
impl OriginDimensions for FbDisplay {
fn size(&self) -> Size {
Size::new(WIDTH, HEIGHT)
}
}
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut fb = FbDisplay::new()?;
fb.clear();
let style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
let text = "Hello embedded-graphics on RGB565";
let text_width = (text.len() as u32) * 6;
let text_height = 10;
let x = (WIDTH - text_width) / 2;
let y = (HEIGHT / 2) + (text_height / 2);
Text::with_baseline(
text,
Point::new(x as i32, y as i32),
style,
Baseline::Bottom,
)
.draw(&mut fb)?;
// Keep the framebuffer content visible
loop {
thread::sleep(Duration::from_secs(1));
}
}