initial commit
This commit is contained in:
387
Cargo.lock
generated
Normal file
387
Cargo.lock
generated
Normal file
@@ -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"
|
||||
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@@ -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"] }
|
||||
23
src/codec/image.rs
Normal file
23
src/codec/image.rs
Normal file
@@ -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<Color>,
|
||||
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<ImageData, RenderError>;
|
||||
}
|
||||
32
src/codec/image/png.rs
Normal file
32
src/codec/image/png.rs
Normal file
@@ -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<super::ImageData, RenderError> {
|
||||
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<DecodeError> for RenderError {
|
||||
fn from(value: DecodeError) -> Self {
|
||||
RenderError::new("PngDecoder", &format!("{:?}", value))
|
||||
}
|
||||
}
|
||||
3
src/codec/mod.rs
Normal file
3
src/codec/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod image;
|
||||
pub mod transform;
|
||||
pub mod video;
|
||||
8
src/codec/transform.rs
Normal file
8
src/codec/transform.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use crate::codec::image::ImageData;
|
||||
|
||||
pub mod crop;
|
||||
pub mod scale;
|
||||
|
||||
pub trait Transformer {
|
||||
fn transform(&self, original: &ImageData) -> ImageData;
|
||||
}
|
||||
43
src/codec/transform/crop.rs
Normal file
43
src/codec/transform/crop.rs
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
72
src/codec/transform/scale.rs
Normal file
72
src/codec/transform/scale.rs
Normal file
@@ -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),
|
||||
}
|
||||
}
|
||||
39
src/codec/video.rs
Normal file
39
src/codec/video.rs
Normal file
@@ -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<Frame>,
|
||||
}
|
||||
|
||||
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<VideoData, RenderError>;
|
||||
}
|
||||
95
src/codec/video/gif.rs
Normal file
95
src/codec/video/gif.rs
Normal file
@@ -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<super::VideoData, RenderError> {
|
||||
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<ParseError> for RenderError {
|
||||
fn from(value: ParseError) -> Self {
|
||||
RenderError::new("VideoDecoder", &format!("{:?}", value))
|
||||
}
|
||||
}
|
||||
|
||||
struct GifData {
|
||||
width: usize,
|
||||
height: usize,
|
||||
pixels: Vec<Color>,
|
||||
}
|
||||
|
||||
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<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
||||
where
|
||||
I: IntoIterator<Item = embedded_graphics_core::Pixel<Self::Color>>,
|
||||
{
|
||||
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(())
|
||||
}
|
||||
}
|
||||
20
src/color.rs
Normal file
20
src/color.rs
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
15
src/error.rs
Normal file
15
src/error.rs
Normal file
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
55
src/graphic.rs
Normal file
55
src/graphic.rs
Normal file
@@ -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<Mutex<Option<ScopedProtocol<GraphicsOutput>>>>,
|
||||
resolution: Arc<Mutex<Option<(usize, usize)>>>,
|
||||
}
|
||||
|
||||
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::<GraphicsOutput>()?;
|
||||
let mut gop = boot::open_protocol_exclusive::<GraphicsOutput>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/graphic/buffer.rs
Normal file
58
src/graphic/buffer.rs
Normal file
@@ -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<BltPixel>,
|
||||
}
|
||||
|
||||
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<ImageData> for Buffer {
|
||||
fn from(value: ImageData) -> Self {
|
||||
Buffer {
|
||||
width: value.width,
|
||||
height: value.height,
|
||||
pixels: value.pixels.iter().map(|p| BltPixel::from(p)).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
42
src/main.rs
Normal file
42
src/main.rs
Normal file
@@ -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
|
||||
}
|
||||
1
src/util/mod.rs
Normal file
1
src/util/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod time;
|
||||
11
src/util/time.rs
Normal file
11
src/util/time.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Milliseconds(pub u32);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Microseconds(pub u32);
|
||||
|
||||
impl From<Milliseconds> for Microseconds {
|
||||
fn from(value: Milliseconds) -> Self {
|
||||
Microseconds(value.0 * 1000)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user