From bc54ee33fda22306dc0e32e61c147f8a11a62ea6 Mon Sep 17 00:00:00 2001 From: Joshua Simmons Date: Sat, 17 Sep 2022 12:04:27 +0200 Subject: [PATCH] Add basic safe wrapper for stb_image --- Cargo.lock | 1 + ffi/stb_image-sys/src/lib.rs | 86 ++++++++++++++--------------- narcissus-core/Cargo.toml | 3 +- narcissus-core/src/image.rs | 103 +++++++++++++++++++++++++++++++++++ narcissus-core/src/lib.rs | 2 + 5 files changed, 151 insertions(+), 44 deletions(-) create mode 100644 narcissus-core/src/image.rs diff --git a/Cargo.lock b/Cargo.lock index b9e260c..d3f1647 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,7 @@ dependencies = [ name = "narcissus-core" version = "0.1.0" dependencies = [ + "stb_image-sys", ] [[package]] diff --git a/ffi/stb_image-sys/src/lib.rs b/ffi/stb_image-sys/src/lib.rs index ef08dd6..ad2c0e4 100644 --- a/ffi/stb_image-sys/src/lib.rs +++ b/ffi/stb_image-sys/src/lib.rs @@ -24,68 +24,68 @@ extern "C" { pub fn stbi_load_from_memory( buffer: *const c_uchar, len: c_int, - x: *mut c_int, - y: *mut c_int, - comp: *mut c_int, + x: &mut c_int, + y: &mut c_int, + comp: &mut c_int, req_comp: c_int, ) -> *mut c_uchar; pub fn stbi_load( filename: *const c_char, - x: *mut c_int, - y: *mut c_int, - comp: *mut c_int, + x: &mut c_int, + y: &mut c_int, + comp: &mut c_int, req_comp: c_int, ) -> *mut c_uchar; pub fn stbi_load_from_file( f: *mut libc::FILE, - x: *mut c_int, - y: *mut c_int, - comp: *mut c_int, + x: &mut c_int, + y: &mut c_int, + comp: &mut c_int, req_comp: c_int, ) -> *mut c_uchar; pub fn stbi_load_from_callbacks( - clbk: *const stbi_io_callbacks, + clbk: &stbi_io_callbacks, user: *mut c_void, - x: *mut c_int, - y: *mut c_int, - comp: *mut c_int, + x: &mut c_int, + y: &mut c_int, + comp: &mut c_int, req_comp: c_int, ) -> *mut c_uchar; pub fn stbi_loadf_from_memory( buffer: *const c_uchar, len: c_int, - x: *mut c_int, - y: *mut c_int, - comp: *mut c_int, + x: &mut c_int, + y: &mut c_int, + comp: &mut c_int, req_comp: c_int, ) -> *mut c_float; pub fn stbi_loadf( filename: *const c_char, - x: *mut c_int, - y: *mut c_int, - comp: *mut c_int, + x: &mut c_int, + y: &mut c_int, + comp: &mut c_int, req_comp: c_int, ) -> *mut c_float; pub fn stbi_loadf_from_file( f: *mut libc::FILE, - x: *mut c_int, - y: *mut c_int, - comp: *mut c_int, + x: &mut c_int, + y: &mut c_int, + comp: &mut c_int, req_comp: c_int, ) -> *mut c_float; pub fn stbi_loadf_from_callbacks( - clbk: *const stbi_io_callbacks, + clbk: &stbi_io_callbacks, user: *mut c_void, - x: *mut c_int, - y: *mut c_int, - comp: *mut c_int, + x: &mut c_int, + y: &mut c_int, + comp: &mut c_int, req_comp: c_int, ) -> *mut c_float; @@ -97,7 +97,7 @@ extern "C" { pub fn stbi_ldr_to_hdr_scale(scale: c_float); - pub fn stbi_is_hdr_from_callbacks(clbk: *const stbi_io_callbacks, user: *mut c_void) -> c_int; + pub fn stbi_is_hdr_from_callbacks(clbk: &stbi_io_callbacks, user: *mut c_void) -> c_int; pub fn stbi_is_hdr_from_memory(buffer: *const c_uchar, len: c_int) -> c_int; @@ -112,31 +112,31 @@ extern "C" { pub fn stbi_info_from_memory( buffer: *const c_uchar, len: c_int, - x: *mut c_int, - y: *mut c_int, - comp: *mut c_int, + x: &mut c_int, + y: &mut c_int, + comp: &mut c_int, ) -> c_int; pub fn stbi_info_from_callbacks( - clbk: *const stbi_io_callbacks, + clbk: &stbi_io_callbacks, user: *mut c_void, - x: *mut c_int, - y: *mut c_int, - comp: *mut c_int, + x: &mut c_int, + y: &mut c_int, + comp: &mut c_int, ) -> c_int; pub fn stbi_info( filename: *const c_char, - x: *mut c_int, - y: *mut c_int, - comp: *mut c_int, + x: &mut c_int, + y: &mut c_int, + comp: &mut c_int, ) -> c_int; pub fn stbi_info_from_file( f: *mut libc::FILE, - x: *mut c_int, - y: *mut c_int, - comp: *mut c_int, + x: &mut c_int, + y: &mut c_int, + comp: &mut c_int, ) -> c_int; pub fn stbi_set_unpremultiply_on_load(flag_true_if_should_unpremultiply: c_int); @@ -147,13 +147,13 @@ extern "C" { buffer: *const c_char, len: c_int, initial_size: c_int, - outlen: *mut c_int, + outlen: &mut c_int, ) -> *mut c_char; pub fn stbi_zlib_decode_malloc( buffer: *const c_char, len: c_int, - outlen: *mut c_int, + outlen: &mut c_int, ) -> *mut c_char; pub fn stbi_zlib_decode_buffer( @@ -166,7 +166,7 @@ extern "C" { pub fn stbi_zlib_decode_noheader_malloc( buffer: *const c_char, len: c_int, - outlen: *mut c_int, + outlen: &mut c_int, ) -> *mut c_char; pub fn stbi_zlib_decode_noheader_buffer( diff --git a/narcissus-core/Cargo.toml b/narcissus-core/Cargo.toml index cd14ed5..bc3c075 100644 --- a/narcissus-core/Cargo.toml +++ b/narcissus-core/Cargo.toml @@ -5,4 +5,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] \ No newline at end of file +[dependencies] +stb_image-sys = { path = "../ffi/stb_image-sys" } \ No newline at end of file diff --git a/narcissus-core/src/image.rs b/narcissus-core/src/image.rs new file mode 100644 index 0000000..cd16d36 --- /dev/null +++ b/narcissus-core/src/image.rs @@ -0,0 +1,103 @@ +use std::ptr::NonNull; + +use stb_image_sys::{stbi_image_free, stbi_load_from_memory}; + +#[derive(Debug)] +pub struct LoadError; + +impl std::fmt::Display for LoadError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LoadError").finish() + } +} + +impl std::error::Error for LoadError {} + +pub struct Image { + width: usize, + height: usize, + components: usize, + len: usize, + buffer: NonNull, +} + +impl Image { + pub fn from_buffer(buffer: &[u8]) -> Result { + let mut x = 0; + let mut y = 0; + let mut components = 0; + let required_components = 0; + let buffer = unsafe { + stbi_load_from_memory( + buffer.as_ptr(), + buffer.len() as i32, + &mut x, + &mut y, + &mut components, + required_components, + ) + }; + + if buffer.is_null() { + return Err(LoadError); + } + + let x = x as usize; + let y = y as usize; + let components = components as usize; + let len = x * y * components; + + Ok(Image { + width: x, + height: y, + components, + len, + // Safety: We just checked that buffer is not null above. + buffer: unsafe { NonNull::new_unchecked(buffer) }, + }) + } + + /// Returns the image's width in pixels. + #[inline] + pub fn width(&self) -> usize { + self.width + } + + /// Returns the image's height in pixels. + #[inline] + pub fn height(&self) -> usize { + self.height + } + + /// Returns the number of components in this image. + #[inline] + pub fn components(&self) -> usize { + self.components + } + + /// The pixel data consists of [`height()`] scanlines of [`width()`] pixels, + /// with each pixel consisting of [`components()`] interleaved 8-bit components; the first + /// pixel pointed to is top-left-most in the image. There is no padding between + /// image scanlines or between pixels, regardless of format. + /// + /// An output image with N components has the following components interleaved + /// in this order in each pixel: + /// + /// | N | Components | + /// |----|-------------------------| + /// | 1 | grey | + /// | 2 | grey, alpha | + /// | 3 | red, green, blue | + /// | 4 | red, green, blue, alpha | + pub fn as_slice(&self) -> &[u8] { + // Safety: Slice size is calculated when creating `Image`. + unsafe { std::slice::from_raw_parts(self.buffer.as_ptr(), self.len) } + } +} + +impl Drop for Image { + fn drop(&mut self) { + // Safety: Always allocated by `stbi_load_xxx` functions. + unsafe { stbi_image_free(self.buffer.as_ptr() as *mut _) } + } +} diff --git a/narcissus-core/src/lib.rs b/narcissus-core/src/lib.rs index c20a0af..e0fef02 100644 --- a/narcissus-core/src/lib.rs +++ b/narcissus-core/src/lib.rs @@ -1,5 +1,6 @@ mod bitset; mod fixed_vec; +mod image; mod libc; pub mod manual_arc; mod mutex; @@ -12,6 +13,7 @@ mod waiter; pub use bitset::BitIter; pub use fixed_vec::FixedVec; +pub use image::Image; pub use mutex::Mutex; pub use pool::{Handle, Pool}; pub use ref_count::{Arc, Rc}; -- 2.49.0