- update README

- fix tests
- crt-static in build.rs
This commit is contained in:
w.pomp
2026-08-07 11:30:02 +02:00
parent a4de4a359f
commit 76d7d6a8ad
3 changed files with 222 additions and 30 deletions
+215 -5
View File
@@ -1,9 +1,219 @@
# libCZIrw-sys
Crate linking to [libCZIAPI](https://github.com/ZEISS/libczi).
This crate attempts to provide save wrappers to objects and functions in libCZIAPI.
Direct often unsafe access using pointer is available through the sys module.
Crate linking to [libCZIAPI](https://github.com/ZEISS/libczi). This crate attempts to provide safe wrappers to objects
and functions in libCZIAPI. Direct often unsafe access using pointer is available through the `sys` module.
By default, libCZIAPI will be statically linked. The feature 'dynamic' will switch it to dynamic linking.
By default, libCZIAPI will be statically linked. The feature `dynamic` will switch it to dynamic linking.
This code is licensed with an MIT or APACHE 2 license, but Zeiss' libCZI which is included as a submodule has a LGPL license.
This code is licensed with an MIT or APACHE 2 license, but Zeiss' libCZI which is included as a submodule has a LGPL
license.
### Reading a CZI file
The typical pattern is: create a reader, open it with an input stream, and query statistics or sub-blocks:
```rust
use libczirw_sys::{CziReader, Dimension, InputStream, ReaderOpenInfo};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// create the reader and a stream for the file
let czi = CziReader::create()?;
let stream = InputStream::create_from_file_utf8("path/to/file.czi")?;
czi.open(ReaderOpenInfo::new(&stream))?;
// get the (simple) statistics about the sub-blocks
let statistics = czi.get_statistics_simple()?;
println!("number of sub-blocks: {}", statistics.get_sub_block_count());
// iterate over the dimensions that are present (Z, C, T, ...)
let dim_bounds = statistics.get_dim_bounds();
let dimensions = Dimension::vec_from_bitflags(dim_bounds.get_dimensions_valid());
for (i, dim) in dimensions.iter().enumerate() {
println!("{:?}: {}", dim, dim_bounds.get_size()[i]);
}
// the bounding box of all sub-blocks
let bbox = statistics.get_bounding_box();
println!(
"overall extent: {} x {} (offset {}, {})",
bbox.get_w(),
bbox.get_h(),
bbox.get_x(),
bbox.get_y()
);
Ok(())
}
```
### Reading pixel data
Each sub-block can be decoded into a bitmap. Locking the bitmap gives access to the raw pixels:
```rust
use libczirw_sys::{CziReader, InputStream, ReaderOpenInfo};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let czi = CziReader::create()?;
let stream = InputStream::create_from_file_utf8("path/to/file.czi")?;
czi.open(ReaderOpenInfo::new(&stream))?;
// read the first sub-block and turn it into a bitmap
let sub_block = czi.read_sub_block(0)?;
let bitmap = sub_block.create_bitmap()?;
let info = bitmap.get_info()?;
println!(
"width: {}, height: {}, pixel type: {:?}",
info.get_width(),
info.get_height(),
info.get_pixel_type()?
);
// locking gives access to the raw pixel data
let locked = bitmap.lock()?;
let stride = locked.lock_info.get_stride();
let pixels = locked.lock_info.get_data_roi(); // copies the pixel bytes
// the bitmap is automatically unlocked when `locked` goes out of scope
Ok(())
}
```
### Reading the metadata
CZI files store metadata as an XML document, which can be retrieved as a string:
```rust
use libczirw_sys::{CziReader, InputStream, ReaderOpenInfo};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let czi = CziReader::create()?;
let stream = InputStream::create_from_file_utf8("path/to/file.czi")?;
czi.open(ReaderOpenInfo::new(&stream))?;
let metadata_segment = czi.get_metadata_segment()?;
let xml = metadata_segment.get_metadata_as_xml()?;
let xml: String = (&xml).try_into()?;
println!("{}", xml);
// document-level information (title, user, creation date, ...) as JSON
let doc_info = metadata_segment.get_czi_document_info()?;
let general_info = doc_info.get_general_document_info()?;
println!("{}", general_info);
Ok(())
}
```
### Reading attachments
Attachments hold additional data (e.g. microscope setup or experiment info) and are decoded based on their content type:
```rust
use libczirw_sys::{AttachmentData, CziReader, InputStream, ReaderOpenInfo};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let czi = CziReader::create()?;
let stream = InputStream::create_from_file_utf8("path/to/file.czi")?;
czi.open(ReaderOpenInfo::new(&stream))?;
for index in 0..czi.get_attachment_count()? {
let info = czi.get_attachment_info_from_directory(index)?;
println!(
"attachment '{}' of type {}",
info.get_name()?,
info.get_content_file_type()?
);
let attachment = czi.read_attachment(index)?;
match attachment.get_data()? {
AttachmentData::Float(values) => println!(" floats: {:?}", values),
AttachmentData::Xml(xml) => println!(" xml: {}", &xml[..xml.len().min(100)]),
AttachmentData::Unknown(bytes) => println!(" raw bytes: {}", bytes.len()),
}
}
Ok(())
}
```
### Writing a CZI file
Create a writer, add sub-blocks and metadata, and close the file to finalize it:
```rust
use libczirw_sys::{
AddSubBlockInfo, Coordinate, CziWriter, OutputStream, PixelType, WriteMetadataInfo,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let writer = CziWriter::create(r#"{"allow_duplicate_subblocks": true}"#)?;
let stream = OutputStream::create_for_file_utf8("output.czi", true)?;
writer.init(&stream, r#"{"minimum_m_index": 0, "maximum_m_index": 0}"#)?;
// a single gray-8-bit sub-block of 100 x 100 pixels
let width = 100;
let height = 100;
let pixels: Vec<u8> = (0..(width * height)).map(|i| i as u8).collect();
// bit 0 => dimension Z is valid (Z is the first dimension, value 1);
// the Z coordinate of this sub-block is 0
let coordinate = Coordinate::new(1 << 0, [0, 0, 0, 0, 0, 0, 0, 0, 0]);
let sub_block = AddSubBlockInfo::new(
coordinate,
0, // m_index_valid
0, // m_index
0, // x
0, // y
width, // logical width
height, // logical height
width, // physical width
height, // physical height
PixelType::Gray8,
0, // compression mode (none)
&pixels, // raw pixel data
b"", // sub-block metadata
b"", // attachment data
);
writer.add_sub_block(sub_block)?;
let xml_metadata = br#"<?xml version="1.0" encoding="utf-8"?>
<ImageDocument>
<Metadata>
<Information>
<Title>My document</Title>
</Information>
</Metadata>
</ImageDocument>"#;
writer.write_metadata(WriteMetadataInfo::new(xml_metadata))?;
// finalize the file - required to produce a valid CZI
writer.close()?;
Ok(())
}
```
### Version and build information
```rust
use libczirw_sys::{LibCZIBuildInformation, LibCZIVersionInfo};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let version = LibCZIVersionInfo::get_lib_czi_version_info()?;
println!(
"libCZI version {}.{}.{}",
version.get_major(),
version.get_minor(),
version.get_patch()
);
let build_info = LibCZIBuildInformation::get()?;
println!("compiler: {}", build_info.get_compiler_information());
println!("repository: {}", build_info.get_repository_url());
Ok(())
}
```
## Error handling
All fallible operations return a `Result` with the crate's `Error` type, which maps libCZIAPI error codes onto
descriptive variants (see `src/error.rs`). The `?` operator can be used directly; conversions from UTF-8 and
null-termination errors are provided via `From` implementations on the error type.