diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..18d0e47 --- /dev/null +++ b/.github/workflows/publish.yml @@ -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 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..78c76b4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libczi"] + path = libczi + url = https://github.com/ZEISS/libczi.git diff --git a/Cargo.toml b/Cargo.toml index 1324604..14450cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libczirw-sys" -version = "0.1.2" +version = "0.2.0" edition = "2024" rust-version = "1.85.1" authors = ["Wim Pomp "] @@ -15,8 +15,13 @@ links = "libCZIAPI" [dependencies] anyhow = "1.0.98" +link-cplusplus = "1.0" [build-dependencies] anyhow = "1.0.98" +bindgen = "0.72.0" cmake = "0.1.54" -git2 = "0.20.2" \ No newline at end of file +regex = "1.11.1" + +[features] +dynamic = [] \ No newline at end of file diff --git a/README.md b/README.md index 7157219..47deb4e 100644 --- a/README.md +++ b/README.md @@ -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. 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. \ No newline at end of file +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. \ No newline at end of file diff --git a/build.rs b/build.rs index a494188..f31111a 100644 --- a/build.rs +++ b/build.rs @@ -1,90 +1,171 @@ -use anyhow::Result; +extern crate bindgen; + +use anyhow::{Error, Result}; use std::env; -use std::fs::OpenOptions; -use std::io::{Read, Seek, Write}; 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<()> { if env::var("DOCS_RS").is_err() { let out_dir = PathBuf::from(env::var("OUT_DIR")?).canonicalize()?; - let libczi_dir = out_dir.join("libczirw"); - let rep = if !libczi_dir.exists() { - git2::Repository::clone("https://github.com/ZEISS/libczi.git", &libczi_dir) - .expect("unable to clone libczirw") - } else { - git2::Repository::open(&libczi_dir)? - }; - let (object, _) = rep.revparse_ext("494ac62f853de6ab86458f167fd85a03ee6d4f7e")?; - rep.checkout_tree(&object, None)?; + let libczi_dir = PathBuf::from("libczi"); + let libczi_src = libczi_dir.join("Src/libCZI"); + let libcziapi_inc = libczi_dir.join("Src/libCZIAPI/inc"); + let libcziapi_src = libczi_dir.join("Src/libCZIAPI/src"); + let libcziapi_h = libcziapi_inc.join("libCZIApi.h"); let dst = cmake::Config::new(&libczi_dir) + .cxxflag("-fms-extensions") .define("LIBCZI_BUILD_UNITTESTS", "OFF") .define("LIBCZI_BUILD_CZICMD", "OFF") .define("LIBCZI_BUILD_DYNLIB", "OFF") .define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_EIGEN3", "OFF") .define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_ZSTD", "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_PREFER_EXTERNALPACKAGE_RAPIDJSON", "OFF") .define("LIBCZI_BUILD_LIBCZIAPI", "ON") .build(); - // let libcziapi_inc = libczi_dir.join("Src/libCZIAPI/inc"); - // let libczi_src = libczi_dir.join("Src/libCZI"); - // let libcziapi_src = libczi_dir.join("Src/libCZIAPI/src"); - // let libczi_h = libcziapi_inc.join("libCZIApi.h"); - - let import_export = libczi_dir.join("Src/libCZIAPI/inc/importexport.h"); - { - 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"); + #[cfg(not(feature = "dynamic"))] + let bindings = { + let mut libcziapi_a = out_dir.join("build/Src/libCZIAPI/liblibCZIAPIStatic.a"); + if !libcziapi_a.exists() { + libcziapi_a = out_dir.join("build/Src/libCZIAPI/liblibCZIAPIStatic.lib"); + } + bindgen::Builder::default().parse_callbacks(Box::new(DeMangler::new(libcziapi_a)?)) }; - // let bindings = bindgen::Builder::default() - // .clang_args([ - // "-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!"); + #[cfg(feature = "dynamic")] + let bindings = bindgen::Builder::default(); - println!( - "cargo:rustc-link-search=native={}", - dst.join("build/Src/libCZIAPI").display() - ); - println!("cargo:rustc-link-lib=libCZIAPI"); + 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!( + "cargo:rustc-link-search=native={}", + dst.join("build/Src/libCZIAPI").display() + ); + println!("cargo:rustc-link-lib=libCZIAPI"); + } } println!("cargo::rerun-if-changed=build.rs"); Ok(()) } + +#[cfg(not(feature = "dynamic"))] +#[derive(Debug)] +struct DeMangler { + map: HashMap, +} + +#[cfg(not(feature = "dynamic"))] +impl DeMangler { + fn new(a_file: PathBuf) -> Result { + 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 { + self.map.get(item_info.name).cloned() + } +} diff --git a/libczi b/libczi new file mode 160000 index 0000000..494ac62 --- /dev/null +++ b/libczi @@ -0,0 +1 @@ +Subproject commit 494ac62f853de6ab86458f167fd85a03ee6d4f7e diff --git a/src/functions.rs b/src/functions.rs index 9ca3844..8fc1412 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -137,7 +137,7 @@ impl CziReader { /// available. /// - In the returned 'SubBlockStatisticsInteropEx' structure, the 'number_of_per_scenes_bounding_boxes' field will be set to the number of per-scene /// information that is put into this struct (which may be less than number of scenes that are available). - /// + /// /// So, the caller is expected to check the returned 'number_of_per_channel_bounding_boxes' to see how many per-channel bounding boxes are available. /// If this number is greater than the number of elements (given with the 'number_of_per_scenes_bounding_boxes' value in the 'statistics' structure), /// then the caller should allocate a larger 'statistics' structure and call this function again (with an increased 'number_of_per_scenes_bounding_boxes'). @@ -640,7 +640,13 @@ impl LockedBitmap { /// \\param \[out\] ptr Pointer to the memory location where the bitmap is to be copied to. /// /// \\returns A LibCZIApiErrorCode. - pub fn copy(&self, width: u32, height: u32, pixel_type: PixelType, stride: u32) -> Result { + pub fn copy( + &self, + width: u32, + height: u32, + pixel_type: PixelType, + stride: u32, + ) -> Result { let mut data = MaybeUninit::::uninit(); LibCZIApiError::try_from(unsafe { libCZI_BitmapCopyTo( diff --git a/src/handle.rs b/src/handle.rs index b18adf7..d0096a8 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -3,60 +3,61 @@ use crate::sys::*; use std::mem::MaybeUninit; use std::ops::Deref; - /// CZI-reader object. #[derive(Clone, Debug)] -pub struct CziReader(pub (crate) CziReaderObjectHandle); +pub struct CziReader(pub(crate) CziReaderObjectHandle); /// sub-block object. #[derive(Clone, Debug)] -pub struct SubBlock(pub (crate) SubBlockObjectHandle); +pub struct SubBlock(pub(crate) SubBlockObjectHandle); /// input stream object. #[derive(Clone, Debug)] -pub struct InputStream(pub (crate) InputStreamObjectHandle); +pub struct InputStream(pub(crate) InputStreamObjectHandle); /// output stream object. #[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 /// freed with 'libCZI_Free'. /// TODO(JBL): this is not really used so far, should be removed I guess. #[derive(Clone, Debug)] -pub struct MemoryAllocation(pub (crate) MemoryAllocationObjectHandle); +pub struct MemoryAllocation(pub(crate) MemoryAllocationObjectHandle); /// bitmap object. #[derive(Clone, Debug)] -pub struct Bitmap(pub (crate) BitmapObjectHandle); +pub struct Bitmap(pub(crate) BitmapObjectHandle); /// metadata segment object. #[derive(Clone, Debug)] -pub struct MetadataSegment(pub (crate) MetadataSegmentObjectHandle); +pub struct MetadataSegment(pub(crate) MetadataSegmentObjectHandle); /// attachment object. #[derive(Clone, Debug)] -pub struct Attachment(pub (crate) AttachmentObjectHandle); +pub struct Attachment(pub(crate) AttachmentObjectHandle); /// writer object. #[derive(Clone, Debug)] -pub struct CziWriter(pub (crate) CziWriterObjectHandle); +pub struct CziWriter(pub(crate) CziWriterObjectHandle); /// single-channel-scaling-tile-accessor. #[derive(Clone, Debug)] -pub struct SingleChannelScalingTileAccessor(pub (crate) SingleChannelScalingTileAccessorObjectHandle); +pub struct SingleChannelScalingTileAccessor( + pub(crate) SingleChannelScalingTileAccessorObjectHandle, +); /// document info object. #[derive(Clone, Debug)] -pub struct CziDocumentInfo(pub (crate) CziDocumentInfoHandle); +pub struct CziDocumentInfo(pub(crate) CziDocumentInfoHandle); /// display settings object. #[derive(Clone, Debug)] -pub struct DisplaySettings(pub (crate) DisplaySettingsHandle); +pub struct DisplaySettings(pub(crate) DisplaySettingsHandle); /// channel display settings object. #[derive(Clone, Debug)] -pub struct ChannelDisplaySettings(pub (crate) ChannelDisplaySettingsHandle); +pub struct ChannelDisplaySettings(pub(crate) ChannelDisplaySettingsHandle); macro_rules! impl_struct { ($($n:ident: $t:ty: $s:ty $(,)?)*) => { diff --git a/src/interop.rs b/src/interop.rs index ade4c2c..66d7f07 100644 --- a/src/interop.rs +++ b/src/interop.rs @@ -8,45 +8,45 @@ use std::mem::{ManuallyDrop, MaybeUninit}; /// This struct contains the version information of the libCZIApi-library. For versioning libCZI, SemVer2 () is used. /// Note that the value of the tweak version number does not have a meaning (as far as SemVer2 is concerned). #[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. /// Note that all strings must be freed by the caller (using libCZI_Free). #[derive(Clone, Debug)] -pub struct LibCZIBuildInformation(pub (crate) LibCZIBuildInformationInterop); +pub struct LibCZIBuildInformation(pub(crate) LibCZIBuildInformationInterop); #[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. #[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, /// 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 /// may extend beyond calling the 'libCZI_ReleaseInputStream' function for the corresponding stream-object. #[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, /// 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 /// may extend beyond calling the 'libCZI_ReleaseOutputStream' function for the corresponding stream-object. #[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. #[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. #[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. #[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. /// 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 /// 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)] -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. /// 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 /// 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)] -pub struct Coordinate(pub (crate) CoordinateInterop); +pub struct Coordinate(pub(crate) CoordinateInterop); /// This structure contains the bounding boxes for a scene. #[derive(Clone, Debug)] -pub struct BoundingBoxes(pub (crate) BoundingBoxesInterop); +pub struct BoundingBoxes(pub(crate) BoundingBoxesInterop); /// This structure contains basic statistics about an CZI-document. #[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. #[derive(Debug)] -pub struct SubBlockStatisticsEx(pub (crate) SubBlockStatisticsInteropEx); +pub struct SubBlockStatisticsEx(pub(crate) SubBlockStatisticsInteropEx); #[derive(Clone, Debug)] -pub struct MetadataAsXml(pub (crate) MetadataAsXmlInterop); +pub struct MetadataAsXml(pub(crate) MetadataAsXmlInterop); /// Information about the bitmap represented by a bitmap-object. #[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 /// access to the pixel data. #[derive(Clone, Debug)] -pub struct BitmapLockInfo(pub (crate) BitmapLockInfoInterop); +pub struct BitmapLockInfo(pub(crate) BitmapLockInfoInterop); /// This structure contains the information about a sub-block. #[derive(Clone, Debug)] -pub struct SubBlockInfo(pub (crate) SubBlockInfoInterop); +pub struct SubBlockInfo(pub(crate) SubBlockInfoInterop); /// 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 @@ -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 /// for releasing the memory is with the caller). #[derive(Clone, Debug)] -pub struct AttachmentInfo(pub (crate) AttachmentInfoInterop); +pub struct AttachmentInfo(pub(crate) AttachmentInfoInterop); /// This structure contains the information about file-header. #[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. #[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. #[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. #[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. #[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. #[derive(Clone, Debug)] -pub struct CompositionChannelInfo(pub (crate) CompositionChannelInfoInterop); +pub struct CompositionChannelInfo(pub(crate) CompositionChannelInfoInterop); /// This structure gathers the information about the scaling. #[derive(Clone, Debug)] -pub struct ScalingInfo(pub (crate) ScalingInfoInterop); +pub struct ScalingInfo(pub(crate) ScalingInfoInterop); macro_rules! impl_ptr { ($($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 + // ) -> 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::, __IncompleteArrayField>(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 { + // 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) { + // 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 { - fn get_data(&self) -> Result { + pub fn get_data(&self) -> Result { let xml_data = unsafe { Vec::from_raw_parts( self.0.data as *mut u8, @@ -697,12 +772,8 @@ impl AddSubBlockInfo { physical_height: i32, pixel_type: PixelType, compression_mode_raw: i32, - size_data: u32, data: &[u8], - stride: u32, - size_metadata: u32, metadata: &[u8], - size_attachment: u32, attachment: &[u8], ) -> Self { let data = ManuallyDrop::new(data.to_vec()); @@ -721,12 +792,12 @@ impl AddSubBlockInfo { physical_height, pixel_type: pixel_type.into(), compression_mode_raw, - size_data, + size_data: data.len() as u32, data: data.as_ptr() as *const c_void, - stride, - size_metadata, + stride: 1, + size_metadata: metadata.len() as u32, metadata: metadata.as_ptr() as *const c_void, - size_attachment, + size_attachment: attachment.len() as u32, 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) { 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]) { let data = ManuallyDrop::new(data.to_vec()); self.0.data = data.as_ptr() as *const c_void; 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]) { let metadata = ManuallyDrop::new(metadata.to_vec()); self.0.metadata = metadata.as_ptr() as *const c_void; 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]) { let attachment = ManuallyDrop::new(attachment.to_vec()); self.0.attachment = attachment.as_ptr() as *const c_void; @@ -863,7 +925,6 @@ impl AddAttachmentInfo { guid: [u8; 16], content_file_type: [u8; 8], name: [u8; 80], - size_attachment_data: u32, attachment_data: &[u8], ) -> Self { let attachment_data = ManuallyDrop::new(attachment_data.to_vec()); @@ -871,7 +932,7 @@ impl AddAttachmentInfo { guid, contentFileType: content_file_type, name, - size_attachment_data, + size_attachment_data: attachment_data.len() as u32, 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]) { 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]) { let attachment_data = ManuallyDrop::new(attachment_data.to_vec()); self.0.attachment_data = attachment_data.as_ptr() as *const c_void; @@ -916,10 +974,10 @@ impl AddAttachmentInfo { } impl WriteMetadataInfo { - pub fn new(size_metadata: u32, metadata: &[u8]) -> Self { + pub fn new(metadata: &[u8]) -> Self { let metadata = ManuallyDrop::new(metadata.to_vec()); Self(WriteMetadataInfoInterop { - size_metadata, + size_metadata: metadata.len() as u32, 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]) { let metadata = ManuallyDrop::new(metadata.to_vec()); self.0.metadata = metadata.as_ptr() as *const c_void; diff --git a/src/lib.rs b/src/lib.rs index 4be7082..f5621ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +extern crate link_cplusplus; + mod functions; mod handle; mod interop; @@ -7,7 +9,7 @@ pub mod sys; pub use functions::*; pub use handle::*; pub use interop::*; -pub use misc::{LibCZIApiError, RawDataType, PixelType}; +pub use misc::{LibCZIApiError, PixelType, RawDataType}; #[cfg(test)] mod tests { @@ -15,6 +17,7 @@ mod tests { use crate::interop::{LibCZIBuildInformation, ReaderOpenInfo}; use anyhow::{Error, Result}; use std::env; + use std::path::PathBuf; #[test] fn test_libczi_xml() -> Result<()> { @@ -37,9 +40,7 @@ mod tests { #[test] fn test_libczi_pyramid_statistics() -> Result<()> { - let path = env::home_dir() - .unwrap() - .join("code/rust/ndbioimage/tests/files/Experiment-2029.czi"); + let path = PathBuf::from("test-files/Experiment-2029.czi"); assert!(path.exists()); let czi = CziReader::create()?; let stream = InputStream::create_from_file_utf8( @@ -54,9 +55,7 @@ mod tests { #[test] fn test_libczi_document_info() -> Result<()> { - let path = env::home_dir() - .unwrap() - .join("code/rust/ndbioimage/tests/files/Experiment-2029.czi"); + let path = PathBuf::from("test-files/Experiment-2029.czi"); assert!(path.exists()); let czi = CziReader::create()?; let stream = InputStream::create_from_file_utf8( @@ -67,16 +66,25 @@ mod tests { let metadata_segment = czi.get_metadata_segment()?; let document_info = metadata_segment.get_czi_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(()) } #[test] fn test_lib_czi_build_information() -> Result<()> { 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 branch: {:?}", build_info.get_repository_branch()); + println!( + "repository branch: {:?}", + build_info.get_repository_branch() + ); println!("repository tag: {:?}", build_info.get_repository_tag()); Ok(()) } diff --git a/src/misc.rs b/src/misc.rs index 5781ecb..b475148 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Error, Result}; +use anyhow::{Error, Result, anyhow}; use std::fmt; use std::mem::MaybeUninit; use std::os::raw::c_int; diff --git a/src/sys.rs b/src/sys.rs index bba9147..62d1490 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -7,4 +7,8 @@ #![allow(rustdoc::invalid_html_tags)] #![allow(clippy::missing_safety_doc)] +#[cfg(docsrs)] include!("lib_czi_api.rs"); + +#[cfg(not(docsrs))] +include!(concat!(env!("OUT_DIR"), "/lib_czi_api.rs")); diff --git a/test-files/Experiment-2029.czi b/test-files/Experiment-2029.czi new file mode 100644 index 0000000..400f985 Binary files /dev/null and b/test-files/Experiment-2029.czi differ