Enable static linking, libczi is now a submodule.

This commit is contained in:
Wim Pomp
2025-08-12 18:52:46 +02:00
parent 37ede463e0
commit 999af74dd6
13 changed files with 368 additions and 148 deletions

54
.github/workflows/publish.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: Publish
on: [push, pull_request, workflow_call]
permissions:
contents: read
jobs:
crates_io_publish:
name: Publish (crates.io)
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: cargo-release Cache
id: cargo_release_cache
uses: actions/cache@v3
with:
path: ~/.cargo/bin/cargo-release
key: ${{ runner.os }}-cargo-release
- run: cargo install cargo-release
if: steps.cargo_release_cache.outputs.cache-hit != 'true'
- name: cargo login
run: cargo login ${{ secrets.CRATES_IO_API_TOKEN }}
# allow-branch HEAD is because GitHub actions switches
# to the tag while building, which is a detached head
# Publishing is currently messy, because:
#
# * `peace_rt_model_core` exports `NativeError` or `WebError` depending on the target.
# * `peace_rt_model_web` fails to build when publishing the workspace for a native target.
# * `peace_rt_model_web` still needs its dependencies to be published before it can be
# published.
# * `peace_rt_model_hack` needs `peace_rt_model_web` to be published before it can be
# published.
#
# We *could* pass through `--no-verify` so `cargo` doesn't build the crate before publishing,
# which is reasonable, since this job only runs after the Linux, Windows, and WASM builds
# have passed.
- name: "cargo release publish"
run: |-
cargo release \
publish \
--workspace \
--all-features \
--allow-branch "main" \
--no-confirm \
--no-verify \
--execute

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "libczi"]
path = libczi
url = https://github.com/ZEISS/libczi.git

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "libczirw-sys" name = "libczirw-sys"
version = "0.1.2" version = "0.2.0"
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>"]
@@ -15,8 +15,13 @@ links = "libCZIAPI"
[dependencies] [dependencies]
anyhow = "1.0.98" anyhow = "1.0.98"
link-cplusplus = "1.0"
[build-dependencies] [build-dependencies]
anyhow = "1.0.98" anyhow = "1.0.98"
bindgen = "0.72.0"
cmake = "0.1.54" cmake = "0.1.54"
git2 = "0.20.2" regex = "1.11.1"
[features]
dynamic = []

View File

@@ -4,4 +4,6 @@ Crate linking to [libCZIAPI](https://github.com/ZEISS/libczi).
This crate attempts to provide save wrappers to objects and functions in libCZIAPI. 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. Direct often unsafe access using pointer is available through the sys module.
This code is licensed with an MIT license, but Zeiss' libCZI has a LGPL license. By default, libCZIAPI will be statically linked. The feature 'dynamic' will switch it to dynamic linking.
This code is licensed with an MIT license, but Zeiss' libCZI which is included as a submodule has a LGPL license.

199
build.rs
View File

@@ -1,90 +1,171 @@
use anyhow::Result; extern crate bindgen;
use anyhow::{Error, Result};
use std::env; use std::env;
use std::fs::OpenOptions;
use std::io::{Read, Seek, Write};
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(not(feature = "dynamic"))]
use std::fmt::Debug;
#[cfg(not(feature = "dynamic"))]
use bindgen::callbacks::ItemInfo;
#[cfg(not(feature = "dynamic"))]
use std::collections::HashMap;
#[cfg(not(feature = "dynamic"))]
use regex::Regex;
fn main() -> Result<()> { fn main() -> Result<()> {
if env::var("DOCS_RS").is_err() { if env::var("DOCS_RS").is_err() {
let out_dir = PathBuf::from(env::var("OUT_DIR")?).canonicalize()?; let out_dir = PathBuf::from(env::var("OUT_DIR")?).canonicalize()?;
let libczi_dir = out_dir.join("libczirw"); let libczi_dir = PathBuf::from("libczi");
let rep = if !libczi_dir.exists() { let libczi_src = libczi_dir.join("Src/libCZI");
git2::Repository::clone("https://github.com/ZEISS/libczi.git", &libczi_dir) let libcziapi_inc = libczi_dir.join("Src/libCZIAPI/inc");
.expect("unable to clone libczirw") let libcziapi_src = libczi_dir.join("Src/libCZIAPI/src");
} else { let libcziapi_h = libcziapi_inc.join("libCZIApi.h");
git2::Repository::open(&libczi_dir)?
};
let (object, _) = rep.revparse_ext("494ac62f853de6ab86458f167fd85a03ee6d4f7e")?;
rep.checkout_tree(&object, None)?;
let dst = cmake::Config::new(&libczi_dir) let dst = cmake::Config::new(&libczi_dir)
.cxxflag("-fms-extensions")
.define("LIBCZI_BUILD_UNITTESTS", "OFF") .define("LIBCZI_BUILD_UNITTESTS", "OFF")
.define("LIBCZI_BUILD_CZICMD", "OFF") .define("LIBCZI_BUILD_CZICMD", "OFF")
.define("LIBCZI_BUILD_DYNLIB", "OFF") .define("LIBCZI_BUILD_DYNLIB", "OFF")
.define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_EIGEN3", "OFF") .define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_EIGEN3", "OFF")
.define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_ZSTD", "OFF") .define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_ZSTD", "OFF")
.define("LIBCZI_BUILD_CURL_BASED_STREAM", "OFF") .define("LIBCZI_BUILD_CURL_BASED_STREAM", "OFF")
.define("LIBCZI_BUILD_PREFER_EXTERNAL_PACKAGE_LIBCURL", "OFF") .define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_LIBCURL", "OFF")
.define("LIBCZI_BUILD_AZURESDK_BASED_STREAM", "OFF") .define("LIBCZI_BUILD_AZURESDK_BASED_STREAM", "OFF")
.define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_RAPIDJSON", "OFF") .define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_RAPIDJSON", "OFF")
.define("LIBCZI_BUILD_LIBCZIAPI", "ON") .define("LIBCZI_BUILD_LIBCZIAPI", "ON")
.build(); .build();
// let libcziapi_inc = libczi_dir.join("Src/libCZIAPI/inc"); #[cfg(not(feature = "dynamic"))]
// let libczi_src = libczi_dir.join("Src/libCZI"); let bindings = {
// let libcziapi_src = libczi_dir.join("Src/libCZIAPI/src"); let mut libcziapi_a = out_dir.join("build/Src/libCZIAPI/liblibCZIAPIStatic.a");
// let libczi_h = libcziapi_inc.join("libCZIApi.h"); if !libcziapi_a.exists() {
libcziapi_a = out_dir.join("build/Src/libCZIAPI/liblibCZIAPIStatic.lib");
let import_export = libczi_dir.join("Src/libCZIAPI/inc/importexport.h"); }
{ bindgen::Builder::default().parse_callbacks(Box::new(DeMangler::new(libcziapi_a)?))
let mut file = OpenOptions::new()
.read(true)
.write(true)
.open(&import_export)
.expect("Could not open file");
let mut data = String::new();
file.read_to_string(&mut data).expect("Could not read file");
let data = data.replace(" __declspec(dllexport)", "");
let bytes = data.as_bytes();
(&file).rewind().expect("Could not rewind");
(&file).write_all(bytes).expect("Could not write file");
file.set_len(bytes.len() as u64)
.expect("Could not truncate");
}; };
// let bindings = bindgen::Builder::default() #[cfg(feature = "dynamic")]
// .clang_args([ let bindings = bindgen::Builder::default();
// "-x",
// "c++",
// "-std=c++14",
// "-I",
// libcziapi_inc
// .to_str()
// .ok_or(Error::msg("cannot into string"))?,
// "-I",
// libcziapi_src
// .to_str()
// .ok_or(Error::msg("cannot into string"))?,
// "-I",
// libczi_src
// .to_str()
// .ok_or(Error::msg("cannot into string"))?,
// ])
// .header(libczi_h.to_str().ok_or(Error::msg("cannot into string"))?)
// .generate()
// .expect("Unable to generate bindings");
//
// bindings
// .write_to_file(out_dir.join("lib_czi_api.rs"))
// .expect("Couldn't write bindings!");
let bindings = bindings
.merge_extern_blocks(true)
.clang_args([
"-fms-extensions",
"-x",
"c++",
"-std=c++14",
"-I",
libcziapi_inc
.to_str()
.ok_or(Error::msg("cannot into string"))?,
"-I",
libcziapi_src
.to_str()
.ok_or(Error::msg("cannot into string"))?,
"-I",
libczi_src
.to_str()
.ok_or(Error::msg("cannot into string"))?,
])
.header(
libcziapi_h
.to_str()
.ok_or(Error::msg("cannot into string"))?,
)
.generate()?;
bindings.write_to_file(out_dir.join("lib_czi_api.rs"))?;
#[cfg(not(feature = "dynamic"))]
{
println!(
"cargo:rustc-link-search=native={}",
dst.join("build/Src/libCZIAPI").display()
);
println!("cargo:rustc-link-lib=static=libCZIAPIStatic");
println!(
"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"),
_ => return Err(Error::msg(format!("unsupported profile: {}", profile))),
}
println!(
"cargo:rustc-link-search=native={}",
dst.join("lib").display()
);
println!(
"cargo:rustc-link-search=native={}",
dst.join("lib64").display()
);
println!("cargo:rustc-link-lib=static=zstd");
}
#[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");
Ok(()) Ok(())
} }
#[cfg(not(feature = "dynamic"))]
#[derive(Debug)]
struct DeMangler {
map: HashMap<String, String>,
}
#[cfg(not(feature = "dynamic"))]
impl DeMangler {
fn new(a_file: PathBuf) -> Result<Self> {
let cmd = std::process::Command::new("nm").arg(&a_file).output()?;
let pat = Regex::new(r"^[\da-f]*\s[A-Z]\s(.*_Z(\d+)(libCZI_.*))$")?;
let mut map = HashMap::new();
for line in std::str::from_utf8(&cmd.stdout)?.lines() {
if let Some(caps) = pat.captures(line.trim()) {
if let (Some(symbol), Some(n), Some(name)) = (caps.get(1), caps.get(2), caps.get(3))
{
let n: usize = n.as_str().parse()?;
let name = name.as_str();
let demangled = name[..n].to_string();
let mangled = symbol.as_str().to_string();
if let Some(existing_mangled) = map.get(&demangled) {
if existing_mangled != &mangled {
return Err(Error::msg(format!(
"conflicting mangled symbols for {} in {}: {}, {}",
demangled,
a_file.to_str().unwrap(),
existing_mangled,
mangled
)));
}
} else {
map.insert(demangled, mangled);
}
}
}
}
Ok(Self { map })
}
}
#[cfg(not(feature = "dynamic"))]
impl bindgen::callbacks::ParseCallbacks for DeMangler {
fn generated_link_name_override(&self, item_info: ItemInfo<'_>) -> Option<String> {
self.map.get(item_info.name).cloned()
}
}

1
libczi Submodule

Submodule libczi added at 494ac62f85

View File

@@ -640,7 +640,13 @@ impl LockedBitmap {
/// \\param \[out\] ptr Pointer to the memory location where the bitmap is to be copied to. /// \\param \[out\] ptr Pointer to the memory location where the bitmap is to be copied to.
/// ///
/// \\returns A LibCZIApiErrorCode. /// \\returns A LibCZIApiErrorCode.
pub fn copy(&self, width: u32, height: u32, pixel_type: PixelType, stride: u32) -> Result<Bitmap> { pub fn copy(
&self,
width: u32,
height: u32,
pixel_type: PixelType,
stride: u32,
) -> Result<Bitmap> {
let mut data = MaybeUninit::<Self>::uninit(); let mut data = MaybeUninit::<Self>::uninit();
LibCZIApiError::try_from(unsafe { LibCZIApiError::try_from(unsafe {
libCZI_BitmapCopyTo( libCZI_BitmapCopyTo(

View File

@@ -3,60 +3,61 @@ use crate::sys::*;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::ops::Deref; use std::ops::Deref;
/// CZI-reader object. /// CZI-reader object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CziReader(pub (crate) CziReaderObjectHandle); pub struct CziReader(pub(crate) CziReaderObjectHandle);
/// sub-block object. /// sub-block object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SubBlock(pub (crate) SubBlockObjectHandle); pub struct SubBlock(pub(crate) SubBlockObjectHandle);
/// input stream object. /// input stream object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct InputStream(pub (crate) InputStreamObjectHandle); pub struct InputStream(pub(crate) InputStreamObjectHandle);
/// output stream object. /// output stream object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct OutputStream(pub (crate) OutputStreamObjectHandle); pub struct OutputStream(pub(crate) OutputStreamObjectHandle);
/// memory allocation object - which is a pointer to a memory block, which must be /// memory allocation object - which is a pointer to a memory block, which must be
/// freed with 'libCZI_Free'. /// freed with 'libCZI_Free'.
/// TODO(JBL): this is not really used so far, should be removed I guess. /// TODO(JBL): this is not really used so far, should be removed I guess.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MemoryAllocation(pub (crate) MemoryAllocationObjectHandle); pub struct MemoryAllocation(pub(crate) MemoryAllocationObjectHandle);
/// bitmap object. /// bitmap object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Bitmap(pub (crate) BitmapObjectHandle); pub struct Bitmap(pub(crate) BitmapObjectHandle);
/// metadata segment object. /// metadata segment object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MetadataSegment(pub (crate) MetadataSegmentObjectHandle); pub struct MetadataSegment(pub(crate) MetadataSegmentObjectHandle);
/// attachment object. /// attachment object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Attachment(pub (crate) AttachmentObjectHandle); pub struct Attachment(pub(crate) AttachmentObjectHandle);
/// writer object. /// writer object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CziWriter(pub (crate) CziWriterObjectHandle); pub struct CziWriter(pub(crate) CziWriterObjectHandle);
/// single-channel-scaling-tile-accessor. /// single-channel-scaling-tile-accessor.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SingleChannelScalingTileAccessor(pub (crate) SingleChannelScalingTileAccessorObjectHandle); pub struct SingleChannelScalingTileAccessor(
pub(crate) SingleChannelScalingTileAccessorObjectHandle,
);
/// document info object. /// document info object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CziDocumentInfo(pub (crate) CziDocumentInfoHandle); pub struct CziDocumentInfo(pub(crate) CziDocumentInfoHandle);
/// display settings object. /// display settings object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DisplaySettings(pub (crate) DisplaySettingsHandle); pub struct DisplaySettings(pub(crate) DisplaySettingsHandle);
/// channel display settings object. /// channel display settings object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ChannelDisplaySettings(pub (crate) ChannelDisplaySettingsHandle); pub struct ChannelDisplaySettings(pub(crate) ChannelDisplaySettingsHandle);
macro_rules! impl_struct { macro_rules! impl_struct {
($($n:ident: $t:ty: $s:ty $(,)?)*) => { ($($n:ident: $t:ty: $s:ty $(,)?)*) => {

View File

@@ -8,45 +8,45 @@ 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.
/// Note that the value of the tweak version number does not have a meaning (as far as SemVer2 is concerned). /// Note that the value of the tweak version number does not have a meaning (as far as SemVer2 is concerned).
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct LibCZIVersionInfo(pub (crate) LibCZIVersionInfoInterop); pub struct LibCZIVersionInfo(pub(crate) LibCZIVersionInfoInterop);
/// This struct gives information about the build of the libCZIApi-library. /// This struct gives information about the build of the libCZIApi-library.
/// Note that all strings must be freed by the caller (using libCZI_Free). /// Note that all strings must be freed by the caller (using libCZI_Free).
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct LibCZIBuildInformation(pub (crate) LibCZIBuildInformationInterop); pub struct LibCZIBuildInformation(pub(crate) LibCZIBuildInformationInterop);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct InputStreamClassInfo(pub (crate) InputStreamClassInfoInterop); pub struct InputStreamClassInfo(pub(crate) InputStreamClassInfoInterop);
/// This structure gives additional information about an error that occurred in the external stream. /// This structure gives additional information about an error that occurred in the external stream.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ExternalStreamErrorInfo(pub (crate) ExternalStreamErrorInfoInterop); pub struct ExternalStreamErrorInfo(pub(crate) ExternalStreamErrorInfoInterop);
/// This structure contains information about externally provided functions for reading data from an input stream, /// This structure contains information about externally provided functions for reading data from an input stream,
/// and it is used to construct a stream-object to be used with libCZI. /// and it is used to construct a stream-object to be used with libCZI.
/// Note on lifetime: The function pointers must remain valid until the function 'close_function' is called. The lifetime /// Note on lifetime: The function pointers must remain valid until the function 'close_function' is called. The lifetime
/// may extend beyond calling the 'libCZI_ReleaseInputStream' function for the corresponding stream-object. /// may extend beyond calling the 'libCZI_ReleaseInputStream' function for the corresponding stream-object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ExternalInputStreamStruct(pub (crate) ExternalInputStreamStructInterop); pub struct ExternalInputStreamStruct(pub(crate) ExternalInputStreamStructInterop);
/// This structure contains information about externally provided functions for writing data to an output stream, /// This structure contains information about externally provided functions for writing data to an output stream,
/// and it is used to construct a stream-object to be used with libCZI. /// and it is used to construct a stream-object to be used with libCZI.
/// Note on lifetime: The function pointers must remain valid until the function 'close_function' is called. The lifetime /// Note on lifetime: The function pointers must remain valid until the function 'close_function' is called. The lifetime
/// may extend beyond calling the 'libCZI_ReleaseOutputStream' function for the corresponding stream-object. /// may extend beyond calling the 'libCZI_ReleaseOutputStream' function for the corresponding stream-object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ExternalOutputStreamStruct(pub (crate) ExternalOutputStreamStructInterop); pub struct ExternalOutputStreamStruct(pub(crate) ExternalOutputStreamStructInterop);
/// This structure gather the information needed to create a reader object. /// This structure gather the information needed to create a reader object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ReaderOpenInfo(pub (crate) ReaderOpenInfoInterop); pub struct ReaderOpenInfo(pub(crate) ReaderOpenInfoInterop);
/// This structure describes a rectangle, given by its top-left corner and its width and height. /// This structure describes a rectangle, given by its top-left corner and its width and height.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct IntRect(pub (crate) IntRectInterop); pub struct IntRect(pub(crate) IntRectInterop);
/// This structure describes a size, given by its width and height. /// This structure describes a size, given by its width and height.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct IntSize(pub (crate) IntSizeInterop); pub struct IntSize(pub(crate) IntSizeInterop);
/// This structure gives the bounds for a set of dimensions. /// This structure gives the bounds for a set of dimensions.
/// The bit at position `i` in `dimensions_valid` indicates whether the interval for dimension `i+1` is valid. So, bit 0 /// The bit at position `i` in `dimensions_valid` indicates whether the interval for dimension `i+1` is valid. So, bit 0
@@ -56,7 +56,7 @@ pub struct IntSize(pub (crate) IntSizeInterop);
/// An example would be: `dimensions_valid` = 0b00000011, `start` = { 0, 2 }, `size` = { 5, 6 }. This would mean that the /// An example would be: `dimensions_valid` = 0b00000011, `start` = { 0, 2 }, `size` = { 5, 6 }. This would mean that the
/// dimension 'Z' is valid, and the interval is [0, 5], and the dimension 'C' is valid, and the interval is [2, 8]. /// dimension 'Z' is valid, and the interval is [0, 5], and the dimension 'C' is valid, and the interval is [2, 8].
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DimBounds(pub (crate) DimBoundsInterop); pub struct DimBounds(pub(crate) DimBoundsInterop);
/// This structure gives the coordinates (of a sub-block) for a set of dimension. /// This structure gives the coordinates (of a sub-block) for a set of dimension.
/// The bit at position `i` in `dimensions_valid` indicates whether the coordinate for dimension `i+1` is valid. So, bit 0 /// The bit at position `i` in `dimensions_valid` indicates whether the coordinate for dimension `i+1` is valid. So, bit 0
@@ -66,35 +66,35 @@ pub struct DimBounds(pub (crate) DimBoundsInterop);
/// An example would be: `dimensions_valid` = 0b00000011, `value` = { 0, 2 }. This would mean that the /// An example would be: `dimensions_valid` = 0b00000011, `value` = { 0, 2 }. This would mean that the
/// dimension 'Z' is valid, and the coordinate for 'Z' is 0, and the dimension 'C' is valid, and the coordinate for 'C' is 2. /// dimension 'Z' is valid, and the coordinate for 'Z' is 0, and the dimension 'C' is valid, and the coordinate for 'C' is 2.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Coordinate(pub (crate) CoordinateInterop); pub struct Coordinate(pub(crate) CoordinateInterop);
/// This structure contains the bounding boxes for a scene. /// This structure contains the bounding boxes for a scene.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct BoundingBoxes(pub (crate) BoundingBoxesInterop); pub struct BoundingBoxes(pub(crate) BoundingBoxesInterop);
/// This structure contains basic statistics about an CZI-document. /// This structure contains basic statistics about an CZI-document.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SubBlockStatistics(pub (crate) SubBlockStatisticsInterop); pub struct SubBlockStatistics(pub(crate) SubBlockStatisticsInterop);
/// This structure extends on the basic statistics about an CZI-document, and includes per-scene statistics. /// This structure extends on the basic statistics about an CZI-document, and includes per-scene statistics.
#[derive(Debug)] #[derive(Debug)]
pub struct SubBlockStatisticsEx(pub (crate) SubBlockStatisticsInteropEx); pub struct SubBlockStatisticsEx(pub(crate) SubBlockStatisticsInteropEx);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MetadataAsXml(pub (crate) MetadataAsXmlInterop); pub struct MetadataAsXml(pub(crate) MetadataAsXmlInterop);
/// Information about the bitmap represented by a bitmap-object. /// Information about the bitmap represented by a bitmap-object.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct BitmapInfo(pub (crate) BitmapInfoInterop); pub struct BitmapInfo(pub(crate) BitmapInfoInterop);
/// This structure contains information about a locked bitmap-object, allowing direct /// This structure contains information about a locked bitmap-object, allowing direct
/// access to the pixel data. /// access to the pixel data.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct BitmapLockInfo(pub (crate) BitmapLockInfoInterop); pub struct BitmapLockInfo(pub(crate) BitmapLockInfoInterop);
/// This structure contains the information about a sub-block. /// This structure contains the information about a sub-block.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SubBlockInfo(pub (crate) SubBlockInfoInterop); pub struct SubBlockInfo(pub(crate) SubBlockInfoInterop);
/// This structure contains the information about an attachment. /// This structure contains the information about an attachment.
/// Note that performance reasons we use a fixed-size array for the name. In the rare case that the name is too long to fit into the /// Note that performance reasons we use a fixed-size array for the name. In the rare case that the name is too long to fit into the
@@ -102,35 +102,35 @@ pub struct SubBlockInfo(pub (crate) SubBlockInfoInterop);
/// In addition, the field 'name_in_case_of_overflow' then contains the full text, allocated with 'libCZI_AllocateString' (and responsibility /// In addition, the field 'name_in_case_of_overflow' then contains the full text, allocated with 'libCZI_AllocateString' (and responsibility
/// for releasing the memory is with the caller). /// for releasing the memory is with the caller).
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct AttachmentInfo(pub (crate) AttachmentInfoInterop); pub struct AttachmentInfo(pub(crate) AttachmentInfoInterop);
/// This structure contains the information about file-header. /// This structure contains the information about file-header.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct FileHeaderInfo(pub (crate) FileHeaderInfoInterop); pub struct FileHeaderInfo(pub(crate) FileHeaderInfoInterop);
/// This structure is used to pass the subblock information to libCZIAPI, describing a subblock to be added to a CZI-file. /// This structure is used to pass the subblock information to libCZIAPI, describing a subblock to be added to a CZI-file.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct AddSubBlockInfo(pub (crate) AddSubBlockInfoInterop); pub struct AddSubBlockInfo(pub(crate) AddSubBlockInfoInterop);
/// This structure is used to pass the attachment information to libCZIAPI, describing an attachment to be added to a CZI-file. /// This structure is used to pass the attachment information to libCZIAPI, describing an attachment to be added to a CZI-file.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct AddAttachmentInfo(pub (crate) AddAttachmentInfoInterop); pub struct AddAttachmentInfo(pub(crate) AddAttachmentInfoInterop);
/// This structure is used to pass the metadata information to libCZIAPI. /// This structure is used to pass the metadata information to libCZIAPI.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct WriteMetadataInfo(pub (crate) WriteMetadataInfoInterop); pub struct WriteMetadataInfo(pub(crate) WriteMetadataInfoInterop);
/// This structure is used to pass the accessor options to libCZIAPI. /// This structure is used to pass the accessor options to libCZIAPI.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct AccessorOptions(pub (crate) AccessorOptionsInterop); pub struct AccessorOptions(pub(crate) AccessorOptionsInterop);
/// This structure gathers all information about a channel for the purpose of multi-channel-composition. /// This structure gathers all information about a channel for the purpose of multi-channel-composition.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CompositionChannelInfo(pub (crate) CompositionChannelInfoInterop); pub struct CompositionChannelInfo(pub(crate) CompositionChannelInfoInterop);
/// This structure gathers the information about the scaling. /// This structure gathers the information about the scaling.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ScalingInfo(pub (crate) ScalingInfoInterop); pub struct ScalingInfo(pub(crate) ScalingInfoInterop);
macro_rules! impl_ptr { macro_rules! impl_ptr {
($($n:ident: $t:ty: $s:ty $(,)?)*) => { ($($n:ident: $t:ty: $s:ty $(,)?)*) => {
@@ -488,8 +488,83 @@ impl SubBlockStatistics {
} }
} }
impl SubBlockStatisticsEx {
// pub fn new(
// sub_block_count: i32,
// min_m_index: i32,
// max_m_index: i32,
// bounding_box: IntRect,
// bounding_box_layer0: IntRect,
// dim_bounds: DimBounds,
// per_scenes_bounding_boxes: Vec<BoundingBoxes>
// ) -> Self {
// Self(SubBlockStatisticsInteropEx {
// sub_block_count,
// min_m_index,
// max_m_index,
// bounding_box: bounding_box.0,
// bounding_box_layer0: bounding_box_layer0.0,
// dim_bounds: dim_bounds.0,
// number_of_per_scenes_bounding_boxes: per_scenes_bounding_boxes.len() as i32,
// per_scenes_bounding_boxes: unsafe { transmute::<Vec<BoundingBoxes>, __IncompleteArrayField<BoundingBoxesInterop>>(per_scenes_bounding_boxes) },
// })
// }
pub fn get_sub_block_count(&self) -> i32 {
self.0.sub_block_count
}
pub fn get_min_m_index(&self) -> i32 {
self.0.min_m_index
}
pub fn get_max_m_index(&self) -> i32 {
self.0.max_m_index
}
pub fn get_bounding_box(&self) -> IntRect {
IntRect(self.0.bounding_box)
}
pub fn get_bounding_box_layer0(&self) -> IntRect {
IntRect(self.0.bounding_box_layer0)
}
pub fn get_dim_bounds(&self) -> DimBounds {
DimBounds(self.0.dim_bounds)
}
pub fn get_number_of_per_scenes_bounding_boxes(&self) -> i32 {
self.0.number_of_per_scenes_bounding_boxes
}
// pub fn get_per_scenes_bounding_boxes(&self) -> Vec<BoundingBoxes> {
// unsafe { transmute(&self.0.per_scenes_bounding_boxes) }.clone()
// }
pub fn set_sub_block_count(&mut self, sub_block_count: i32) {
self.0.sub_block_count = sub_block_count;
}
pub fn set_min_m_index(&mut self, min_m_index: i32) {
self.0.min_m_index = min_m_index;
}
pub fn set_max_m_index(&mut self, max_m_index: i32) {
self.0.max_m_index = max_m_index;
}
pub fn set_bounding_box(&mut self, bounding_box: IntRect) {
self.0.bounding_box = bounding_box.0
}
pub fn set_bounding_box_layer0(&mut self, bounding_box_layer0: IntRect) {
self.0.bounding_box_layer0 = bounding_box_layer0.0
}
pub fn set_dim_bounds(&mut self, dim_bounds: DimBounds) {
self.0.dim_bounds = dim_bounds.0
}
pub fn set_number_of_per_scenes_bounding_boxes(
&mut self,
number_of_per_scenes_bounding_boxes: i32,
) {
self.0.number_of_per_scenes_bounding_boxes = number_of_per_scenes_bounding_boxes;
}
// pub fn set_per_scenes_bounding_boxes(&mut self, per_scenes_bounding_boxes: Vec<BoundingBoxes>) {
// self.0.number_of_per_scenes_bounding_boxes = per_scenes_bounding_boxes.len() as i32;
// self.0.per_scenes_bounding_boxes = unsafe { transmute(per_scenes_bounding_boxes) };
// }
}
impl MetadataAsXml { impl MetadataAsXml {
fn get_data(&self) -> Result<String> { pub fn get_data(&self) -> Result<String> {
let xml_data = unsafe { let xml_data = unsafe {
Vec::from_raw_parts( Vec::from_raw_parts(
self.0.data as *mut u8, self.0.data as *mut u8,
@@ -697,12 +772,8 @@ impl AddSubBlockInfo {
physical_height: i32, physical_height: i32,
pixel_type: PixelType, pixel_type: PixelType,
compression_mode_raw: i32, compression_mode_raw: i32,
size_data: u32,
data: &[u8], data: &[u8],
stride: u32,
size_metadata: u32,
metadata: &[u8], metadata: &[u8],
size_attachment: u32,
attachment: &[u8], attachment: &[u8],
) -> Self { ) -> Self {
let data = ManuallyDrop::new(data.to_vec()); let data = ManuallyDrop::new(data.to_vec());
@@ -721,12 +792,12 @@ impl AddSubBlockInfo {
physical_height, physical_height,
pixel_type: pixel_type.into(), pixel_type: pixel_type.into(),
compression_mode_raw, compression_mode_raw,
size_data, size_data: data.len() as u32,
data: data.as_ptr() as *const c_void, data: data.as_ptr() as *const c_void,
stride, stride: 1,
size_metadata, size_metadata: metadata.len() as u32,
metadata: metadata.as_ptr() as *const c_void, metadata: metadata.as_ptr() as *const c_void,
size_attachment, size_attachment: attachment.len() as u32,
attachment: attachment.as_ptr() as *const c_void, attachment: attachment.as_ptr() as *const c_void,
}) })
} }
@@ -832,25 +903,16 @@ impl AddSubBlockInfo {
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
} }
pub fn set_size_data(&mut self, size_data: u32) {
self.0.size_data = size_data
}
pub fn set_data(&mut self, data: &[u8]) { pub fn set_data(&mut self, data: &[u8]) {
let data = ManuallyDrop::new(data.to_vec()); let data = ManuallyDrop::new(data.to_vec());
self.0.data = data.as_ptr() as *const c_void; self.0.data = data.as_ptr() as *const c_void;
self.0.size_data = data.len() as u32; self.0.size_data = data.len() as u32;
} }
pub fn set_size_metadata(&mut self, size_metadata: u32) {
self.0.size_metadata = size_metadata
}
pub fn set_metadata(&mut self, metadata: &[u8]) { pub fn set_metadata(&mut self, metadata: &[u8]) {
let metadata = ManuallyDrop::new(metadata.to_vec()); let metadata = ManuallyDrop::new(metadata.to_vec());
self.0.metadata = metadata.as_ptr() as *const c_void; self.0.metadata = metadata.as_ptr() as *const c_void;
self.0.size_metadata = metadata.len() as u32; self.0.size_metadata = metadata.len() as u32;
} }
pub fn set_size_attachment(&mut self, size_attachment: u32) {
self.0.size_attachment = size_attachment
}
pub fn set_attachment(&mut self, attachment: &[u8]) { pub fn set_attachment(&mut self, attachment: &[u8]) {
let attachment = ManuallyDrop::new(attachment.to_vec()); let attachment = ManuallyDrop::new(attachment.to_vec());
self.0.attachment = attachment.as_ptr() as *const c_void; self.0.attachment = attachment.as_ptr() as *const c_void;
@@ -863,7 +925,6 @@ impl AddAttachmentInfo {
guid: [u8; 16], guid: [u8; 16],
content_file_type: [u8; 8], content_file_type: [u8; 8],
name: [u8; 80], name: [u8; 80],
size_attachment_data: u32,
attachment_data: &[u8], attachment_data: &[u8],
) -> Self { ) -> Self {
let attachment_data = ManuallyDrop::new(attachment_data.to_vec()); let attachment_data = ManuallyDrop::new(attachment_data.to_vec());
@@ -871,7 +932,7 @@ impl AddAttachmentInfo {
guid, guid,
contentFileType: content_file_type, contentFileType: content_file_type,
name, name,
size_attachment_data, size_attachment_data: attachment_data.len() as u32,
attachment_data: attachment_data.as_ptr() as *const c_void, attachment_data: attachment_data.as_ptr() as *const c_void,
}) })
} }
@@ -905,9 +966,6 @@ impl AddAttachmentInfo {
pub fn set_name(&mut self, name: [u8; 80]) { pub fn set_name(&mut self, name: [u8; 80]) {
self.0.name = name self.0.name = name
} }
pub fn set_size_attachment_data(&mut self, size_attachment_data: u32) {
self.0.size_attachment_data = size_attachment_data
}
pub fn set_attachment_data(&mut self, attachment_data: &[u8]) { pub fn set_attachment_data(&mut self, attachment_data: &[u8]) {
let attachment_data = ManuallyDrop::new(attachment_data.to_vec()); let attachment_data = ManuallyDrop::new(attachment_data.to_vec());
self.0.attachment_data = attachment_data.as_ptr() as *const c_void; self.0.attachment_data = attachment_data.as_ptr() as *const c_void;
@@ -916,10 +974,10 @@ impl AddAttachmentInfo {
} }
impl WriteMetadataInfo { impl WriteMetadataInfo {
pub fn new(size_metadata: u32, metadata: &[u8]) -> Self { pub fn new(metadata: &[u8]) -> Self {
let metadata = ManuallyDrop::new(metadata.to_vec()); let metadata = ManuallyDrop::new(metadata.to_vec());
Self(WriteMetadataInfoInterop { Self(WriteMetadataInfoInterop {
size_metadata, size_metadata: metadata.len() as u32,
metadata: metadata.as_ptr() as *const c_void, metadata: metadata.as_ptr() as *const c_void,
}) })
} }
@@ -935,9 +993,6 @@ impl WriteMetadataInfo {
) )
} }
} }
pub fn set_size_metadata(&mut self, size_metadata: u32) {
self.0.size_metadata = size_metadata
}
pub fn set_metadata(&mut self, metadata: &[u8]) { pub fn set_metadata(&mut self, metadata: &[u8]) {
let metadata = ManuallyDrop::new(metadata.to_vec()); let metadata = ManuallyDrop::new(metadata.to_vec());
self.0.metadata = metadata.as_ptr() as *const c_void; self.0.metadata = metadata.as_ptr() as *const c_void;

View File

@@ -1,3 +1,5 @@
extern crate link_cplusplus;
mod functions; mod functions;
mod handle; mod handle;
mod interop; mod interop;
@@ -7,7 +9,7 @@ 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, RawDataType, PixelType}; pub use misc::{LibCZIApiError, PixelType, RawDataType};
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@@ -15,6 +17,7 @@ mod tests {
use crate::interop::{LibCZIBuildInformation, ReaderOpenInfo}; use crate::interop::{LibCZIBuildInformation, ReaderOpenInfo};
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use std::env; use std::env;
use std::path::PathBuf;
#[test] #[test]
fn test_libczi_xml() -> Result<()> { fn test_libczi_xml() -> Result<()> {
@@ -37,9 +40,7 @@ mod tests {
#[test] #[test]
fn test_libczi_pyramid_statistics() -> Result<()> { fn test_libczi_pyramid_statistics() -> Result<()> {
let path = env::home_dir() let path = PathBuf::from("test-files/Experiment-2029.czi");
.unwrap()
.join("code/rust/ndbioimage/tests/files/Experiment-2029.czi");
assert!(path.exists()); assert!(path.exists());
let czi = CziReader::create()?; let czi = CziReader::create()?;
let stream = InputStream::create_from_file_utf8( let stream = InputStream::create_from_file_utf8(
@@ -54,9 +55,7 @@ mod tests {
#[test] #[test]
fn test_libczi_document_info() -> Result<()> { fn test_libczi_document_info() -> Result<()> {
let path = env::home_dir() let path = PathBuf::from("test-files/Experiment-2029.czi");
.unwrap()
.join("code/rust/ndbioimage/tests/files/Experiment-2029.czi");
assert!(path.exists()); assert!(path.exists());
let czi = CziReader::create()?; let czi = CziReader::create()?;
let stream = InputStream::create_from_file_utf8( let stream = InputStream::create_from_file_utf8(
@@ -67,16 +66,25 @@ mod tests {
let metadata_segment = czi.get_metadata_segment()?; let metadata_segment = czi.get_metadata_segment()?;
let document_info = metadata_segment.get_czi_document_info()?; let document_info = metadata_segment.get_czi_document_info()?;
let general_document_info = document_info.get_general_document_info()?; let general_document_info = document_info.get_general_document_info()?;
println!("xml: {}", &general_document_info[..general_document_info.len().min(100)]); println!(
"xml: {}",
&general_document_info[..general_document_info.len().min(100)]
);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_lib_czi_build_information() -> Result<()> { fn test_lib_czi_build_information() -> Result<()> {
let build_info = LibCZIBuildInformation::get()?; let build_info = LibCZIBuildInformation::get()?;
println!("compiler information: {:?}", build_info.get_compiler_information()); println!(
"compiler information: {:?}",
build_info.get_compiler_information()
);
println!("repository url: {:?}", build_info.get_repository_url()); println!("repository url: {:?}", build_info.get_repository_url());
println!("repository branch: {:?}", build_info.get_repository_branch()); println!(
"repository branch: {:?}",
build_info.get_repository_branch()
);
println!("repository tag: {:?}", build_info.get_repository_tag()); println!("repository tag: {:?}", build_info.get_repository_tag());
Ok(()) Ok(())
} }

View File

@@ -1,4 +1,4 @@
use anyhow::{anyhow, Error, Result}; use anyhow::{Error, Result, anyhow};
use std::fmt; use std::fmt;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::os::raw::c_int; use std::os::raw::c_int;

View File

@@ -7,4 +7,8 @@
#![allow(rustdoc::invalid_html_tags)] #![allow(rustdoc::invalid_html_tags)]
#![allow(clippy::missing_safety_doc)] #![allow(clippy::missing_safety_doc)]
#[cfg(docsrs)]
include!("lib_czi_api.rs"); include!("lib_czi_api.rs");
#[cfg(not(docsrs))]
include!(concat!(env!("OUT_DIR"), "/lib_czi_api.rs"));

Binary file not shown.