diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 18d0e47..7285546 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,7 +11,9 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 25 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 + with: + submodules: 'true' - uses: dtolnay/rust-toolchain@stable - name: cargo-release Cache diff --git a/Cargo.toml b/Cargo.toml index 31ff6cf..0ad56d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libczirw-sys" -version = "0.2.0" +version = "0.2.1" edition = "2024" rust-version = "1.85.1" authors = ["Wim Pomp "] diff --git a/build.rs b/build.rs index f31111a..e2ece49 100644 --- a/build.rs +++ b/build.rs @@ -83,40 +83,40 @@ fn main() -> Result<()> { #[cfg(not(feature = "dynamic"))] { println!( - "cargo:rustc-link-search=native={}", + "cargo::rustc-link-search=native={}", dst.join("build/Src/libCZIAPI").display() ); - println!("cargo:rustc-link-lib=static=libCZIAPIStatic"); + println!("cargo::rustc-link-lib=static=libCZIAPIStatic"); println!( - "cargo:rustc-link-search=native={}", + "cargo::rustc-link-search=native={}", dst.join("build/Src/libCZI").display() ); let profile = env::var("PROFILE")?; match profile.as_str() { - "debug" => println!("cargo:rustc-link-lib=static=libCZIStaticd"), - "release" => println!("cargo:rustc-link-lib=static=libCZIStatic"), + "debug" => println!("cargo::rustc-link-lib=static=libCZIStaticd"), + "release" => println!("cargo::rustc-link-lib=static=libCZIStatic"), _ => return Err(Error::msg(format!("unsupported profile: {}", profile))), } println!( - "cargo:rustc-link-search=native={}", + "cargo::rustc-link-search=native={}", dst.join("lib").display() ); println!( - "cargo:rustc-link-search=native={}", + "cargo::rustc-link-search=native={}", dst.join("lib64").display() ); - println!("cargo:rustc-link-lib=static=zstd"); + println!("cargo::rustc-link-lib=static=zstd"); } #[cfg(feature = "dynamic")] { println!( - "cargo:rustc-link-search=native={}", + "cargo::rustc-link-search=native={}", dst.join("build/Src/libCZIAPI").display() ); - println!("cargo:rustc-link-lib=libCZIAPI"); + println!("cargo::rustc-link-lib=libCZIAPI"); } } println!("cargo::rerun-if-changed=build.rs"); diff --git a/src/functions.rs b/src/functions.rs index 8fc1412..8c93cec 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -196,7 +196,10 @@ impl CziReader { libCZI_ReaderGetPyramidStatistics(**self, ptr.as_mut_ptr()) })?; let ptr = unsafe { ptr.assume_init() }; - let statistics = unsafe { CStr::from_ptr(ptr) }.to_str()?.to_owned(); + assert!(!ptr.is_null()); + let statistics = unsafe { CStr::from_ptr(ptr) } + .to_string_lossy() + .into_owned(); unsafe { libCZI_Free(ptr as *mut c_void) }; Ok(statistics) } @@ -653,7 +656,7 @@ impl LockedBitmap { ***self, width, height, - pixel_type.into(), + pixel_type as i32, stride, data.as_mut_ptr() as *mut c_void, ) @@ -733,7 +736,10 @@ impl CziDocumentInfo { ) })?; let ptr = unsafe { ptr.assume_init() }; - let info = unsafe { CStr::from_ptr(ptr) }.to_str()?.to_owned(); + assert!(!ptr.is_null()); + let info = unsafe { CStr::from_ptr(ptr) } + .to_string_lossy() + .into_owned(); unsafe { libCZI_Free(ptr as *mut c_void) }; Ok(info) } @@ -782,9 +788,10 @@ impl CziDocumentInfo { ) })?; let ptr = unsafe { ptr.assume_init() }; - let info = unsafe { CStr::from_ptr(ptr) }.to_str()?.to_owned(); - unsafe { libCZI_Free(ptr as *mut c_void) }; - Ok(info) + assert!(!ptr.is_null()); + Ok(unsafe { CStr::from_ptr(ptr) } + .to_string_lossy() + .into_owned()) } /// Release the specified CZI-document-info object. diff --git a/src/interop.rs b/src/interop.rs index 66d7f07..3f7f053 100644 --- a/src/interop.rs +++ b/src/interop.rs @@ -3,6 +3,8 @@ use crate::misc::{PixelType, Ptr}; use crate::sys::*; use anyhow::{Error, Result}; use std::ffi::{CStr, CString, c_char, c_void}; +use std::fmt::Debug; +use std::mem; use std::mem::{ManuallyDrop, MaybeUninit}; /// This struct contains the version information of the libCZIApi-library. For versioning libCZI, SemVer2 () is used. @@ -597,7 +599,7 @@ impl BitmapInfo { Self(BitmapInfoInterop { width, height, - pixelType: pixel_type.into(), + pixelType: pixel_type as i32, }) } pub fn get_width(&self) -> u32 { @@ -616,19 +618,22 @@ impl BitmapInfo { self.0.height = height; } pub fn set_pixel_type(&mut self, pixel_type: PixelType) { - self.0.pixelType = pixel_type.into(); + self.0.pixelType = pixel_type as i32; } } impl BitmapLockInfo { pub fn get_data_roi(&self) -> Vec { - unsafe { + let vec = unsafe { Vec::from_raw_parts( self.0.ptrDataRoi as *mut u8, self.0.size as usize, self.0.size as usize, ) - } + }; + let res = vec.clone(); + mem::forget(vec); + res } pub fn get_stride(&self) -> u32 { @@ -651,7 +656,7 @@ impl SubBlockInfo { ) -> Self { Self(SubBlockInfoInterop { compression_mode_raw, - pixel_type: pixel_type.into(), + pixel_type: pixel_type as i32, coordinate: coordinate.0, logical_rect: logical_rect.0, physical_size: physical_size.0, @@ -680,7 +685,7 @@ impl SubBlockInfo { self.0.compression_mode_raw = compression_mode_raw } pub fn set_pixel_type(&mut self, pixel_type: PixelType) { - self.0.pixel_type = pixel_type.into(); + self.0.pixel_type = pixel_type as i32; } pub fn set_coordinate(&mut self, coordinate: Coordinate) { self.0.coordinate = coordinate.0 @@ -790,7 +795,7 @@ impl AddSubBlockInfo { logical_height, physical_width, physical_height, - pixel_type: pixel_type.into(), + pixel_type: pixel_type as i32, compression_mode_raw, size_data: data.len() as u32, data: data.as_ptr() as *const c_void, @@ -898,7 +903,7 @@ impl AddSubBlockInfo { self.0.physical_height = physical_height } pub fn set_pixel_type(&mut self, pixel_type: PixelType) { - self.0.pixel_type = pixel_type.into() + self.0.pixel_type = pixel_type as i32 } pub fn set_compression_mode_raw(&mut self, compression_mode_raw: i32) { self.0.compression_mode_raw = compression_mode_raw diff --git a/src/lib.rs b/src/lib.rs index f5621ea..9ef59a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,16 +9,71 @@ pub mod sys; pub use functions::*; pub use handle::*; pub use interop::*; -pub use misc::{LibCZIApiError, PixelType, RawDataType}; +pub use misc::{Dimension, LibCZIApiError, PixelType, RawDataType}; #[cfg(test)] mod tests { use crate::handle::{CziReader, InputStream}; use crate::interop::{LibCZIBuildInformation, ReaderOpenInfo}; + use crate::misc::Dimension; use anyhow::{Error, Result}; use std::env; use std::path::PathBuf; + #[test] + fn test_read_shape() -> Result<()> { + let path = env::home_dir() + .unwrap() + .join("code/rust/ndbioimage/tests/files/Experiment-2029.czi"); + assert!(path.exists()); + let czi = CziReader::create()?; + let stream = InputStream::create_from_file_utf8( + path.to_str().ok_or(Error::msg("cannot into str"))?, + )?; + let open_info = ReaderOpenInfo::new(&stream); + czi.open(open_info)?; + println!("pyramid statistics: {:?}", czi.get_pyramid_statistics()?); + println!("file header info: {:?}", czi.get_file_header_info()?); + let statistics_simple = czi.get_statistics_simple()?; + println!("statistics simple: {:?}", czi.get_statistics_simple()?); + let bounding_box = statistics_simple.get_bounding_box(); + let dim_bounds = statistics_simple.get_dim_bounds(); + let dimensions = Dimension::vec_from_bitflags(dim_bounds.get_dimensions_valid()); + let size = dim_bounds.get_size(); + for (i, d) in dimensions.iter().enumerate() { + println!("{:?}: {}", d, size[i]); + } + println!("X: {}", bounding_box.get_w()); + println!("Y: {}", bounding_box.get_h()); + Ok(()) + } + + #[test] + fn test_read_bytes() -> Result<()> { + let path = env::home_dir() + .unwrap() + .join("code/rust/ndbioimage/tests/files/Experiment-2029.czi"); + assert!(path.exists()); + let czi = CziReader::create()?; + let stream = InputStream::create_from_file_utf8( + path.to_str().ok_or(Error::msg("cannot into str"))?, + )?; + let open_info = ReaderOpenInfo::new(&stream); + czi.open(open_info)?; + let sub_block = czi.read_sub_block(0)?; + let bitmap = sub_block.create_bitmap()?.lock()?; + let bitmap_info = bitmap.get_info()?; + println!( + "height: {}, width: {} pixel type: {:#?}", + bitmap_info.get_height(), + bitmap_info.get_width(), + bitmap_info.get_pixel_type()? + ); + let bytes = bitmap.lock_info.get_data_roi(); + println!("bytes: {:?}", bytes.as_slice()[..100].to_vec()); + Ok(()) + } + #[test] fn test_libczi_xml() -> Result<()> { let path = env::home_dir() diff --git a/src/misc.rs b/src/misc.rs index b475148..c61f117 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -40,25 +40,94 @@ impl fmt::Display for LibCZIApiError { } } +#[derive(Clone, Debug)] +pub enum Dimension { + /// The Z-dimension. + Z = 1, + /// The C-dimension ("channel"). + C = 2, + /// The T-dimension ("time"). + T = 3, + /// The R-dimension ("rotation"). + R = 4, + /// The S-dimension ("scene"). + S = 5, + /// The I-dimension ("illumination"). + I = 6, + /// The H-dimension ("phase"). + H = 7, + /// The V-dimension ("view"). + V = 8, + /// The B-dimension ("block") - its use is deprecated. + B = 9, +} + +impl Dimension { + pub fn vec_from_bitflags(bit_flags: u32) -> Vec { + let mut bit_flags = bit_flags; + let mut dimensions = Vec::with_capacity(9); + for i in 1..=9 { + if (bit_flags & 1) > 0 { + dimensions.push(Dimension::try_from(i).expect("i must be 0 <= i <= 9")); + } + bit_flags >>= 1; + } + dimensions + } +} + +impl TryFrom for Dimension { + type Error = Error; + + fn try_from(dimension: i32) -> Result { + match dimension { + 1 => Ok(Dimension::Z), + 2 => Ok(Dimension::C), + 3 => Ok(Dimension::T), + 4 => Ok(Dimension::R), + 5 => Ok(Dimension::S), + 6 => Ok(Dimension::I), + 7 => Ok(Dimension::H), + 8 => Ok(Dimension::V), + 9 => Ok(Dimension::B), + _ => Err(anyhow!("Unknown dimension value {}", dimension)), + } + } +} + /// enum for SubBlock.get_raw_data +#[derive(Clone, Debug)] pub enum RawDataType { - Data, - Metadata, + Data = 0, + Metadata = 1, +} + +impl TryFrom for RawDataType { + type Error = Error; + + fn try_from(raw_data_type: i32) -> Result { + match raw_data_type { + 0 => Ok(RawDataType::Data), + 1 => Ok(RawDataType::Metadata), + _ => Err(anyhow!("Unknown data type {}", raw_data_type)), + } + } } /// pixel type +#[derive(Clone, Debug)] pub enum PixelType { - Gray8, - Gray16, - Gray32Float, - Bgr24, - Bgr48, - Bgr96Float, - Bgra32, - Gray64ComplexFloat, - Bgr192ComplexFloat, - Gray32, - Gray64Float, + Gray8 = 0, + Gray16 = 1, + Gray32Float = 2, + Bgr24 = 3, + Bgr48 = 4, + Bgr96Float = 8, + Bgra32 = 9, + Gray64ComplexFloat = 10, + Bgr192ComplexFloat = 11, + Gray32 = 12, + Gray64Float = 13, } impl TryFrom for PixelType { @@ -82,24 +151,6 @@ impl TryFrom for PixelType { } } -impl From for i32 { - fn from(pixel_type: PixelType) -> Self { - match pixel_type { - PixelType::Gray8 => 0, - PixelType::Gray16 => 1, - PixelType::Gray32Float => 2, - PixelType::Bgr24 => 3, - PixelType::Bgr48 => 4, - PixelType::Bgr96Float => 8, - PixelType::Bgra32 => 9, - PixelType::Gray64ComplexFloat => 10, - PixelType::Bgr192ComplexFloat => 11, - PixelType::Gray32 => 12, - PixelType::Gray64Float => 13, - } - } -} - pub trait Ptr { type Pointer;