some more test and fixes to get them working

This commit is contained in:
Wim Pomp
2025-08-14 19:29:43 +02:00
parent e902eebd2b
commit d4899e275b
7 changed files with 178 additions and 58 deletions

View File

@@ -11,7 +11,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 25 timeout-minutes: 25
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with:
submodules: 'true'
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- name: cargo-release Cache - name: cargo-release Cache

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "libczirw-sys" name = "libczirw-sys"
version = "0.2.0" version = "0.2.1"
edition = "2024" edition = "2024"
rust-version = "1.85.1" rust-version = "1.85.1"
authors = ["Wim Pomp <w.pomp@nki.nl>"] authors = ["Wim Pomp <w.pomp@nki.nl>"]

View File

@@ -83,40 +83,40 @@ fn main() -> Result<()> {
#[cfg(not(feature = "dynamic"))] #[cfg(not(feature = "dynamic"))]
{ {
println!( println!(
"cargo:rustc-link-search=native={}", "cargo::rustc-link-search=native={}",
dst.join("build/Src/libCZIAPI").display() dst.join("build/Src/libCZIAPI").display()
); );
println!("cargo:rustc-link-lib=static=libCZIAPIStatic"); println!("cargo::rustc-link-lib=static=libCZIAPIStatic");
println!( println!(
"cargo:rustc-link-search=native={}", "cargo::rustc-link-search=native={}",
dst.join("build/Src/libCZI").display() dst.join("build/Src/libCZI").display()
); );
let profile = env::var("PROFILE")?; let profile = env::var("PROFILE")?;
match profile.as_str() { match profile.as_str() {
"debug" => println!("cargo:rustc-link-lib=static=libCZIStaticd"), "debug" => println!("cargo::rustc-link-lib=static=libCZIStaticd"),
"release" => println!("cargo:rustc-link-lib=static=libCZIStatic"), "release" => println!("cargo::rustc-link-lib=static=libCZIStatic"),
_ => return Err(Error::msg(format!("unsupported profile: {}", profile))), _ => return Err(Error::msg(format!("unsupported profile: {}", profile))),
} }
println!( println!(
"cargo:rustc-link-search=native={}", "cargo::rustc-link-search=native={}",
dst.join("lib").display() dst.join("lib").display()
); );
println!( println!(
"cargo:rustc-link-search=native={}", "cargo::rustc-link-search=native={}",
dst.join("lib64").display() dst.join("lib64").display()
); );
println!("cargo:rustc-link-lib=static=zstd"); println!("cargo::rustc-link-lib=static=zstd");
} }
#[cfg(feature = "dynamic")] #[cfg(feature = "dynamic")]
{ {
println!( println!(
"cargo:rustc-link-search=native={}", "cargo::rustc-link-search=native={}",
dst.join("build/Src/libCZIAPI").display() 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"); println!("cargo::rerun-if-changed=build.rs");

View File

@@ -196,7 +196,10 @@ impl CziReader {
libCZI_ReaderGetPyramidStatistics(**self, ptr.as_mut_ptr()) libCZI_ReaderGetPyramidStatistics(**self, ptr.as_mut_ptr())
})?; })?;
let ptr = unsafe { ptr.assume_init() }; 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) }; unsafe { libCZI_Free(ptr as *mut c_void) };
Ok(statistics) Ok(statistics)
} }
@@ -653,7 +656,7 @@ impl LockedBitmap {
***self, ***self,
width, width,
height, height,
pixel_type.into(), pixel_type as i32,
stride, stride,
data.as_mut_ptr() as *mut c_void, data.as_mut_ptr() as *mut c_void,
) )
@@ -733,7 +736,10 @@ impl CziDocumentInfo {
) )
})?; })?;
let ptr = unsafe { ptr.assume_init() }; 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) }; unsafe { libCZI_Free(ptr as *mut c_void) };
Ok(info) Ok(info)
} }
@@ -782,9 +788,10 @@ impl CziDocumentInfo {
) )
})?; })?;
let ptr = unsafe { ptr.assume_init() }; let ptr = unsafe { ptr.assume_init() };
let info = unsafe { CStr::from_ptr(ptr) }.to_str()?.to_owned(); assert!(!ptr.is_null());
unsafe { libCZI_Free(ptr as *mut c_void) }; Ok(unsafe { CStr::from_ptr(ptr) }
Ok(info) .to_string_lossy()
.into_owned())
} }
/// Release the specified CZI-document-info object. /// Release the specified CZI-document-info object.

View File

@@ -3,6 +3,8 @@ use crate::misc::{PixelType, Ptr};
use crate::sys::*; use crate::sys::*;
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use std::ffi::{CStr, CString, c_char, c_void}; use std::ffi::{CStr, CString, c_char, c_void};
use std::fmt::Debug;
use std::mem;
use std::mem::{ManuallyDrop, MaybeUninit}; use std::mem::{ManuallyDrop, MaybeUninit};
/// This struct contains the version information of the libCZIApi-library. For versioning libCZI, SemVer2 (<https://semver.org/>) is used. /// This struct contains the version information of the libCZIApi-library. For versioning libCZI, SemVer2 (<https://semver.org/>) is used.
@@ -597,7 +599,7 @@ impl BitmapInfo {
Self(BitmapInfoInterop { Self(BitmapInfoInterop {
width, width,
height, height,
pixelType: pixel_type.into(), pixelType: pixel_type as i32,
}) })
} }
pub fn get_width(&self) -> u32 { pub fn get_width(&self) -> u32 {
@@ -616,19 +618,22 @@ impl BitmapInfo {
self.0.height = height; self.0.height = height;
} }
pub fn set_pixel_type(&mut self, pixel_type: PixelType) { 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 { impl BitmapLockInfo {
pub fn get_data_roi(&self) -> Vec<u8> { pub fn get_data_roi(&self) -> Vec<u8> {
unsafe { let vec = unsafe {
Vec::from_raw_parts( Vec::from_raw_parts(
self.0.ptrDataRoi as *mut u8, self.0.ptrDataRoi as *mut u8,
self.0.size as usize, self.0.size as usize,
self.0.size as usize, self.0.size as usize,
) )
} };
let res = vec.clone();
mem::forget(vec);
res
} }
pub fn get_stride(&self) -> u32 { pub fn get_stride(&self) -> u32 {
@@ -651,7 +656,7 @@ impl SubBlockInfo {
) -> Self { ) -> Self {
Self(SubBlockInfoInterop { Self(SubBlockInfoInterop {
compression_mode_raw, compression_mode_raw,
pixel_type: pixel_type.into(), pixel_type: pixel_type as i32,
coordinate: coordinate.0, coordinate: coordinate.0,
logical_rect: logical_rect.0, logical_rect: logical_rect.0,
physical_size: physical_size.0, physical_size: physical_size.0,
@@ -680,7 +685,7 @@ impl SubBlockInfo {
self.0.compression_mode_raw = compression_mode_raw self.0.compression_mode_raw = compression_mode_raw
} }
pub fn set_pixel_type(&mut self, pixel_type: PixelType) { 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) { pub fn set_coordinate(&mut self, coordinate: Coordinate) {
self.0.coordinate = coordinate.0 self.0.coordinate = coordinate.0
@@ -790,7 +795,7 @@ impl AddSubBlockInfo {
logical_height, logical_height,
physical_width, physical_width,
physical_height, physical_height,
pixel_type: pixel_type.into(), pixel_type: pixel_type as i32,
compression_mode_raw, compression_mode_raw,
size_data: data.len() as u32, size_data: data.len() as u32,
data: data.as_ptr() as *const c_void, data: data.as_ptr() as *const c_void,
@@ -898,7 +903,7 @@ impl AddSubBlockInfo {
self.0.physical_height = physical_height self.0.physical_height = physical_height
} }
pub fn set_pixel_type(&mut self, pixel_type: PixelType) { 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) { pub fn set_compression_mode_raw(&mut self, compression_mode_raw: i32) {
self.0.compression_mode_raw = compression_mode_raw self.0.compression_mode_raw = compression_mode_raw

View File

@@ -9,16 +9,71 @@ pub mod sys;
pub use functions::*; pub use functions::*;
pub use handle::*; pub use handle::*;
pub use interop::*; pub use interop::*;
pub use misc::{LibCZIApiError, PixelType, RawDataType}; pub use misc::{Dimension, LibCZIApiError, PixelType, RawDataType};
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::handle::{CziReader, InputStream}; use crate::handle::{CziReader, InputStream};
use crate::interop::{LibCZIBuildInformation, ReaderOpenInfo}; use crate::interop::{LibCZIBuildInformation, ReaderOpenInfo};
use crate::misc::Dimension;
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use std::env; use std::env;
use std::path::PathBuf; 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] #[test]
fn test_libczi_xml() -> Result<()> { fn test_libczi_xml() -> Result<()> {
let path = env::home_dir() let path = env::home_dir()

View File

@@ -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<Dimension> {
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<i32> for Dimension {
type Error = Error;
fn try_from(dimension: i32) -> Result<Self> {
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 /// enum for SubBlock.get_raw_data
#[derive(Clone, Debug)]
pub enum RawDataType { pub enum RawDataType {
Data, Data = 0,
Metadata, Metadata = 1,
}
impl TryFrom<i32> for RawDataType {
type Error = Error;
fn try_from(raw_data_type: i32) -> Result<Self> {
match raw_data_type {
0 => Ok(RawDataType::Data),
1 => Ok(RawDataType::Metadata),
_ => Err(anyhow!("Unknown data type {}", raw_data_type)),
}
}
} }
/// pixel type /// pixel type
#[derive(Clone, Debug)]
pub enum PixelType { pub enum PixelType {
Gray8, Gray8 = 0,
Gray16, Gray16 = 1,
Gray32Float, Gray32Float = 2,
Bgr24, Bgr24 = 3,
Bgr48, Bgr48 = 4,
Bgr96Float, Bgr96Float = 8,
Bgra32, Bgra32 = 9,
Gray64ComplexFloat, Gray64ComplexFloat = 10,
Bgr192ComplexFloat, Bgr192ComplexFloat = 11,
Gray32, Gray32 = 12,
Gray64Float, Gray64Float = 13,
} }
impl TryFrom<i32> for PixelType { impl TryFrom<i32> for PixelType {
@@ -82,24 +151,6 @@ impl TryFrom<i32> for PixelType {
} }
} }
impl From<PixelType> 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 { pub trait Ptr {
type Pointer; type Pointer;