commit 3c35bc7feade44dc4454b01d0b9d2a33f8158d8e Author: mincomk Date: Thu May 15 16:33:37 2025 +0900 initial commit diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5116bb8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,387 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[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 = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "spin 0.9.8", + "stable_deref_trait", +] + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "micromath" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "png-decoder" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e3e93d4884a2609f2dccbafabd3e25c49e89bb872ab84bfea60feaf17615944" +dependencies = [ + "crc32fast", + "miniz_oxide", + "num_enum", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinygif" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f1406b710986188de8d393c810213d4bbf940e327d90d52cff9930d007a248" +dependencies = [ + "embedded-graphics", + "heapless", +] + +[[package]] +name = "ucs2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79298e11f316400c57ec268f3c2c29ac3c4d4777687955cd3d4f3a35ce7eba" +dependencies = [ + "bit_field", +] + +[[package]] +name = "uefi" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7569ceafb898907ff764629bac90ac24ba4203c38c33ef79ee88c74aa35b11" +dependencies = [ + "bitflags", + "cfg-if", + "log", + "ptr_meta", + "ucs2", + "uefi-macros", + "uefi-raw", + "uguid", +] + +[[package]] +name = "uefi-macros" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3dad47b3af8f99116c0f6d4d669c439487d9aaf1c8d9480d686cda6f3a8aa23" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "uefi-raw" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cad96b8baaf1615d3fdd0f03d04a0b487d857c1b51b19dcbfe05e2e3c447b78" +dependencies = [ + "bitflags", + "uguid", +] + +[[package]] +name = "uefi-video" +version = "0.1.0" +dependencies = [ + "embedded-graphics-core", + "libm", + "log", + "png-decoder", + "spin 0.10.0", + "tinygif", + "uefi", +] + +[[package]] +name = "uguid" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab14ea9660d240e7865ce9d54ecdbd1cd9fa5802ae6f4512f093c7907e921533" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d43ad46 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "uefi-video" +version = "0.1.0" +edition = "2024" + +[dependencies] +embedded-graphics-core = "0.4.0" +libm = "0.2.15" +log = "0.4.27" +png-decoder = "0.1.1" +spin = "0.10.0" +tinygif = "0.0.4" +uefi = { version = "0.35.0", features = ["logger", "panic_handler"] } diff --git a/OVMF.fd b/OVMF.fd new file mode 100644 index 0000000..0875ec6 Binary files /dev/null and b/OVMF.fd differ diff --git a/src/codec/image.rs b/src/codec/image.rs new file mode 100644 index 0000000..3acbd1d --- /dev/null +++ b/src/codec/image.rs @@ -0,0 +1,23 @@ +use alloc::{boxed::Box, vec::Vec}; + +use crate::{color::Color, error::RenderError}; + +use super::transform::Transformer; + +pub mod png; + +pub struct ImageData { + pub pixels: Vec, + pub width: usize, + pub height: usize, +} + +impl ImageData { + pub fn transform(&self, transformer: &dyn Transformer) -> Self { + transformer.transform(&self) + } +} + +pub trait ImageDecoder { + fn decode(&self, bytes: &[u8]) -> Result; +} diff --git a/src/codec/image/png.rs b/src/codec/image/png.rs new file mode 100644 index 0000000..90ea514 --- /dev/null +++ b/src/codec/image/png.rs @@ -0,0 +1,32 @@ +use alloc::format; +use png_decoder::DecodeError; + +use crate::{color::Color, error::RenderError}; + +use super::{ImageData, ImageDecoder}; + +pub struct PngDecoder; + +impl ImageDecoder for PngDecoder { + fn decode(&self, bytes: &[u8]) -> Result { + let (header, rgba_pixels) = + png_decoder::decode(bytes).map_err(|err| RenderError::from(err))?; + + let pixels = rgba_pixels + .chunks(4) + .map(|chunk| Color::new(chunk[0].into(), chunk[1].into(), chunk[2].into())) + .collect(); + + Ok(ImageData { + width: header.width as usize, + height: header.height as usize, + pixels, + }) + } +} + +impl From for RenderError { + fn from(value: DecodeError) -> Self { + RenderError::new("PngDecoder", &format!("{:?}", value)) + } +} diff --git a/src/codec/mod.rs b/src/codec/mod.rs new file mode 100644 index 0000000..f55d90a --- /dev/null +++ b/src/codec/mod.rs @@ -0,0 +1,3 @@ +pub mod image; +pub mod transform; +pub mod video; diff --git a/src/codec/transform.rs b/src/codec/transform.rs new file mode 100644 index 0000000..6c9fa44 --- /dev/null +++ b/src/codec/transform.rs @@ -0,0 +1,8 @@ +use crate::codec::image::ImageData; + +pub mod crop; +pub mod scale; + +pub trait Transformer { + fn transform(&self, original: &ImageData) -> ImageData; +} diff --git a/src/codec/transform/crop.rs b/src/codec/transform/crop.rs new file mode 100644 index 0000000..660b733 --- /dev/null +++ b/src/codec/transform/crop.rs @@ -0,0 +1,43 @@ +use alloc::vec; + +use crate::{codec::image::ImageData, color::Color}; + +use super::Transformer; + +pub struct CropTransformer { + pub new_resolution: (usize, usize), +} + +impl CropTransformer { + pub fn new(w: usize, h: usize) -> CropTransformer { + CropTransformer { + new_resolution: (w, h), + } + } +} + +impl Transformer for CropTransformer { + fn transform(&self, original: &ImageData) -> ImageData { + let new_resolution = self.new_resolution; + + let new_pixels = (0..new_resolution.0 * new_resolution.1) + .map(|i| { + let x = i % new_resolution.0; + let y = i / new_resolution.0; + + if x < original.width && y < original.height { + Some(original.pixels[y * original.width + x].clone()) + } else { + None + } + }) + .map(|o| o.unwrap_or_else(|| Color::new(0, 0, 0))) + .collect(); + + ImageData { + width: new_resolution.0, + height: new_resolution.1, + pixels: new_pixels, + } + } +} diff --git a/src/codec/transform/scale.rs b/src/codec/transform/scale.rs new file mode 100644 index 0000000..12e63f6 --- /dev/null +++ b/src/codec/transform/scale.rs @@ -0,0 +1,72 @@ +use alloc::vec; +use libm::{floorf, roundf}; + +use crate::{codec::image::ImageData, color::Color}; + +use super::Transformer; + +pub struct ScaleTransformer { + pub scale: f32, +} + +impl ScaleTransformer { + pub fn new(scale: f32) -> Self { + Self { scale } + } +} + +impl Transformer for ScaleTransformer { + fn transform(&self, original: &ImageData) -> ImageData { + let width = original.width; + let height = original.height; + let scale = self.scale; + + let new_width = roundf(width as f32 * scale) as usize; + let new_height = roundf(height as f32 * scale) as usize; + let mut new_pixels = vec![Color { r: 0, g: 0, b: 0 }; new_width * new_height]; + + for y in 0..new_height { + for x in 0..new_width { + let src_x = floorf(x as f32 / scale); + let src_y = floorf(y as f32 / scale); + let fx = (x as f32 / scale) - src_x; + let fy = (y as f32 / scale) - src_y; + + let x0 = src_x as usize; + let y0 = src_y as usize; + let x1 = (x0 + 1).min(width - 1); + let y1 = (y0 + 1).min(height - 1); + + let idx = |x, y| y * width + x; + + let c00 = original.pixels[idx(x0, y0)].clone(); + let c10 = original.pixels[idx(x1, y0)].clone(); + let c01 = original.pixels[idx(x0, y1)].clone(); + let c11 = original.pixels[idx(x1, y1)].clone(); + + let color = bilinear_interpolate(c00, c10, c01, c11, fx, fy); + new_pixels[y * new_width + x] = color; + } + } + + ImageData { + width: new_width, + height: new_height, + pixels: new_pixels, + } + } +} + +fn bilinear_interpolate(c00: Color, c10: Color, c01: Color, c11: Color, tx: f32, ty: f32) -> Color { + let interpolate = |v00, v10, v01, v11| { + let top = (1.0 - tx) * v00 as f32 + tx * v10 as f32; + let bottom = (1.0 - tx) * v01 as f32 + tx * v11 as f32; + roundf((1.0 - ty) * top + ty * bottom).clamp(0.0, 255.0) as u8 + }; + + Color { + r: interpolate(c00.r, c10.r, c01.r, c11.r), + g: interpolate(c00.g, c10.g, c01.g, c11.g), + b: interpolate(c00.b, c10.b, c01.b, c11.b), + } +} diff --git a/src/codec/video.rs b/src/codec/video.rs new file mode 100644 index 0000000..e6423e1 --- /dev/null +++ b/src/codec/video.rs @@ -0,0 +1,39 @@ +use alloc::vec::Vec; + +use crate::{color::Color, error::RenderError, util::time::Milliseconds}; + +use super::{image::ImageData, transform::Transformer}; + +pub mod gif; + +pub struct Frame { + pub image: ImageData, + pub delay: Milliseconds, +} + +pub struct VideoData { + pub width: usize, + pub height: usize, + pub frames: Vec, +} + +impl VideoData { + pub fn transform(&self, transformer: &dyn Transformer) -> Self { + VideoData { + width: self.width, + height: self.height, + frames: self + .frames + .iter() + .map(|f| Frame { + image: f.image.transform(transformer), + delay: f.delay.clone(), + }) + .collect(), + } + } +} + +pub trait VideoDecoder { + fn decode(&self, bytes: &[u8]) -> Result; +} diff --git a/src/codec/video/gif.rs b/src/codec/video/gif.rs new file mode 100644 index 0000000..5cae64c --- /dev/null +++ b/src/codec/video/gif.rs @@ -0,0 +1,95 @@ +use alloc::{format, vec::Vec}; +use embedded_graphics_core::{ + image::ImageDrawable, + pixelcolor::Rgb888, + prelude::{Dimensions, DrawTarget, PixelColor, Point, RgbColor, Size}, + primitives::Rectangle, +}; +use tinygif::ParseError; + +use crate::{codec::image::ImageData, color::Color, error::RenderError, util::time::Milliseconds}; + +use super::{Frame, VideoData, VideoDecoder}; + +pub struct GifDecoder; + +impl VideoDecoder for GifDecoder { + fn decode(&self, bytes: &[u8]) -> Result { + let image = tinygif::Gif::from_slice(bytes).map_err(|e| RenderError::from(e))?; + + let frames = image + .frames() + .map(|f| { + let mut raw = GifData::new(image.width() as usize, image.height() as usize); + f.draw(&mut raw).unwrap(); + + Frame { + image: ImageData { + width: image.width() as usize, + height: image.height() as usize, + pixels: raw.pixels, + }, + delay: Milliseconds((f.delay_centis * 10) as u32), + } + }) + .collect(); + + Ok(VideoData { + width: image.width() as usize, + height: image.height() as usize, + frames, + }) + } +} + +impl From for RenderError { + fn from(value: ParseError) -> Self { + RenderError::new("VideoDecoder", &format!("{:?}", value)) + } +} + +struct GifData { + width: usize, + height: usize, + pixels: Vec, +} + +impl GifData { + pub fn new(width: usize, height: usize) -> GifData { + GifData { + width, + height, + pixels: alloc::vec![Color::new(0, 0, 0); width*height], + } + } +} + +impl Dimensions for GifData { + fn bounding_box(&self) -> embedded_graphics_core::primitives::Rectangle { + Rectangle::new( + Point::new(0, 0), + Size::new(self.width as u32, self.height as u32), + ) + } +} + +impl DrawTarget for GifData { + type Color = Rgb888; + type Error = (); + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for pixel in pixels.into_iter() { + let point = pixel.0; + let color = pixel.1; + + let i = (point.y as usize) * self.width + (point.x as usize); + + self.pixels[i] = Color::new(color.r(), color.g(), color.b()); + } + + Ok(()) + } +} diff --git a/src/color.rs b/src/color.rs new file mode 100644 index 0000000..71da34a --- /dev/null +++ b/src/color.rs @@ -0,0 +1,20 @@ +use uefi::proto::console::gop::BltPixel; + +#[derive(Debug, Clone)] +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, +} + +impl Color { + pub fn new(r: u8, g: u8, b: u8) -> Self { + Self { r, g, b } + } +} + +impl From<&Color> for BltPixel { + fn from(value: &Color) -> Self { + BltPixel::new(value.r, value.g, value.b) + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..06fe9b8 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,15 @@ +use alloc::string::{String, ToString}; + +pub struct RenderError { + ident: String, + message: String, +} + +impl RenderError { + pub fn new(ident: &str, message: &str) -> RenderError { + RenderError { + ident: ident.to_string(), + message: message.to_string(), + } + } +} diff --git a/src/graphic.rs b/src/graphic.rs new file mode 100644 index 0000000..92a83d9 --- /dev/null +++ b/src/graphic.rs @@ -0,0 +1,55 @@ +use alloc::sync::Arc; +use buffer::Buffer; +use spin::Mutex; +use uefi::{ + Result, + boot::{self, ScopedProtocol}, + proto::console::gop::GraphicsOutput, +}; + +use crate::{ + codec::{ + image::ImageData, + transform::{Transformer, crop::CropTransformer}, + video::VideoData, + }, + util::time::Microseconds, +}; + +pub mod buffer; + +pub struct GraphicLoader { + gop: Arc>>>, + resolution: Arc>>, +} + +impl GraphicLoader { + pub fn new() -> Self { + Self { + gop: Arc::new(Mutex::new(None)), + resolution: Arc::new(Mutex::new(None)), + } + } + + pub fn render(&self, data: &ImageData) -> Result { + let gop_handle = boot::get_handle_for_protocol::()?; + let mut gop = boot::open_protocol_exclusive::(gop_handle)?; + + let (width, height) = gop.current_mode_info().resolution(); + + let new_data = data.transform(&CropTransformer::new(width, height)); + + let buffer = Buffer::from(new_data); + + buffer.blit(&mut gop) + } + + pub fn render_video(&self, data: &VideoData) -> Result { + loop { + for frame in data.frames.iter() { + self.render(&frame.image)?; + boot::stall(Microseconds::from(frame.delay.clone()).0 as usize); + } + } + } +} diff --git a/src/graphic/buffer.rs b/src/graphic/buffer.rs new file mode 100644 index 0000000..5add4eb --- /dev/null +++ b/src/graphic/buffer.rs @@ -0,0 +1,58 @@ +use alloc::vec::Vec; +use uefi::{ + Result, + proto::console::gop::{BltOp, BltPixel, BltRegion, GraphicsOutput}, +}; + +use crate::codec::image::ImageData; + +pub struct Buffer { + width: usize, + height: usize, + pixels: Vec, +} + +impl Buffer { + pub fn new(width: usize, height: usize) -> Self { + Buffer { + width, + height, + pixels: alloc::vec![BltPixel::new(255, 255, 0); width * height], + } + } + + pub fn pixel(&mut self, x: usize, y: usize) -> Option<&mut BltPixel> { + self.pixels.get_mut(y * self.width + x) + } + + pub fn blit(&self, gop: &mut GraphicsOutput) -> Result { + gop.blt(BltOp::BufferToVideo { + buffer: &self.pixels, + src: BltRegion::Full, + dest: (0, 0), + dims: (self.width, self.height), + }) + } + + pub fn blit_pixel(&self, gop: &mut GraphicsOutput, coords: (usize, usize)) -> Result { + gop.blt(BltOp::BufferToVideo { + buffer: &self.pixels, + src: BltRegion::SubRectangle { + coords: coords, + px_stride: self.width, + }, + dest: coords, + dims: (1, 1), + }) + } +} + +impl From for Buffer { + fn from(value: ImageData) -> Self { + Buffer { + width: value.width, + height: value.height, + pixels: value.pixels.iter().map(|p| BltPixel::from(p)).collect(), + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9a2d59d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,42 @@ +#![no_main] +#![no_std] + +extern crate alloc; + +use codec::{ + image::{ImageDecoder, png::PngDecoder}, + transform::scale::ScaleTransformer, + video::{VideoDecoder, gif::GifDecoder}, +}; +use graphic::GraphicLoader; +use log::info; +use uefi::{allocator::Allocator, prelude::*}; + +mod codec; +mod color; +mod error; +mod graphic; +mod util; + +#[global_allocator] +static ALLOC: Allocator = Allocator; + +static IMAGE: &[u8] = include_bytes!("../assets/pet.gif"); + +#[entry] +fn main() -> Status { + uefi::helpers::init().unwrap(); + info!("Hello world!"); + + let gl = GraphicLoader::new(); + + let data = GifDecoder.decode(IMAGE); + + if let Ok(data) = data { + let data = data.transform(&ScaleTransformer::new(4.0)); + gl.render_video(&data); + } + + boot::stall(10_000_000); + Status::SUCCESS +} diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000..077885d --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1 @@ +pub mod time; diff --git a/src/util/time.rs b/src/util/time.rs new file mode 100644 index 0000000..e46b2a8 --- /dev/null +++ b/src/util/time.rs @@ -0,0 +1,11 @@ +#[derive(Debug, Clone)] +pub struct Milliseconds(pub u32); + +#[derive(Debug, Clone)] +pub struct Microseconds(pub u32); + +impl From for Microseconds { + fn from(value: Milliseconds) -> Self { + Microseconds(value.0 * 1000) + } +}