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