Add graphics crate
This commit is contained in:
83
Cargo.lock
generated
83
Cargo.lock
generated
@@ -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"
|
||||
|
||||
18
crates/graphics/Cargo.toml
Normal file
18
crates/graphics/Cargo.toml
Normal 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
119
crates/graphics/src/main.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user