diff --git a/Cargo.lock b/Cargo.lock index ba1f8932..014c3f1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,6 +95,12 @@ dependencies = [ "which", ] +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -107,6 +113,18 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bytemuck" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cargo-ledger" version = "1.5.0" @@ -235,6 +253,37 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "either" version = "1.12.0" @@ -261,6 +310,31 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + [[package]] name = "flate2" version = "1.0.30" @@ -271,11 +345,20 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + [[package]] name = "gif" -version = "0.11.4" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" dependencies = [ "color_quant", "weezl", @@ -298,6 +381,16 @@ dependencies = [ "scroll", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "heck" version = "0.5.0" @@ -313,12 +406,30 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-traits", + "png", + "qoi", + "tiff", +] + [[package]] name = "include_gif" version = "1.1.0" dependencies = [ "flate2", - "gif", + "image", "syn 1.0.109", ] @@ -334,6 +445,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +dependencies = [ + "rayon", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -346,9 +466,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "ledger_device_sdk" -version = "1.12.0" +version = "1.13.0" dependencies = [ "const-zero", "include_gif", @@ -400,6 +526,16 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[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.21" @@ -425,6 +561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", + "simd-adler32", ] [[package]] @@ -470,6 +607,19 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "prettyplease" version = "0.2.20" @@ -489,6 +639,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quote" version = "1.0.36" @@ -504,6 +663,26 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex" version = "1.10.4" @@ -558,6 +737,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "scroll" version = "0.10.2" @@ -631,6 +816,27 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "strsim" version = "0.11.1" @@ -667,6 +873,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -775,3 +992,12 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/include_gif/Cargo.toml b/include_gif/Cargo.toml index cf001dca..64f94715 100644 --- a/include_gif/Cargo.toml +++ b/include_gif/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "include_gif" -version = "1.1.0" +version = "1.2.0" edition = "2021" license.workspace = true repository.workspace = true @@ -11,5 +11,5 @@ proc-macro = true [dependencies] syn = { version = "1.0", features = ["full"] } -gif = "0.11.3" flate2 = "1.0.28" +image = "=0.24.9" diff --git a/include_gif/src/lib.rs b/include_gif/src/lib.rs index fa692779..1877b825 100644 --- a/include_gif/src/lib.rs +++ b/include_gif/src/lib.rs @@ -1,8 +1,8 @@ extern crate proc_macro; +use image::*; use proc_macro::TokenStream; -use std::collections::HashSet; -use std::fs::File; +use std::collections::HashMap; use std::io::Write; use syn::{parse_macro_input, Ident, LitStr}; @@ -56,38 +56,36 @@ pub fn include_gif(input: TokenStream) -> TokenStream { } fn generate_glyph(filename: LitStr, glyph_type: GlyphType) -> TokenStream { - let filename = filename.value(); - let mut decoder = gif::DecodeOptions::new(); - decoder.set_color_output(gif::ColorOutput::Indexed); - let path = format!( "{}/{}", std::env::var("CARGO_MANIFEST_DIR").unwrap(), - filename + filename.value() ); - let file = File::open(path).unwrap(); - let mut decoder = decoder.read_info(file).unwrap(); - - let frame = decoder.read_next_frame().unwrap().unwrap().clone(); - let palette = decoder.palette().unwrap(); + let grayscale_image: GrayImage = open(path).unwrap().to_luma8(); let mut vec_output = Vec::new(); match glyph_type { GlyphType::Bagl => { - let packed = generate_bagl_glyph(&frame, &palette); + let packed = generate_bagl_glyph(&grayscale_image); write!( &mut vec_output, "(&{:?}, {}, {})", - packed, frame.width, frame.height + packed, + grayscale_image.width(), + grayscale_image.height() ) .unwrap(); } GlyphType::Nbgl => { - let (compressed_buffer, bpp) = generate_nbgl_glyph(&frame, &palette); + let (compressed_buffer, bpp) = generate_nbgl_glyph(&grayscale_image); write!( &mut vec_output, "(&{:?}, {}, {}, {}, {})", - compressed_buffer, frame.width, frame.height, bpp, true + compressed_buffer, + grayscale_image.width(), + grayscale_image.height(), + bpp, + true ) .unwrap(); } @@ -97,34 +95,65 @@ fn generate_glyph(filename: LitStr, glyph_type: GlyphType) -> TokenStream { stream_output.parse().unwrap() } -fn generate_bagl_glyph(frame: &gif::Frame, palette: &[u8]) -> Vec { - let dimensions = frame.width * frame.height; - let (size, remainder) = ((dimensions / 8) as usize, (dimensions % 8) as usize); - - let mut packed = Vec::new(); - for i in 0..size { +// Convert a frame into a bagl glyph : pack 8 pixels in a single byte. +// Each pixel is 1 bit, 0 for black, 1 for white. +fn generate_bagl_glyph(frame: &GrayImage) -> Vec { + let width = frame.width() as usize; + let height = frame.height() as usize; + // Number of pixels to be packed into bytes + let size = width * height; + let mut packed = Vec::with_capacity(size / 8); + // Main loop, run through all pixels in the frame, by groups of 8 + for i in 0..size / 8 { let mut byte = 0; for j in 0..8 { - let color = (palette[frame.buffer[8 * i + j] as usize * 3] != 0) as u8; + // Compute linear index + let idx = 8 * i + j; + // Get x and y coordinates from linear index + // Remainder of the division by width tells us how far we are on the x axis. + let x = idx % width; + // Integer division by width tells us how far we are on the y axis. + let y = idx / width; + let pixel = frame.get_pixel(x as u32, y as u32); + // If pixel is not black (0), set the corresponding bit in the byte. + let color = (pixel[0] != 0) as u8; + // Set the j-th bit of the byte to the color of the pixel. byte |= color << j; } packed.push(byte); } - let mut byte = 0; - for j in 0..remainder { - let color = (palette[frame.buffer[8 * size + j] as usize * 3] != 0) as u8; - byte |= color << j; + // Remainder handling + let remainder = size % 8; + if remainder != 0 { + let mut byte = 0; + for j in 0..remainder { + let x = (8 * (size / 8) + j) % width; + let y = (8 * (size / 8) + j) / width; + let pixel = frame.get_pixel(x as u32, y as u32); + let color = (pixel[0] != 0) as u8; + byte |= color << j; + } + packed.push(byte); } - packed.push(byte); packed } -fn image_to_packed_buffer(frame: &gif::Frame, palette: &[u8]) -> (Vec, u8) { - let mut colors = palette.iter().collect::>().len() as u8; +// Get the palette of colors of a grayscale image +fn get_palette<'a>(img: &'a GrayImage) -> Vec { + let mut palette = HashMap::new(); + // Count the number of occurrences of each color + for &pixel in img.pixels() { + *palette.entry(pixel[0]).or_insert(0) += 1; + } + let palette: Vec<_> = palette.into_iter().collect(); + // Collect all colors in a vector + palette.into_iter().map(|(luma, _)| luma).collect() +} - // Exit/Panic if number of colors > 16 +fn image_to_packed_buffer(frame: &GrayImage) -> (Vec, u8) { + let mut colors = get_palette(&frame).len() as u8; if colors > 16 { - panic!("Image has more than 16 colors"); + colors = 16; } // Round number of colors to a power of 2 if !(colors != 0 && colors.count_ones() == 1) { @@ -138,8 +167,8 @@ fn image_to_packed_buffer(frame: &gif::Frame, palette: &[u8]) -> (Vec, u8) { _ => (), } - let width = frame.width; - let height = frame.height; + let width = frame.width(); + let height = frame.height(); let base_threshold = (256 / colors as u32) as u8; let half_threshold = base_threshold / 2; let mut current_byte = 0 as u16; @@ -148,8 +177,7 @@ fn image_to_packed_buffer(frame: &gif::Frame, palette: &[u8]) -> (Vec, u8) { for x in (0..width).rev() { for y in 0..height { - let pixel_index = ((y * width) + x) as usize; - let mut color: u16 = palette[frame.buffer[pixel_index] as usize * 3] as u16; + let mut color: u16 = frame.get_pixel(x, y)[0] as u16; color = (color + half_threshold as u16) / base_threshold as u16; if color >= colors as u16 { color = colors as u16 - 1; @@ -169,8 +197,8 @@ fn image_to_packed_buffer(frame: &gif::Frame, palette: &[u8]) -> (Vec, u8) { (packed, bits_per_pixel) } -fn generate_nbgl_glyph(frame: &gif::Frame, palette: &[u8]) -> (Vec, u8) { - let (packed, bpp) = image_to_packed_buffer(&frame, &palette); +fn generate_nbgl_glyph(frame: &GrayImage) -> (Vec, u8) { + let (packed, bpp) = image_to_packed_buffer(&frame); let mut compressed_image: Vec = Vec::new(); let mut full_uncompressed_size = packed.len(); @@ -206,10 +234,10 @@ fn generate_nbgl_glyph(frame: &gif::Frame, palette: &[u8]) -> (Vec, u8) { let len = compressed_image.len(); let metadata: [u8; 8] = [ - frame.width as u8, - (frame.width >> 8) as u8, - frame.height as u8, - (frame.height >> 8) as u8, + frame.width() as u8, + (frame.width() >> 8) as u8, + frame.height() as u8, + (frame.height() >> 8) as u8, bpp_format << 4 | 1, // 1 is gzip compression type. We only support gzip. len as u8, (len >> 8) as u8, diff --git a/ledger_device_sdk/Cargo.toml b/ledger_device_sdk/Cargo.toml index 769c3969..13ef6291 100644 --- a/ledger_device_sdk/Cargo.toml +++ b/ledger_device_sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ledger_device_sdk" -version = "1.13.0" +version = "1.13.1" authors = ["yhql", "yogh333", "agrojean-ledger", "kingofpayne"] edition = "2021" license.workspace = true @@ -15,7 +15,7 @@ ledger_device_sdk = { path = ".", features = ["speculos"] } testmacro = { path = "../testmacro", version = "0.1.0"} [dependencies] -include_gif = {path = "../include_gif", version = "1.1.0"} +include_gif = {path = "../include_gif", version = "1.2.0"} num-traits = { version = "0.2.14", default_features = false } rand_core = { version = "0.6.3", default_features = false } zeroize = { version = "1.6.0", default_features = false }