libczirw-sys 0.5.1

Wrapper around libCZIAPI
Documentation
# libCZIrw-sys

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 pointers is available through the `sys` module.

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.

### 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.