commit 3e98e7e86f65edac4ed8b715431a282263d274b4 Author: Wim Pomp Date: Sun Aug 10 17:34:35 2025 +0200 - first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3bc6b6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,75 @@ +/target +/Cargo.lock + +# Byte-compiled / optimized / DLL files +__pycache__/ +.pytest_cache/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +.venv/ +env/ +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +include/ +man/ +venv/ +*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt +pip-selfcheck.json + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Django stuff: +*.log +*.pot + +.DS_Store + +# Sphinx documentation +docs/_build/ + +# PyCharm +.idea/ + +# VSCode +.vscode/ + +# Pyenv +.python-version + +/tests/files/* diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e145b42 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "libczirw-sys" +version = "0.1.0" +edition = "2024" +rust-version = "1.85.1" +authors = ["Wim Pomp "] +license = "MIT" +description = "Wrapper around libCZIAPI" +homepage = "https://github.com/wimpomp/libczirw-sys" +repository = "https://github.com/wimpomp/libczirw-sys" +readme = "README.md" +keywords = ["czi", "zeiss", "libczi"] +categories = ["multimedia::images", "science"] + +[dependencies] +anyhow = "1.0.98" + +[build-dependencies] +anyhow = "1.0.98" +bindgen = "0.72.0" +cmake = "0.1.54" +git2 = "0.20.2" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f9b7904 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2025 Wim Pomp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7157219 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# 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. + +This code is licensed with an MIT license, but Zeiss' libCZI has a LGPL license. \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..f3f09a4 --- /dev/null +++ b/build.rs @@ -0,0 +1,90 @@ +use anyhow::{Error, Result}; +use std::env; +use std::fs::OpenOptions; +use std::io::{Read, Seek, Write}; +use std::path::PathBuf; + +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 dst = cmake::Config::new(&libczi_dir) + .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_AZURESDK_BASED_STREAM", "OFF") + .define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_RAPIDJSON", "OFF") + .define("LIBCZI_BUILD_LIBCZIAPI", "ON") + .build(); + + 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 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"); + }; + + 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!"); + + 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(()) +} diff --git a/src/functions.rs b/src/functions.rs new file mode 100644 index 0000000..9ca3844 --- /dev/null +++ b/src/functions.rs @@ -0,0 +1,1158 @@ +use crate::handle::*; +use crate::interop::*; +use crate::misc::*; +use crate::sys::*; +use anyhow::{Error, Result}; +use std::ffi::{CStr, CString, c_char, c_int, c_ulong, c_void}; +use std::mem::{ManuallyDrop, MaybeUninit}; +use std::ops::Deref; + +/// Release the memory - this function is to be used for freeing memory allocated by the libCZIApi-library +/// (and returned to the caller). +/// +/// \\param data Pointer to the memory to be freed. +pub fn free(data: T) { + let ptr = data.as_mut_ptr() as *mut c_void; + unsafe { libCZI_Free(ptr) }; +} + +/// Allocate memory of the specified size. +/// +/// \\param size The size of the memory block to be allocated in bytes. +/// \\param \[out\] data If successful, a pointer to the allocated memory is put here. The memory must be freed using 'libCZI_Free'. +/// +/// \\returns An error-code indicating success or failure of the operation. +pub fn allocate_memory(size: usize) -> Result> { + let mut data = MaybeUninit::::uninit(); + let mut ptr = data.as_mut_ptr() as *mut c_void; + LibCZIApiError::try_from(unsafe { libCZI_AllocateMemory(size as c_ulong, &mut ptr) })?; + Ok(data) +} + +impl LibCZIVersionInfo { + /// Get version information about the libCZIApi-library. + /// + /// \\param \[out\] version_info If successful, the version information is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_lib_czi_version_info() -> Result { + let mut version_info = MaybeUninit::uninit(); + let ptr = version_info.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_GetLibCZIVersionInfo(ptr) })?; + Ok(unsafe { LibCZIVersionInfo::assume_init(version_info) }) + } +} + +impl LibCZIBuildInformation { + /// Get information about the build of the libCZIApi-library. + /// + /// \\param \[out\] build_info If successful, the build information is put here. Note that all strings must be freed by the caller (using 'libCZI_Free'). + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get() -> Result { + let mut build_info = MaybeUninit::uninit(); + let ptr = build_info.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_GetLibCZIBuildInformation(ptr) })?; + Ok(unsafe { LibCZIBuildInformation::assume_init(build_info) }) + } +} + +impl CziReader { + /// Create a new CZI-reader object. + /// + /// \\param \[out\] reader_object If the operation is successful, a handle to the newly created reader object is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn create() -> Result { + let mut reader = MaybeUninit::uninit(); + let ptr = reader.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_CreateReader(ptr) })?; + Ok(unsafe { Self::assume_init(reader) }) + } + + /// Instruct the specified reader-object to open a CZI-document. The 'open_info' parameter contains + /// a handle to a stream-object which is used to read the document. + /// + /// \\param reader_object A handle representing the reader-object. + /// \\param open_info Parameters controlling the operation. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn open(&self, open_info: ReaderOpenInfo) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReaderOpen(**self, open_info.as_ptr()) })?; + Ok(()) + } + + /// Get information about the file-header of the CZI document. The information is put into the 'file_header_info_interop' structure. + /// This file_header_info_interop structure contains the GUID of the CZI document and the version levels of CZI. + /// + /// \\param reader_object The reader object. + /// \\param \[out\] file_header_info_interop If successful, the retrieved information is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_file_header_info(&self) -> Result { + let mut file_header_info = MaybeUninit::uninit(); + let ptr = file_header_info.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_ReaderGetFileHeaderInfo(**self, ptr) })?; + Ok(unsafe { FileHeaderInfo::assume_init(file_header_info) }) + } + + /// Reads the sub-block identified by the specified index. If there is no sub-block present (for the + /// specified index) then the function returns 'LibCZIApi_ErrorCode_OK', but the 'sub_block_object' + /// is set to 'kInvalidObjectHandle'. + /// + /// \\param reader_object The reader object. + /// \\param index Index of the sub-block. + /// \\param \[out\] sub_block_object If successful, a handle to the sub-block object is put here; otherwise 'kInvalidObjectHandle'. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn read_sub_block(&self, index: i32) -> Result { + let mut sub_block = MaybeUninit::uninit(); + let ptr = sub_block.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { + libCZI_ReaderReadSubBlock(**self, index as c_int, ptr) + })?; + Ok(unsafe { SubBlock::assume_init(sub_block) }) + } + + /// Get statistics about the sub-blocks in the CZI-document. This function provides a simple version of the statistics, the + /// information retrieved does not include the per-scene statistics. + /// + /// \\param reader_object The reader object. + /// \\param \[out\] statistics If non-null, the simple statistics will be put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_statistics_simple(&self) -> Result { + let mut statistics = MaybeUninit::uninit(); + let ptr = statistics.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_ReaderGetStatisticsSimple(**self, ptr) })?; + Ok(unsafe { SubBlockStatistics::assume_init(statistics) }) + } + + /// Get extended statistics about the sub-blocks in the CZI-document. This function provides a more detailed version of the statistics, + /// including the per-scene statistics. Note that the statistics is of variable size, and the semantic is as follows: + /// - On input, the argument 'number_of_per_channel_bounding_boxes' must point to an integer which describes the size of the argument 'statistics'. + /// This number gives how many elements the array 'per_scenes_bounding_boxes' in 'SubBlockStatisticsInteropEx' can hold. Only that number of + /// per-scene information elements will be put into the 'statistics' structure at most, in any case. + /// - On output, the argument 'number_of_per_channel_bounding_boxes' will be set to the number of per-channel bounding boxes that were actually + /// 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'). + /// + /// \\param reader_object The reader object. + /// \\param \[out\] statistics If non-null, the statistics will be put here. + /// \\param \[in,out\] number_of_per_channel_bounding_boxes On input, it gives the number of elements that can be put into the 'per_scenes_bounding_boxes' array. + /// On output, it gives the number of elements which are available. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_statistics_ex( + &self, + number_of_per_channel_bounding_boxes: i32, + ) -> Result<(SubBlockStatisticsEx, i32)> { + let mut statistics = MaybeUninit::uninit(); + let ptr = statistics.as_mut_ptr(); + let number_of_per_channel_bounding_boxes = + Box::into_raw(Box::new(number_of_per_channel_bounding_boxes)); + LibCZIApiError::try_from(unsafe { + libCZI_ReaderGetStatisticsEx(**self, ptr, number_of_per_channel_bounding_boxes) + })?; + Ok(unsafe { + ( + SubBlockStatisticsEx::assume_init(statistics), + *Box::from_raw(number_of_per_channel_bounding_boxes), + ) + }) + } + + /// Get \"pyramid-statistics\" about the CZI-document. This function provides a JSON-formatted string which contains information about the pyramid. + /// The JSON-schema is as follows: + /// \\code + /// { + /// \"scenePyramidStatistics\": { + /// \: [ + /// { + /// \"layerInfo\": { + /// \"minificationFactor\": \, + /// \"pyramidLayerNo\" : \ + /// }, + /// \"count\" : \ + /// } + /// ]} + /// } + /// \\endcode + /// It resembles the corresponding C++-structure 'PyramidStatistics' in the libCZI-library. + /// + /// \\param reader_object The reader object. + /// \\param \[out\] pyramid_statistics_as_json If successful, a pointer to a JSON-formatted string is placed here. The caller + /// is responsible for freeing this memory (by calling libCZI_Free). + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_pyramid_statistics(&self) -> Result { + let mut ptr = MaybeUninit::<*mut c_char>::uninit(); + LibCZIApiError::try_from(unsafe { + libCZI_ReaderGetPyramidStatistics(**self, ptr.as_mut_ptr()) + })?; + let ptr = unsafe { ptr.assume_init() }; + let statistics = unsafe { CStr::from_ptr(ptr) }.to_str()?.to_owned(); + unsafe { libCZI_Free(ptr as *mut c_void) }; + Ok(statistics) + } + + /// Create a metadata-segment object from the reader-object. The metadata-segment object can be used to retrieve the XML-metadata of the CZI-document. + /// + /// \\param reader_object The reader object. + /// \\param \[out\] metadata_segment_object If successful, a handle to the metadata-segment object is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_metadata_segment(&self) -> Result { + let mut metadata_segment = MaybeUninit::uninit(); + let ptr = metadata_segment.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_ReaderGetMetadataSegment(**self, ptr) })?; + Ok(unsafe { MetadataSegment::assume_init(metadata_segment) }) + } + + /// Get the number of attachments available. + /// + /// \\param reader_object The reader object. + /// \\param \[out\] count The number of available attachments is put here. + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_attachment_count(&self) -> Result { + let mut count = MaybeUninit::::uninit(); + LibCZIApiError::try_from(unsafe { + libCZI_ReaderGetAttachmentCount(**self, count.as_mut_ptr()) + })?; + Ok(unsafe { count.assume_init() }) + } + + /// Get information about the attachment at the specified index. The information is put into the 'attachment_info_interop' structure. + /// If the index is not valid, then the function returns 'LibCZIApi_ErrorCode_IndexOutOfRange'. + /// + /// \\param reader_object The reader object. + /// \\param index The index of the attachment to query information for. + /// \\param \[out\] attachment_info_interop If successful, the retrieved information is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_attachment_info_from_directory(&self, index: i32) -> Result { + let mut attachment_info = MaybeUninit::uninit(); + let ptr = attachment_info.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { + libCZI_ReaderGetAttachmentInfoFromDirectory(**self, index, ptr) + })?; + Ok(unsafe { AttachmentInfo::assume_init(attachment_info) }) + } + + /// Read the attachment with the specified index and create an attachment object representing it. If the specified index + /// is invalid, then the returned attachment-object handle will have the value 'kInvalidObjectHandle'. + /// \\param reader_object The reader object. + /// \\param index The index of the attachment to get. + /// \\param \[out\] attachment_object If successful and index is valid, a handle representing the attachment object is put here. If the index is + /// invalid, then the handle will have the value 'kInvalidObjectHandle'. + /// \\returns An error-code indicating success or failure of the operation. + pub fn read_attachment(&self, index: i32) -> Result { + let mut attachment = MaybeUninit::uninit(); + let ptr = attachment.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_ReaderReadAttachment(**self, index, ptr) })?; + Ok(unsafe { Attachment::assume_init(attachment) }) + } + + /// Release the specified reader-object. After this function is called, the handle is no + /// longer valid. + /// + /// \\param reader_object The reader object. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn release(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReleaseReader(**self) })?; + Ok(()) + } + + /// Get information about the sub-block with the specified index. The information is put into the 'sub_block_info_interop' structure. + /// If the index is not valid, then the function returns 'LibCZIApi_ErrorCode_IndexOutOfRange'. + /// + /// \\param reader_object The reader object. + /// \\param index The index of the attachment to query information for. + /// \\param \[out\] sub_block_info_interop If successful, the retrieved information is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn try_get_sub_block_info_for_index(&self, index: i32) -> Result { + let mut sub_block_info = MaybeUninit::uninit(); + let ptr = sub_block_info.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_TryGetSubBlockInfoForIndex(**self, index, ptr) })?; + Ok(unsafe { SubBlockInfo::assume_init(sub_block_info) }) + } + + /// Create a single channel scaling tile accessor. + /// + /// \\param reader_object A handle representing the reader-object. + /// \\param accessor_object \[out\] If the operation is successful, a handle to the newly created single-channel-scaling-tile-accessor is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn create_single_channel_tile_accessor(&self) -> Result { + let mut accessor = MaybeUninit::uninit(); + let ptr = accessor.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_CreateSingleChannelTileAccessor(**self, ptr) })?; + Ok(unsafe { SingleChannelScalingTileAccessor::assume_init(accessor) }) + } +} + +impl Drop for CziReader { + fn drop(&mut self) { + self.release().ok(); + } +} + +/// Get information about the stream class at the specified index. +/// +/// \\param index Zero-based index of the stream class to query information about. +/// \\param \[out\] input_stream_class_info If successful, information about the stream class is put here. Note that the strings in the structure +/// must be freed (by the caller) using 'libCZI_Free'. +/// +/// \\returns An error-code indicating success or failure of the operation. +pub fn get_stream_classes_count(index: i32) -> Result { + let mut input_stream_class_info = MaybeUninit::uninit(); + let ptr = input_stream_class_info.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_GetStreamClassInfo(index, ptr) })?; + Ok(unsafe { InputStreamClassInfo::assume_init(input_stream_class_info) }) +} + +impl InputStream { + /// Create an input stream object of the specified type, using the specified JSON-formatted property bag and + /// the specified file identifier as input. + /// + /// \\param stream_class_name Name of the stream class to be instantiated. + /// \\param creation_property_bag JSON formatted string (containing additional parameters for the stream creation) in UTF8-encoding. + /// \\param stream_identifier The filename (or, more generally, a URI of some sort) identifying the file to be opened in UTF8-encoding. + /// \\param \[out\] stream_object If successful, a handle representing the newly created stream object is put here. + /// + /// \\returns An error-code that indicates whether the operation is successful or not. + pub fn create( + stream_class_name: impl AsRef, + creation_property_bag: impl AsRef, + stream_identifier: impl AsRef, + ) -> Result { + let mut stream = MaybeUninit::uninit(); + let ptr = stream.as_mut_ptr(); + let stream_class_name = ManuallyDrop::new(CString::new(stream_class_name.as_ref())?); + let creation_property_bag = + ManuallyDrop::new(CString::new(creation_property_bag.as_ref())?); + let stream_identifier = ManuallyDrop::new(CString::new(stream_identifier.as_ref())?); + LibCZIApiError::try_from(unsafe { + libCZI_CreateInputStream( + stream_class_name.as_ptr(), + creation_property_bag.as_ptr(), + stream_identifier.as_ptr(), + ptr, + ) + })?; + Ok(unsafe { Self::assume_init(stream) }) + } + + /// Create an input stream object for a file identified by its filename, which is given as a wide string. Note that wchar_t on + /// Windows is 16-bit wide, and on Unix-like systems it is 32-bit wide. + /// + /// \\param \[in\] filename Filename of the file which is to be opened (zero terminated wide string). Note that on Windows, this + /// is a string with 16-bit code units, and on Unix-like systems it is typically a string with 32-bit code units. + /// + /// \\param \[out\] stream_object The output stream object that will hold the created stream. + /// \\return An error-code that indicates whether the operation is successful or not. Non-positive values indicates successful, positive values + /// indicates unsuccessful operation. + pub fn create_from_file_wide(file_name: Vec) -> Result { + let mut stream = MaybeUninit::uninit(); + let ptr = stream.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { + libCZI_CreateInputStreamFromFileWide(file_name.as_ptr(), ptr) + })?; + Ok(unsafe { Self::assume_init(stream) }) + } + + /// Create an input stream object for a file identified by its filename, which is given as an UTF8-encoded string. + /// + /// \\param \[in\] filename Filename of the file which is to be opened (in UTF8 encoding). + /// \\param \[out\] stream_object The output stream object that will hold the created stream. + /// \\return An error-code that indicates whether the operation is successful or not. Non-positive values indicates successful, positive values + /// indicates unsuccessful operation. + pub fn create_from_file_utf8>(file_name: S) -> Result { + let mut stream = MaybeUninit::uninit(); + let ptr = stream.as_mut_ptr(); + let file_name = ManuallyDrop::new(CString::new(file_name.as_ref())?); + // let file_name = file_name.as_ref().as_bytes().to_vec(); + LibCZIApiError::try_from(unsafe { + libCZI_CreateInputStreamFromFileUTF8(file_name.as_ptr() as *const c_char, ptr) + })?; + Ok(unsafe { Self::assume_init(stream) }) + } + + /// Create an input stream object which is using externally provided functions for operation + /// and reading the data. Please refer to the documentation of + /// 'ExternalInputStreamStructInterop' for more information. + /// + /// \\param external_input_stream_struct Structure containing the information about the externally provided functions. + /// \\param \[out\] stream_object If successful, the handle to the newly created input stream object is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn create_from_external(external_input_stream: ExternalInputStreamStruct) -> Result { + let mut stream = MaybeUninit::uninit(); + let ptr = stream.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { + libCZI_CreateInputStreamFromExternal(external_input_stream.as_ptr(), ptr) + })?; + Ok(unsafe { Self::assume_init(stream) }) + } + + /// Release the specified input stream object. After this function is called, the handle is no + /// longer valid. Note that calling this function will only decrement the usage count of the + /// underlying object; whereas the object itself (and the resources it holds) will only be + /// released when the usage count reaches zero. + /// + /// \\param stream_object The input stream object. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn release(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReleaseInputStream(**self) })?; + Ok(()) + } +} + +impl Drop for InputStream { + fn drop(&mut self) { + self.release().ok(); + } +} + +impl SubBlock { + /// Create a bitmap object from the specified sub-block object. The bitmap object can be used to access the pixel + /// data contained in the sub-block. If the subblock contains compressed data, then decompression will be performed + /// in this call. + /// + /// \\param sub_block_object The sub-block object. + /// \\param \[out\] bitmap_object If successful, the handle to the newly created bitmap object is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn create_bitmap(&self) -> Result { + let mut bitmap = MaybeUninit::uninit(); + let ptr = bitmap.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_SubBlockCreateBitmap(**self, ptr) })?; + Ok(unsafe { Bitmap::assume_init(bitmap) }) + } + + /// Get Information about the sub-block. + /// + /// \\param sub_block_object The sub-block object. + /// \\param \[out\] sub_block_info If successful, information about the sub-block object is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_info(&self) -> Result { + let mut sub_block_info = MaybeUninit::uninit(); + let ptr = sub_block_info.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_SubBlockGetInfo(**self, ptr) })?; + Ok(unsafe { SubBlockInfo::assume_init(sub_block_info) }) + } + + /// Copy the raw data from the specified sub-block object to the specified memory buffer. The value of the 'size' parameter + /// on input is the size of the buffer pointed to by 'data'. On output, the value of 'size' is the actual size of the data. At most + /// the initial value of 'size' bytes are copied to the buffer. If the initial value of 'size' is zero (0) or 'data' is null, then + /// no data is copied. + /// For the 'type' parameter, the following values are valid: 0 (data) and 1 (metadata). + /// For 0 (data), the data is the raw pixel data of the bitmap. This data may be compressed. + /// For 1 (metadata), the data is the raw metadata in XML-format (UTF8-encoded). + /// + /// \\param sub_block_object The sub block object. + /// \\param type The type - 0 for \"pixel-data\", 1 for \"sub-block metadata\". + /// \\param \[in,out\] size On input, the size of the memory block pointed to by 'data', on output the actual size of the available data. + /// \\param \[out\] data Pointer where the data is to be copied to. At most the initial content of 'size' bytes are copied. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_raw_data(&self, tp: RawDataType, size: i32) -> Result<(i32, Vec)> { + let mut data = Vec::::with_capacity(size as usize); + let size = Box::into_raw(Box::new(size as c_ulong)); + LibCZIApiError::try_from(unsafe { + libCZI_SubBlockGetRawData(**self, tp as c_int, size, data.as_mut_ptr() as *mut c_void) + })?; + Ok((unsafe { *Box::from_raw(size) as i32 }, data)) + } + + /// Release the specified sub-block object. + /// + /// \\param sub_block_object The sub block object to be released. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn release(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReleaseSubBlock(**self) })?; + Ok(()) + } +} + +impl Drop for SubBlock { + fn drop(&mut self) { + self.release().ok(); + } +} + +impl Attachment { + /// Get information about the specified attachment object. + /// \\param attachment_object The attachment object. + /// \\param \[out\] attachment_info Information about the attachment. + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_info(&self) -> Result { + let mut attachment_info = MaybeUninit::uninit(); + let ptr = attachment_info.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_AttachmentGetInfo(**self, ptr) })?; + Ok(unsafe { AttachmentInfo::assume_init(attachment_info) }) + } + + /// Copy the raw data from the specified attachment object to the specified memory buffer. The value of the 'size' parameter + /// on input is the size of the buffer pointed to by 'data'. On output, the value of 'size' is the actual size of the data. At most + /// the initial value of 'size' bytes are copied to the buffer. If the initial value of 'size' is zero (0) or 'data' is null, then + /// no data is copied. + /// \\param attachment_object The attachment object. + /// \\param \[in,out\] size On input, the size of the memory block pointed to by 'data', on output the actual size of the available data. + /// \\param \[out\] data Pointer where the data is to be copied to. At most the initial content of 'size' bytes are copied. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_raw_data(&self, size: i32) -> Result<(i32, Vec)> { + let mut data = Vec::::with_capacity(size as usize); + let size = Box::into_raw(Box::new(size as c_ulong)); + LibCZIApiError::try_from(unsafe { + libCZI_AttachmentGetRawData(**self, size, data.as_mut_ptr() as *mut c_void) + })?; + Ok((unsafe { *Box::from_raw(size) as i32 }, data)) + } + + /// Release the specified attachment object. + /// + /// \\param attachment_object The attachment object to be released. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn release(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReleaseAttachment(**self) })?; + Ok(()) + } +} + +impl Drop for Attachment { + fn drop(&mut self) { + self.release().ok(); + } +} + +impl Bitmap { + /// Get information about the specified bitmap object. + /// + /// \\param bitmap_object The bitmap object. + /// \\param \[out\] bitmap_info If successful, information about the bitmap object is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_info(&self) -> Result { + let mut bitmap_info = MaybeUninit::uninit(); + let ptr = bitmap_info.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_BitmapGetInfo(**self, ptr) })?; + Ok(unsafe { BitmapInfo::assume_init(bitmap_info) }) + } + + /// Locks the bitmap object. Once the bitmap is locked, the pixel data can be accessed. Memory access to the + /// pixel data must only occur while the bitmap is locked. The lock must be released by calling 'libCZI_BitmapUnlock'. + /// It is a fatal error if the bitmap is destroyed while still being locked. Calls to Lock and Unlock are counted, and + /// they must be balanced. + /// + /// \\param bitmap_object The bitmap object. + /// \\param \[out\] lockInfo If successful, information about how to access the pixel data is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn lock(self) -> Result { + let mut bitmap_info = MaybeUninit::uninit(); + let ptr = bitmap_info.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_BitmapLock(*self, ptr) })?; + let bitmap_lock_info = unsafe { BitmapLockInfo::assume_init(bitmap_info) }; + Ok(LockedBitmap { + bitmap: self, + lock_info: bitmap_lock_info, + }) + } + + /// Release the specified bitmap object. + /// It is a fatal error trying to release a bitmap object that is still locked. + /// + /// \\param bitmap_object The bitmap object. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn release(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReleaseBitmap(**self) })?; + Ok(()) + } +} + +impl TryFrom<&SubBlock> for Bitmap { + type Error = Error; + + fn try_from(sub_block: &SubBlock) -> Result { + sub_block.create_bitmap() + } +} + +impl Drop for Bitmap { + fn drop(&mut self) { + self.release().ok(); + } +} + +/// Locked version of bitmap so that the data can be accessed +pub struct LockedBitmap { + bitmap: Bitmap, + pub lock_info: BitmapLockInfo, +} + +impl Deref for LockedBitmap { + type Target = Bitmap; + + fn deref(&self) -> &Self::Target { + &self.bitmap + } +} + +impl Drop for LockedBitmap { + fn drop(&mut self) { + unsafe { libCZI_BitmapUnlock(self.handle()) }; + } +} + +impl LockedBitmap { + /// Unlock the bitmap object. Once the bitmap is unlocked, the pixel data must not be accessed anymore. + /// + /// \\param bitmap_object The bitmap object. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn unlock(self) -> Result { + LibCZIApiError::try_from(unsafe { libCZI_BitmapUnlock(**self) })?; + Ok(self.bitmap.clone()) + } + + /// Copy the pixel data from the specified bitmap object to the specified memory buffer. The specified + /// destination bitmap must have same width, height and pixel type as the source bitmap. + /// + /// \\param bitmap_object The bitmap object. + /// \\param width The width of the destination bitmap. + /// \\param height The height of the destination bitmap. + /// \\param pixel_type The pixel type. + /// \\param stride The stride (given in bytes). + /// \\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 { + let mut data = MaybeUninit::::uninit(); + LibCZIApiError::try_from(unsafe { + libCZI_BitmapCopyTo( + ***self, + width, + height, + pixel_type.into(), + stride, + data.as_mut_ptr() as *mut c_void, + ) + })?; + Ok(unsafe { data.assume_init().unlock()? }) + } +} + +impl MetadataSegment { + /// Get the XML-metadata information from the specified metadata-segment object. + /// Note that the XML-metadata is returned as a pointer to the data (in the 'data' field of the 'MetadataAsXmlInterop' structure), which + /// must be freed by the caller using 'libCZI_Free'. + /// + /// \\param metadata_segment_object The metadata segment object. + /// \\param \[out\] metadata_as_xml_interop If successful, the XML-metadata information is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_metadata_as_xml(&self) -> Result { + let mut metadata_as_xml_interop = MaybeUninit::uninit(); + let ptr = metadata_as_xml_interop.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_MetadataSegmentGetMetadataAsXml(**self, ptr) })?; + Ok(unsafe { MetadataAsXml::assume_init(metadata_as_xml_interop) }) + } + + /// Create a CZI-document-information object from the specified metadata-segment object. + /// + /// \\param metadata_segment_object The metadata segment object. + /// \\param \[in,out\] czi_document_info If successful, a handle to the newly created CZI-document-info object is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_czi_document_info(&self) -> Result { + let mut czi_document = MaybeUninit::uninit(); + let ptr = czi_document.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_MetadataSegmentGetCziDocumentInfo(**self, ptr) })?; + Ok(unsafe { CziDocumentInfo::assume_init(czi_document) }) + } + + /// Release the specified metadata-segment object. + /// + /// \\param metadata_segment_object The metadata-segment object to be released. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn release(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReleaseMetadataSegment(**self) })?; + Ok(()) + } +} + +impl Drop for MetadataSegment { + fn drop(&mut self) { + self.release().ok(); + } +} + +impl CziDocumentInfo { + /// Get \"general document information\" from the specified czi-document information object. The information is returned as a JSON-formatted string. + /// The JSON returned is an object, with the following possible key-value pairs: + /// \"name\" : \, type string + /// \"title\" : \, type string + /// \"user_name\" : \<user name\>, type string + /// \"description\" : \<description\>, type string + /// \"comment\" : \<comment\>, type string + /// \"keywords\" : \<keyword1\>,\<keyword2\>,...\", type string + /// \"rating\" : \<rating\>, type integer + /// \"creation_date\" : \<creation date\>, type string, conforming to ISO 8601 + /// + /// \\param czi_document_info The CZI-document-info object. + /// \\param \[out\] general_document_info_json If successful, the general document information is put here. Note that the data must be freed using 'libCZI_Free' by the caller. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_general_document_info(&self) -> Result<String> { + let mut ptr = MaybeUninit::<*mut c_char>::uninit(); + LibCZIApiError::try_from(unsafe { + libCZI_CziDocumentInfoGetGeneralDocumentInfo( + **self, + ptr.as_mut_ptr() as *mut *mut c_void, + ) + })?; + let ptr = unsafe { ptr.assume_init() }; + let info = unsafe { CStr::from_ptr(ptr) }.to_str()?.to_owned(); + unsafe { libCZI_Free(ptr as *mut c_void) }; + Ok(info) + } + + /// Get scaling information from the specified czi-document information object. The information gives the size of an image pixels. + /// + /// \\param czi_document_info Handle to the CZI-document-info object from which the scaling information will be retrieved. + /// \\param \[out\] scaling_info_interop If successful, the scaling information is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_scaling_info(&self) -> Result<ScalingInfo> { + let mut scaling_info_interop = MaybeUninit::uninit(); + let ptr = scaling_info_interop.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_CziDocumentInfoGetScalingInfo(**self, ptr) })?; + Ok(unsafe { ScalingInfo::assume_init(scaling_info_interop) }) + } + + /// Get the display-settings from the document's XML-metadata. The display-settings are returned in the form of an object, + /// for which a handle is returned. + /// + /// \\param czi_document_info The CZI-document-info object. + /// \\param \[in,out\] display_settings_handle If successful, a handle to the display-settings object is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_display_settings(&self) -> Result<DisplaySettings> { + let mut display_settings = MaybeUninit::uninit(); + let ptr = display_settings.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { libCZI_CziDocumentInfoGetDisplaySettings(**self, ptr) })?; + Ok(unsafe { DisplaySettings::assume_init(display_settings) }) + } + + /// Get the dimension information from the document's XML-metadata. The information is returned as a JSON-formatted string. + /// + /// \\param czi_document_info Handle to the CZI-document-info object from which the dimension information will be retrieved. + /// \\param dimension_index Index of the dimension. + /// \\param \[out\] dimension_info_json If successful, the information is put here as JSON format. Note that the data must be freed using 'libCZI_Free' by the caller. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get_dimension_info(&self, dimension_index: u32) -> Result<String> { + let mut ptr = MaybeUninit::<*mut c_char>::uninit(); + LibCZIApiError::try_from(unsafe { + libCZI_CziDocumentInfoGetDimensionInfo( + **self, + dimension_index, + ptr.as_mut_ptr() as *mut *mut c_void, + ) + })?; + let ptr = unsafe { ptr.assume_init() }; + let info = unsafe { CStr::from_ptr(ptr) }.to_str()?.to_owned(); + unsafe { libCZI_Free(ptr as *mut c_void) }; + Ok(info) + } + + /// Release the specified CZI-document-info object. + /// + /// \\param czi_document_info The CZI-document-info object. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn release(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReleaseCziDocumentInfo(**self) })?; + Ok(()) + } +} + +impl Drop for CziDocumentInfo { + fn drop(&mut self) { + self.release().ok(); + } +} + +impl OutputStream { + /// Create an output stream object for a file identified by its filename, which is given as a wide string. Note that wchar_t on + /// Windows is 16-bit wide, and on Unix-like systems it is 32-bit wide. + /// + /// \\param filename Filename of the file which is to be opened (zero terminated wide string). Note that on Windows, this + /// is a string with 16-bit code units, and on Unix-like systems it is typically a string with 32-bit code units. + /// \\param overwrite Indicates whether the file should be overwritten. + /// \\param \[out\] output_stream_object The output stream object that will hold the created stream. + /// + /// \\return An error-code that indicates whether the operation is successful or not. Non-positive values indicates successful, positive values + /// indicates unsuccessful operation. + pub fn create_for_file_wide(file_name: Vec<u32>, overwrite: bool) -> Result<Self> { + let mut output_stream = MaybeUninit::uninit(); + let ptr = output_stream.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { + libCZI_CreateOutputStreamForFileWide(file_name.as_ptr(), overwrite, ptr) + })?; + Ok(unsafe { Self::assume_init(output_stream) }) + } + + /// Create an input stream object for a file identified by its filename, which is given as an UTF8 - encoded string. + /// + /// \\param filename Filename of the file which is to be opened (in UTF8 encoding). + /// \\param overwrite Indicates whether the file should be overwritten. + /// \\param \[out\] output_stream_object The output stream object that will hold the created stream. + /// + /// \\return An error-code that indicates whether the operation is successful or not. Non-positive values indicates successful, positive values + /// indicates unsuccessful operation. + pub fn create_for_file_utf8<S: AsRef<str>>(file_name: S, overwrite: bool) -> Result<Self> { + let mut output_stream = MaybeUninit::uninit(); + let ptr = output_stream.as_mut_ptr(); + let file_name = ManuallyDrop::new(CString::new(file_name.as_ref())?); + LibCZIApiError::try_from(unsafe { + libCZI_CreateOutputStreamForFileUTF8(file_name.as_ptr(), overwrite, ptr) + })?; + Ok(unsafe { Self::assume_init(output_stream) }) + } + + /// Release the specified output stream object. After this function is called, the handle is no + /// longer valid. Note that calling this function will only decrement the usage count of the + /// underlying object; whereas the object itself (and the resources it holds) will only be + /// released when the usage count reaches zero. + /// + /// \\param output_stream_object The output stream object. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn release(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReleaseOutputStream(**self) })?; + Ok(()) + } + + /// Create an output stream object which is using externally provided functions for operation + /// and writing the data. Please refer to the documentation of + /// 'ExternalOutputStreamStructInterop' for more information. + /// + /// \\param external_output_stream_struct Structure containing the information about the externally provided functions. + /// \\param \[out\] output_stream_object If successful, the handle to the newly created output stream object is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn create_from_external(external_input_stream: ExternalOutputStreamStruct) -> Result<Self> { + let mut stream = MaybeUninit::uninit(); + let ptr = stream.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { + libCZI_CreateOutputStreamFromExternal(external_input_stream.as_ptr(), ptr) + })?; + Ok(unsafe { Self::assume_init(stream) }) + } +} + +impl Drop for OutputStream { + fn drop(&mut self) { + self.release().ok(); + } +} + +impl CziWriter { + /// Create a writer object for authoring a document in CZI-format. The options string is a JSON-formatted string, here + /// is an example: + /// \\code + /// { + /// \"allow_duplicate_subblocks\" : true + /// } + /// \\endcode + /// + /// \\param \[out\] writer_object If the operation is successful, a handle to the newly created writer object is put here. + /// \\param options A JSON-formatted zero-terminated string (in UTF8-encoding) containing options for the writer creation. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn create<S: AsRef<str>>(options: S) -> Result<Self> { + let mut writer = MaybeUninit::uninit(); + let ptr = writer.as_mut_ptr(); + let options = ManuallyDrop::new(CString::new(options.as_ref())?); + LibCZIApiError::try_from(unsafe { libCZI_CreateWriter(ptr, options.as_ptr()) })?; + Ok(unsafe { Self::assume_init(writer) }) + } + + /// Initializes the writer object with the specified output stream object. The options string is a JSON-formatted string, here + /// is an example: + /// \\code + /// { + /// \"file_guid\" : \"123e4567-e89b-12d3-a456-426614174000\", + /// \"reserved_size_attachments_directory\" : 4096, + /// \"reserved_size_metadata_segment\" : 50000, + /// \"minimum_m_index\" : 0, + /// \"maximum_m_index\" : 100 + /// } + /// \\endcode + /// + /// \\param \[out\] writer_object If the operation is successful, a handle to the newly created writer object is put here. + /// \\param output_stream_object The output stream object to be used for writing the CZI data. + /// \\param parameters A JSON-formatted zero-terminated string (in UTF8-encoding) containing options for the writer initialization. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn init<S: AsRef<str>>(&self, output_stream: &OutputStream, parameters: S) -> Result<()> { + let parameters = ManuallyDrop::new(CString::new(parameters.as_ref())?); + LibCZIApiError::try_from(unsafe { + libCZI_WriterCreate(**self, **output_stream, parameters.as_ptr()) + })?; + Ok(()) + } + + /// Add the specified sub-block to the writer object. The sub-block information is provided in the 'add_sub_block_info_interop' structure. + /// + /// \\param writer_object The writer object. + /// \\param add_sub_block_info_interop Information describing the sub-block to be added. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn add_sub_block(&self, add_sub_block_info: AddSubBlockInfo) -> Result<()> { + LibCZIApiError::try_from(unsafe { + libCZI_WriterAddSubBlock(**self, add_sub_block_info.as_ptr()) + })?; + Ok(()) + } + + /// Add the specified attachment to the writer object. The attachment is provided in the 'add_attachment_info_interop' structure. + /// + /// \\param writer_object The writer object. + /// \\param add_attachment_info_interop Information describing the attachment to be added. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn add_attachement(&self, add_attachment_info: AddAttachmentInfo) -> Result<()> { + LibCZIApiError::try_from(unsafe { + libCZI_WriterAddAttachment(**self, add_attachment_info.as_ptr()) + })?; + Ok(()) + } + + /// Add the specified metadata to the writer object. The metadata is provided in the 'write_metadata_info_interop' structure. + /// + /// \\param writer_object Handle to the writer object to which the metadata will be added. + /// \\param write_metadata_info_interop Information describing the metadata to be added. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn write_metadata(&self, write_metadata_info: WriteMetadataInfo) -> Result<()> { + LibCZIApiError::try_from(unsafe { + libCZI_WriterWriteMetadata(**self, write_metadata_info.as_ptr()) + })?; + Ok(()) + } + + /// inalizes the CZI (i.e. writes out the final directory-segments) and closes the file. + /// Note that this method must be called explicitly in order to get a valid CZI - calling 'libCZI_ReleaseWriter' without + /// a prior call to this method will close the file immediately without finalization. + /// + /// \\param writer_object Handle to the writer object that is to be closed. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn close(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_WriterClose(**self) })?; + Ok(()) + } + + /// Release the specified writer object. + /// + /// \\param writer_object Handle to the writer object that is to be released. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn release(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReleaseWriter(**self) })?; + Ok(()) + } +} + +impl Drop for CziWriter { + fn drop(&mut self) { + self.close().ok(); + self.release().ok(); + } +} + +impl SingleChannelScalingTileAccessor { + /// Gets the size information of the specified tile accessor based on the region of interest and zoom factor. + /// + /// \\param accessor_object Handle to the tile accessor object for which the size is to be calculated. This object is responsible for managing the access to the tiles within the specified plane. + /// \\param roi The region of interest that defines the region of interest within the plane for which the size is to be calculated. + /// \\param zoom A floating-point value representing the zoom factor. + /// \\param size \[out\] The size of the tile accessor. It contains width and height information. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn calc_size(&self, roi: IntRect, zoom: f32) -> Result<IntSize> { + let mut size = MaybeUninit::uninit(); + let ptr = size.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { + libCZI_SingleChannelTileAccessorCalcSize(**self, roi.as_ptr(), zoom, ptr) + })?; + Ok(unsafe { IntSize::assume_init(size) }) + } + + /// Gets the tile bitmap of the specified plane and the specified roi with the specified zoom factor. + /// + /// \\param accessor_object Handle to the tile accessor object. This object is responsible for managing the access to the tiles within the specified plane. + /// \\param coordinate Pointer to a `CoordinateInterop` structure that specifies the coordinates within the plane from which the tile bitmap is to be retrieved. + /// \\param roi The region of interest that defines within the plane for which the tile bitmap is requested. + /// \\param zoom A floating-point value representing the zoom factor. + /// \\param options A pointer to an AccessorOptionsInterop structure that may contain additional options for accessing the tile bitmap. + /// \\param bitmap_object \[out\] If the operation is successful, the created bitmap object will be put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn get( + &self, + coordinate: Coordinate, + roi: IntRect, + zoom: f32, + options: AccessorOptions, + ) -> Result<Bitmap> { + let mut bitmap = MaybeUninit::uninit(); + let ptr = bitmap.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { + libCZI_SingleChannelTileAccessorGet( + **self, + coordinate.as_ptr(), + roi.as_ptr(), + zoom, + options.as_ptr(), + ptr, + ) + })?; + Ok(unsafe { Bitmap::assume_init(bitmap) }) + } + + /// Release the specified accessor object. + /// + /// \\param accessor_object The accessor object. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn release(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReleaseCreateSingleChannelTileAccessor(**self) })?; + Ok(()) + } +} + +impl Drop for SingleChannelScalingTileAccessor { + fn drop(&mut self) { + self.release().ok(); + } +} + +impl DisplaySettings { + /// Given a display-settings object and the channel-number, this function fills out the + /// composition-channel-information which is needed for the multi-channel-composition. + /// Note that in the returned 'CompositionChannelInfoInterop' structure, the 'lut' field is a pointer to the LUT-data, + /// which must be freed with 'libCZI_Free' by the caller. + /// + /// \\param display_settings_handle The display settings handle. + /// \\param channel_index The channel-index (referring to the display settings object) we are concerned with. + /// \\param sixteen_or_eight_bits_lut True for generating a 16-bit LUT; if false, then an 8-bit LUT is generated. + /// \\param \[out\] composition_channel_info_interop The composition channel information is put here. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn compositor_fill_out_composition_channel_info_interop( + &self, + channel_index: i32, + sixteen_or_eight_bits_lut: bool, + ) -> Result<CompositionChannelInfo> { + let mut composition_channel_info = MaybeUninit::uninit(); + let ptr = composition_channel_info.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { + libCZI_CompositorFillOutCompositionChannelInfoInterop( + **self, + channel_index, + sixteen_or_eight_bits_lut, + ptr, + ) + })?; + Ok(unsafe { CompositionChannelInfo::assume_init(composition_channel_info) }) + } + + pub fn get_channel_display_settings(&self, channel_id: i32) -> Result<ChannelDisplaySettings> { + let mut channel_display_setting = MaybeUninit::uninit(); + let ptr = channel_display_setting.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { + libCZI_DisplaySettingsGetChannelDisplaySettings(**self, channel_id, ptr) + })?; + Ok(unsafe { ChannelDisplaySettings::assume_init(channel_display_setting) }) + } + + /// Release the specified display settings object. + /// + /// \\param display_settings_handle The display settings object. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn release(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReleaseDisplaySettings(**self) })?; + Ok(()) + } +} + +impl Drop for DisplaySettings { + fn drop(&mut self) { + self.release().ok(); + } +} + +/// Perform a multi-channel-composition operation. The source bitmaps are provided in the 'source_bitmaps' array, and the +/// array of 'CompositionChannelInfoInterop' structures provide the information needed for the composition. The resulting bitmap +/// is then put into the 'bitmap_object' handle. +/// +/// \\param channelCount The number of channels - this defines the size of the 'source_bitmaps' and 'channel_info' arrays. +/// \\param source_bitmaps The array of source bitmaps. +/// \\param channel_info The array of channel information. +/// \\param \[out\] bitmap_object The resulting bitmap is put here. +/// +/// \\return An error-code indicating success or failure of the operation. +pub fn compositor_do_multi_channel_composition( + channel_count: i32, + source_bitmaps: Vec<Bitmap>, + channel_info: CompositionChannelInfo, +) -> Result<Bitmap> { + let mut bitmap = MaybeUninit::uninit(); + let ptr = bitmap.as_mut_ptr(); + LibCZIApiError::try_from(unsafe { + libCZI_CompositorDoMultiChannelComposition( + channel_count, + source_bitmaps.as_ptr() as *const BitmapObjectHandle, + channel_info.as_ptr(), + ptr, + ) + })?; + Ok(unsafe { Bitmap::assume_init(bitmap) }) +} + +impl ChannelDisplaySettings { + /// Release the specified channel-display settings object. + /// + /// \\param channel_display_settings_handle The channel-display settings object. + /// + /// \\returns An error-code indicating success or failure of the operation. + pub fn release(&self) -> Result<()> { + LibCZIApiError::try_from(unsafe { libCZI_ReleaseDisplaySettings(**self) })?; + Ok(()) + } +} + +impl Drop for ChannelDisplaySettings { + fn drop(&mut self) { + self.release().ok(); + } +} diff --git a/src/handle.rs b/src/handle.rs new file mode 100644 index 0000000..b18adf7 --- /dev/null +++ b/src/handle.rs @@ -0,0 +1,110 @@ +use crate::misc::Ptr; +use crate::sys::*; +use std::mem::MaybeUninit; +use std::ops::Deref; + + +/// CZI-reader object. +#[derive(Clone, Debug)] +pub struct CziReader(pub (crate) CziReaderObjectHandle); + +/// sub-block object. +#[derive(Clone, Debug)] +pub struct SubBlock(pub (crate) SubBlockObjectHandle); + +/// input stream object. +#[derive(Clone, Debug)] +pub struct InputStream(pub (crate) InputStreamObjectHandle); + +/// output stream object. +#[derive(Clone, Debug)] +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); + +/// bitmap object. +#[derive(Clone, Debug)] +pub struct Bitmap(pub (crate) BitmapObjectHandle); + +/// metadata segment object. +#[derive(Clone, Debug)] +pub struct MetadataSegment(pub (crate) MetadataSegmentObjectHandle); + +/// attachment object. +#[derive(Clone, Debug)] +pub struct Attachment(pub (crate) AttachmentObjectHandle); + +/// writer object. +#[derive(Clone, Debug)] +pub struct CziWriter(pub (crate) CziWriterObjectHandle); + +/// single-channel-scaling-tile-accessor. +#[derive(Clone, Debug)] +pub struct SingleChannelScalingTileAccessor(pub (crate) SingleChannelScalingTileAccessorObjectHandle); + +/// document info object. +#[derive(Clone, Debug)] +pub struct CziDocumentInfo(pub (crate) CziDocumentInfoHandle); + +/// display settings object. +#[derive(Clone, Debug)] +pub struct DisplaySettings(pub (crate) DisplaySettingsHandle); + +/// channel display settings object. +#[derive(Clone, Debug)] +pub struct ChannelDisplaySettings(pub (crate) ChannelDisplaySettingsHandle); + +macro_rules! impl_struct { + ($($n:ident: $t:ty: $s:ty $(,)?)*) => { + $( + impl $t { + #[allow(dead_code)] + pub (crate) fn handle(&self) -> ObjectHandle { self.0 } + } + + impl Ptr for $t { + type Pointer = $s; + + unsafe fn assume_init(ptr: MaybeUninit<Self::Pointer>) -> Self { + Self(unsafe { ptr.assume_init() }) + } + + fn as_mut_ptr(&self) -> *mut Self::Pointer { + &self.0 as *const _ as *mut _ + } + + fn as_ptr(&self) -> *const Self::Pointer { + &self.0 as *const _ as *const _ + } + } + + impl Deref for $t { + type Target = ObjectHandle; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + )* + }; +} + +impl_struct! { + CziReader: CziReader: CziReaderObjectHandle, + SubBlock: SubBlock: SubBlockObjectHandle, + InputStream: InputStream: InputStreamObjectHandle, + OutputStream: OutputStream: OutputStreamObjectHandle, + MemoryAllocation: MemoryAllocation: MemoryAllocationObjectHandle, + Bitmap: Bitmap: BitmapObjectHandle, + MetadataSegment: MetadataSegment: MetadataSegmentObjectHandle, + Attachment: Attachment: AttachmentObjectHandle, + CziWriter: CziWriter: CziWriterObjectHandle, + SingleChannelScalingTileAccessor: SingleChannelScalingTileAccessor: SingleChannelScalingTileAccessorObjectHandle, + CziDocumentInfo: CziDocumentInfo: CziDocumentInfoHandle, + DisplaySettings: DisplaySettings: DisplaySettingsHandle, + ChannelDisplaySettings: ChannelDisplaySettings: ChannelDisplaySettingsHandle, +} diff --git a/src/interop.rs b/src/interop.rs new file mode 100644 index 0000000..c7f09e4 --- /dev/null +++ b/src/interop.rs @@ -0,0 +1,1125 @@ +use crate::handle::{InputStream, MemoryAllocation}; +use crate::misc::{PixelType, Ptr}; +use crate::sys::*; +use anyhow::{Error, Result}; +use std::ffi::{CStr, CString, c_char, c_void}; +use std::mem::{ManuallyDrop, MaybeUninit}; + +/// 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). +#[derive(Clone, Debug)] +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); + +#[derive(Clone, Debug)] +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); + +/// 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); + +/// 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); + +/// This structure gather the information needed to create a reader object. +#[derive(Clone, Debug)] +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); + +/// This structure describes a size, given by its width and height. +#[derive(Clone, Debug)] +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 +/// is corresponding to dimension 1 (=Z), bit 1 to dimension 2 (=C), and so on. +/// In the fixed-sized arrays `start` and `size`, the start and size values for the dimensions are stored. The elements at +/// position 0 corresponds to the first valid dimension, the element at position 1 to the second valid dimension, and so on. +/// 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); + +/// 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 +/// is corresponding to dimension 1 (=Z), bit 1 to dimension 2 (=C), and so on. +/// In the fixed-sized array `value`, the coordinate for the dimensions is stored. The element at +/// position 0 corresponds to the first valid dimension, the element at position 1 to the second valid dimension, and so on. +/// 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); + +/// This structure contains the bounding boxes for a scene. +#[derive(Clone, Debug)] +pub struct BoundingBoxes(pub (crate) BoundingBoxesInterop); + +/// This structure contains basic statistics about an CZI-document. +#[derive(Clone, Debug)] +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); + +#[derive(Clone, Debug)] +pub struct MetadataAsXml(pub (crate) MetadataAsXmlInterop); + +/// Information about the bitmap represented by a bitmap-object. +#[derive(Clone, Debug)] +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); + +/// This structure contains the information about a sub-block. +#[derive(Clone, Debug)] +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 +/// fixed-size array, the 'overflow' field is set to true. In this case, the name is truncated and the 'overflow' field is set to true. +/// 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); + +/// This structure contains the information about file-header. +#[derive(Clone, Debug)] +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); + +/// 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); + +/// This structure is used to pass the metadata information to libCZIAPI. +#[derive(Clone, Debug)] +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); + +/// This structure gathers all information about a channel for the purpose of multi-channel-composition. +#[derive(Clone, Debug)] +pub struct CompositionChannelInfo(pub (crate) CompositionChannelInfoInterop); + +/// This structure gathers the information about the scaling. +#[derive(Clone, Debug)] +pub struct ScalingInfo(pub (crate) ScalingInfoInterop); + +macro_rules! impl_ptr { + ($($n:ident: $t:ty: $s:ty $(,)?)*) => { + $( + impl Ptr for $t { + type Pointer = $s; + + unsafe fn assume_init(ptr: MaybeUninit<Self::Pointer>) -> Self { + Self(unsafe { ptr.assume_init() }) + } + + fn as_mut_ptr(&self) -> *mut Self::Pointer { + // Box::into_raw(Box::new(self.0)) + &self.0 as *const _ as *mut _ + } + + fn as_ptr(&self) -> *const Self::Pointer { + &self.0 as *const _ as *const _ + // Box::into_raw(Box::new(self.0)) as *const Self::Pointer + } + } + )* + }; +} + +impl_ptr! { + LibCZIVersionInfo: LibCZIVersionInfo: LibCZIVersionInfoInterop, + LibCZIBuildInformation: LibCZIBuildInformation: LibCZIBuildInformationInterop, + InputStreamClassInfo: InputStreamClassInfo: InputStreamClassInfoInterop, + ExternalStreamErrorInfo: ExternalStreamErrorInfo: ExternalStreamErrorInfoInterop, + ExternalInputStreamStruct: ExternalInputStreamStruct: ExternalInputStreamStructInterop, + ExternalOutputStreamStruct: ExternalOutputStreamStruct: ExternalOutputStreamStructInterop, + ReaderOpenInfo: ReaderOpenInfo: ReaderOpenInfoInterop, + IntRect: IntRect: IntRectInterop, + IntSize: IntSize: IntSizeInterop, + DimBounds: DimBounds: DimBoundsInterop, + Coordinate: Coordinate: CoordinateInterop, + BoundingBoxes: BoundingBoxes: BoundingBoxesInterop, + SubBlockStatistics: SubBlockStatistics: SubBlockStatisticsInterop, + SubBlockStatisticsEx: SubBlockStatisticsEx: SubBlockStatisticsInteropEx, + MetadataAsXml: MetadataAsXml: MetadataAsXmlInterop, + BitmapInfo: BitmapInfo: BitmapInfoInterop, + BitmapLockInfo: BitmapLockInfo: BitmapLockInfoInterop, + SubBlockInfo: SubBlockInfo: SubBlockInfoInterop, + AttachmentInfo: AttachmentInfo: AttachmentInfoInterop, + FileHeaderInfo: FileHeaderInfo: FileHeaderInfoInterop, + AddSubBlockInfo: AddSubBlockInfo: AddSubBlockInfoInterop, + AddAttachmentInfo: AddAttachmentInfo: AddAttachmentInfoInterop, + WriteMetadataInfo: WriteMetadataInfo: WriteMetadataInfoInterop, + AccessorOptions: AccessorOptions: AccessorOptionsInterop, + CompositionChannelInfo: CompositionChannelInfo: CompositionChannelInfoInterop, + ScalingInfo: ScalingInfo: ScalingInfoInterop, +} + +impl LibCZIVersionInfo { + pub fn get_major(&self) -> i32 { + self.0.major + } + pub fn get_minor(&self) -> i32 { + self.0.minor + } + pub fn get_patch(&self) -> i32 { + self.0.patch + } + pub fn get_tweak(&self) -> i32 { + self.0.tweak + } +} + +impl LibCZIBuildInformation { + pub fn get_compiler_information(&self) -> Result<&str> { + Ok(unsafe { CStr::from_ptr(self.0.compilerIdentification) }.to_str()?) + } + pub fn get_repository_url(&self) -> Result<&str> { + Ok(unsafe { CStr::from_ptr(self.0.repositoryUrl) }.to_str()?) + } + pub fn get_repository_branch(&self) -> Result<&str> { + Ok(unsafe { CStr::from_ptr(self.0.repositoryBranch) }.to_str()?) + } + pub fn get_repository_tag(&self) -> Result<&str> { + Ok(unsafe { CStr::from_ptr(self.0.repositoryTag) }.to_str()?) + } +} + +impl Drop for LibCZIBuildInformation { + fn drop(&mut self) { + unsafe { + libCZI_Free(self.0.compilerIdentification as *mut c_void); + libCZI_Free(self.0.repositoryUrl as *mut c_void); + libCZI_Free(self.0.repositoryBranch as *mut c_void); + libCZI_Free(self.0.repositoryTag as *mut c_void); + } + } +} + +impl InputStreamClassInfo { + pub fn get_name(&self) -> Result<&str> { + Ok(unsafe { CStr::from_ptr(self.0.name) }.to_str()?) + } + pub fn get_description(&self) -> Result<&str> { + Ok(unsafe { CStr::from_ptr(self.0.description) }.to_str()?) + } +} + +impl Drop for InputStreamClassInfo { + fn drop(&mut self) { + unsafe { + libCZI_Free(self.0.name as *mut c_void); + libCZI_Free(self.0.description as *mut c_void); + } + } +} + +impl ExternalStreamErrorInfo { + pub fn get_error_code(&self) -> i32 { + self.0.error_code + } + + pub fn get_error_message(&self) -> MemoryAllocation { + MemoryAllocation(self.0.error_message) + } +} + +// TODO +impl ExternalInputStreamStruct { + /// A user parameter which is passed to the callback function. + pub fn get_opaque_handle1(&self) -> u64 { + self.0.opaque_handle1 + } + /// A user parameter which is passed to the callback function. + pub fn get_opaque_handle2(&self) -> u64 { + self.0.opaque_handle2 + } + pub fn set_opaque_handle1(&mut self, handle: u64) { + self.0.opaque_handle1 = handle; + } + pub fn set_opaque_handle2(&mut self, handle: u64) { + self.0.opaque_handle2 = handle; + } +} + +// TODO +impl ExternalOutputStreamStruct { + /// A user parameter which is passed to the callback function. + pub fn get_opaque_handle1(&self) -> u64 { + self.0.opaque_handle1 + } + /// A user parameter which is passed to the callback function. + pub fn get_opaque_handle2(&self) -> u64 { + self.0.opaque_handle2 + } + pub fn set_opaque_handle1(&mut self, handle: u64) { + self.0.opaque_handle1 = handle; + } + pub fn set_opaque_handle2(&mut self, handle: u64) { + self.0.opaque_handle2 = handle; + } +} + +/// This structure gather the information needed to create a reader object. +impl ReaderOpenInfo { + pub fn new(stream: &InputStream) -> Self { + Self(ReaderOpenInfoInterop { + streamObject: stream.handle(), + }) + } + pub fn get_stream(&self) -> InputStream { + InputStream(self.0.streamObject) + } +} + +/// This structure describes a rectangle, given by its top-left corner and its width and height. +impl IntRect { + pub fn new(x: i32, y: i32, w: i32, h: i32) -> Self { + Self(IntRectInterop { x, y, w, h }) + } + pub fn get_x(&self) -> i32 { + self.0.x + } + pub fn get_y(&self) -> i32 { + self.0.y + } + pub fn get_w(&self) -> i32 { + self.0.w + } + pub fn get_h(&self) -> i32 { + self.0.h + } + pub fn set_x(&mut self, x: i32) { + self.0.x = x; + } + pub fn set_y(&mut self, y: i32) { + self.0.y = y; + } + pub fn set_w(&mut self, w: i32) { + self.0.w = w; + } + pub fn set_h(&mut self, h: i32) { + self.0.h = h; + } +} + +impl IntSize { + pub fn new(w: i32, h: i32) -> Self { + Self(IntSizeInterop { w, h }) + } + pub fn get_w(&self) -> i32 { + self.0.w + } + pub fn get_h(&self) -> i32 { + self.0.h + } + pub fn set_w(&mut self, w: i32) { + self.0.w = w; + } + pub fn set_h(&mut self, h: i32) { + self.0.h = h; + } +} + +impl DimBounds { + pub fn new(dimensions_valid: u32, start: [i32; 9], size: [i32; 9]) -> Self { + Self(DimBoundsInterop { + dimensions_valid, + start, + size, + }) + } + pub fn get_dimensions_valid(&self) -> u32 { + self.0.dimensions_valid + } + pub fn get_start(&self) -> [i32; 9] { + self.0.start + } + pub fn get_size(&self) -> [i32; 9] { + self.0.size + } + pub fn set_dimensions_valid(&mut self, dimensions_valid: u32) { + self.0.dimensions_valid = dimensions_valid; + } + pub fn set_start(&mut self, start: [i32; 9]) { + self.0.start = start; + } + pub fn set_size(&mut self, size: [i32; 9]) { + self.0.size = size; + } +} + +impl Coordinate { + pub fn new(dimensions_valid: u32, value: [i32; 9]) -> Self { + Self(CoordinateInterop { + dimensions_valid, + value, + }) + } + pub fn get_dimensions_valid(&self) -> u32 { + self.0.dimensions_valid + } + pub fn get_value(&self) -> [i32; 9] { + self.0.value + } + pub fn set_dimensions_valid(&mut self, dimensions_valid: u32) { + self.0.dimensions_valid = dimensions_valid; + } + pub fn set_value(&mut self, value: [i32; 9]) { + self.0.value = value; + } +} + +impl BoundingBoxes { + pub fn new( + scene_index: i32, + bounding_box: IntRectInterop, + bounding_box_layer0_only: IntRectInterop, + ) -> Self { + Self(BoundingBoxesInterop { + sceneIndex: scene_index, + bounding_box, + bounding_box_layer0_only, + }) + } + pub fn get_scene_index(&self) -> i32 { + self.0.sceneIndex + } + pub fn get_bounding_box(&self) -> IntRectInterop { + self.0.bounding_box + } + pub fn get_bounding_box_layer0_only(&self) -> IntRectInterop { + self.0.bounding_box_layer0_only + } + pub fn set_scene_index(&mut self, scene_index: i32) { + self.0.sceneIndex = scene_index; + } + pub fn set_bounding_box(&mut self, bounding_box: IntRectInterop) { + self.0.bounding_box = bounding_box; + } + pub fn set_bounding_box_layer0_only(&mut self, bounding_box_layer0_only: IntRectInterop) { + self.0.bounding_box_layer0_only = bounding_box_layer0_only + } +} + +impl SubBlockStatistics { + 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, + ) -> Self { + Self(SubBlockStatisticsInterop { + 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, + }) + } + 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 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 + } +} + +impl MetadataAsXml { + fn get_data(&self) -> Result<String> { + let xml_data = unsafe { + Vec::from_raw_parts( + self.0.data as *mut u8, + self.0.size as usize, + self.0.size as usize, + ) + }; + Ok(String::from_utf8(xml_data)?) + } +} + +impl Drop for MetadataAsXml { + fn drop(&mut self) { + unsafe { + libCZI_Free(Box::into_raw(Box::new(self.0.data)) as *mut c_void); + } + } +} + +impl TryFrom<&MetadataAsXml> for String { + type Error = Error; + + fn try_from(value: &MetadataAsXml) -> std::result::Result<Self, Self::Error> { + value.get_data() + } +} + +impl BitmapInfo { + pub fn new(width: u32, height: u32, pixel_type: PixelType) -> Self { + Self(BitmapInfoInterop { + width, + height, + pixelType: pixel_type.into(), + }) + } + pub fn get_width(&self) -> u32 { + self.0.width + } + pub fn get_height(&self) -> u32 { + self.0.height + } + pub fn get_pixel_type(&self) -> Result<PixelType> { + PixelType::try_from(self.0.pixelType) + } + pub fn set_width(&mut self, width: u32) { + self.0.width = width; + } + pub fn set_height(&mut self, height: u32) { + self.0.height = height; + } + pub fn set_pixel_type(&mut self, pixel_type: PixelType) { + self.0.pixelType = pixel_type.into(); + } +} + +impl BitmapLockInfo { + pub fn get_data_roi(&self) -> Vec<u8> { + unsafe { + Vec::from_raw_parts( + self.0.ptrDataRoi as *mut u8, + self.0.size as usize, + self.0.size as usize, + ) + } + } +} + +impl SubBlockInfo { + pub fn new( + compression_mode_raw: i32, + pixel_type: PixelType, + coordinate: Coordinate, + logical_rect: IntRect, + physical_size: IntSize, + m_index: i32, + ) -> Self { + Self(SubBlockInfoInterop { + compression_mode_raw, + pixel_type: pixel_type.into(), + coordinate: coordinate.0, + logical_rect: logical_rect.0, + physical_size: physical_size.0, + m_index, + }) + } + pub fn get_compression_mode_raw(&self) -> i32 { + self.0.compression_mode_raw + } + pub fn get_pixel_type(&self) -> Result<PixelType> { + PixelType::try_from(self.0.pixel_type) + } + pub fn get_coordinate(&self) -> Coordinate { + Coordinate(self.0.coordinate) + } + pub fn get_logical_rect(&self) -> IntRect { + IntRect(self.0.logical_rect) + } + pub fn get_physical_size(&self) -> IntSize { + IntSize(self.0.physical_size) + } + pub fn get_m_index(&self) -> i32 { + self.0.m_index + } + pub fn set_compression_mode_raw(&mut self, compression_mode_raw: i32) { + self.0.compression_mode_raw = compression_mode_raw + } + pub fn set_pixel_type(&mut self, pixel_type: PixelType) { + self.0.pixel_type = pixel_type.into(); + } + pub fn set_coordinate(&mut self, coordinate: Coordinate) { + self.0.coordinate = coordinate.0 + } + pub fn set_logical_rect(&mut self, logical_rect: IntRect) { + self.0.logical_rect = logical_rect.0 + } + pub fn set_physical_size(&mut self, physical_size: IntSize) { + self.0.physical_size = physical_size.0 + } + pub fn set_m_index(&mut self, m_index: i32) { + self.0.m_index = m_index + } +} + +impl AttachmentInfo { + pub fn get_guid(&self) -> [u8; 16] { + self.0.guid + } + pub fn get_content_file_type(&self) -> [u8; 9] { + self.0.content_file_type + } + pub fn get_name(&self) -> Result<String> { + Ok( + CStr::from_bytes_until_nul(&self.0.name.iter().map(|&i| i as u8).collect::<Vec<_>>())? + .to_str()? + .to_string(), + ) + } + pub fn get_name_overflow(&self) -> bool { + self.0.name_overflow + } + pub fn get_name_in_case_of_overflow(&self) -> Result<String> { + Ok( + unsafe { CString::from_raw(self.0.name_in_case_of_overflow as *mut c_char) } + .to_str()? + .to_string(), + ) + } +} + +impl Drop for AttachmentInfo { + fn drop(&mut self) { + if self.0.name_overflow { + unsafe { libCZI_Free(self.0.name_in_case_of_overflow) } + } + } +} + +impl FileHeaderInfo { + pub fn new(guid: [u8; 16], major_version: i32, minor_version: i32) -> Self { + Self(FileHeaderInfoInterop { + guid, + majorVersion: major_version, + minorVersion: minor_version, + }) + } + pub fn get_guid(&self) -> [u8; 16] { + self.0.guid + } + pub fn get_major_version(&self) -> i32 { + self.0.majorVersion + } + pub fn get_minor_version(&self) -> i32 { + self.0.minorVersion + } + pub fn set_guid(&mut self, guid: [u8; 16]) { + self.0.guid = guid + } + pub fn set_major_version(&mut self, major_version: i32) { + self.0.majorVersion = major_version + } + pub fn set_minor_version(&mut self, minor_version: i32) { + self.0.minorVersion = minor_version + } +} + +impl AddSubBlockInfo { + #[allow(clippy::too_many_arguments)] + pub fn new( + coordinate: Coordinate, + m_index_valid: u8, + m_index: i32, + x: i32, + y: i32, + logical_width: i32, + logical_height: i32, + physical_width: i32, + 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()); + let metadata = ManuallyDrop::new(metadata.to_vec()); + let attachment = ManuallyDrop::new(attachment.to_vec()); + + Self(AddSubBlockInfoInterop { + coordinate: coordinate.0, + m_index_valid, + m_index, + x, + y, + logical_width, + logical_height, + physical_width, + physical_height, + pixel_type: pixel_type.into(), + compression_mode_raw, + size_data, + data: data.as_ptr() as *const c_void, + stride, + size_metadata, + metadata: metadata.as_ptr() as *const c_void, + size_attachment, + attachment: attachment.as_ptr() as *const c_void, + }) + } + pub fn get_coordinate(&self) -> Coordinate { + Coordinate(self.0.coordinate) + } + pub fn get_m_index_valid(&self) -> u8 { + self.0.m_index_valid + } + pub fn get_m_index(&self) -> i32 { + self.0.m_index + } + pub fn get_x(&self) -> i32 { + self.0.x + } + pub fn get_y(&self) -> i32 { + self.0.y + } + pub fn get_logical_width(&self) -> i32 { + self.0.logical_width + } + pub fn get_logical_height(&self) -> i32 { + self.0.logical_height + } + pub fn get_physical_width(&self) -> i32 { + self.0.physical_width + } + pub fn get_physical_height(&self) -> i32 { + self.0.physical_height + } + pub fn get_pixel_type(&self) -> Result<PixelType> { + PixelType::try_from(self.0.pixel_type) + } + pub fn get_compression_mode_raw(&self) -> i32 { + self.0.compression_mode_raw + } + pub fn get_size_data(&self) -> u32 { + self.0.size_data + } + pub fn get_data(&self) -> Vec<u8> { + unsafe { + Vec::from_raw_parts( + self.0.data as *mut u8, + self.0.size_data as usize, + self.0.size_data as usize, + ) + } + } + pub fn get_size_metadata(&self) -> u32 { + self.0.size_metadata + } + pub fn get_metadata(&self) -> Vec<u8> { + unsafe { + Vec::from_raw_parts( + self.0.metadata as *mut u8, + self.0.size_metadata as usize, + self.0.size_metadata as usize, + ) + } + } + pub fn get_size_attachment(&self) -> u32 { + self.0.size_attachment + } + pub fn get_attachment(&self) -> Vec<u8> { + unsafe { + Vec::from_raw_parts( + self.0.attachment as *mut u8, + self.0.attachment as usize, + self.0.attachment as usize, + ) + } + } + pub fn set_coordinate(&mut self, coordinate: Coordinate) { + self.0.coordinate = coordinate.0 + } + pub fn set_m_index_valid(&mut self, m_index_valid: u8) { + self.0.m_index_valid = m_index_valid + } + pub fn set_m_index(&mut self, m_index: i32) { + self.0.m_index = m_index + } + pub fn set_x(&mut self, x: i32) { + self.0.x = x + } + pub fn set_y(&mut self, y: i32) { + self.0.y = y + } + pub fn set_logical_width(&mut self, logical_width: i32) { + self.0.logical_width = logical_width + } + pub fn set_logical_height(&mut self, logical_height: i32) { + self.0.logical_height = logical_height + } + pub fn set_physical_width(&mut self, physical_width: i32) { + self.0.physical_width = physical_width + } + pub fn set_physical_height(&mut self, physical_height: i32) { + self.0.physical_height = physical_height + } + pub fn set_pixel_type(&mut self, pixel_type: PixelType) { + self.0.pixel_type = pixel_type.into() + } + 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; + self.0.size_attachment = attachment.len() as u32; + } +} + +impl AddAttachmentInfo { + pub fn new( + 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()); + Self(AddAttachmentInfoInterop { + guid, + contentFileType: content_file_type, + name, + size_attachment_data, + attachment_data: attachment_data.as_ptr() as *const c_void, + }) + } + pub fn get_guid(&self) -> [u8; 16] { + self.0.guid + } + pub fn get_content_file_type(&self) -> [u8; 8] { + self.0.contentFileType + } + pub fn get_name(&self) -> [u8; 80] { + self.0.name + } + pub fn get_size_attachment_data(&self) -> u32 { + self.0.size_attachment_data + } + pub fn get_attachment_data(&self) -> Vec<u8> { + unsafe { + Vec::from_raw_parts( + self.0.attachment_data as *mut u8, + self.0.attachment_data as usize, + self.0.attachment_data as usize, + ) + } + } + pub fn set_guid(&mut self, guid: [u8; 16]) { + self.0.guid = guid + } + pub fn set_content_file_type(&mut self, content_file_type: [u8; 8]) { + self.0.contentFileType = content_file_type + } + 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; + self.0.size_attachment_data = attachment_data.len() as u32; + } +} + +impl WriteMetadataInfo { + pub fn new(size_metadata: u32, metadata: &[u8]) -> Self { + let metadata = ManuallyDrop::new(metadata.to_vec()); + Self(WriteMetadataInfoInterop { + size_metadata, + metadata: metadata.as_ptr() as *const c_void, + }) + } + pub fn get_size_metadata(&self) -> u32 { + self.0.size_metadata + } + pub fn get_metadata(&self) -> Vec<u8> { + unsafe { + Vec::from_raw_parts( + self.0.metadata as *mut u8, + self.0.metadata as usize, + self.0.metadata as usize, + ) + } + } + 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; + } +} + +impl AccessorOptions { + pub fn new<S: AsRef<str>>( + back_ground_color_r: f32, + back_ground_color_g: f32, + back_ground_color_b: f32, + sort_by_m: bool, + use_visibility_check_optimization: bool, + additional_parameters: S, + ) -> Result<Self> { + let additional_parameters = + ManuallyDrop::new(CString::new(additional_parameters.as_ref())?); + Ok(Self(AccessorOptionsInterop { + back_ground_color_r, + back_ground_color_g, + back_ground_color_b, + sort_by_m, + use_visibility_check_optimization, + additional_parameters: additional_parameters.as_ptr(), + })) + } + pub fn get_background_color_r(&self) -> f32 { + self.0.back_ground_color_r + } + pub fn get_background_color_g(&self) -> f32 { + self.0.back_ground_color_g + } + pub fn get_background_color_b(&self) -> f32 { + self.0.back_ground_color_b + } + pub fn get_sort_by_m(&self) -> bool { + self.0.sort_by_m + } + pub fn get_use_visibility_check_optimization(&self) -> bool { + self.0.use_visibility_check_optimization + } + pub fn get_additional_parameters(&self) -> Result<String> { + Ok(unsafe { CStr::from_ptr(self.0.additional_parameters) } + .to_str()? + .to_string()) + } + pub fn set_background_color_r(&mut self, back_ground_color_r: f32) { + self.0.back_ground_color_r = back_ground_color_r + } + pub fn set_background_color_g(&mut self, back_ground_color_g: f32) { + self.0.back_ground_color_g = back_ground_color_g + } + pub fn set_background_color_b(&mut self, back_ground_color_b: f32) { + self.0.back_ground_color_b = back_ground_color_b + } + pub fn set_sort_by_m(&mut self, sort_by_m: bool) { + self.0.sort_by_m = sort_by_m + } + pub fn set_use_visibility_check_optimization( + &mut self, + use_visibility_check_optimization: bool, + ) { + self.0.use_visibility_check_optimization = use_visibility_check_optimization + } + pub fn set_additional_parameters<S: AsRef<str>>( + &mut self, + additional_parameters: S, + ) -> Result<()> { + let additional_parameters = + ManuallyDrop::new(CString::new(additional_parameters.as_ref())?); + self.0.additional_parameters = additional_parameters.as_ptr(); + Ok(()) + } +} + +impl CompositionChannelInfo { + #[allow(clippy::too_many_arguments)] + pub fn new( + weight: f32, + enable_tinting: u8, + tinting_color_r: u8, + tinting_color_g: u8, + tinting_color_b: u8, + black_point: f32, + white_point: f32, + look_up_table_element_count: i32, + look_up_table: &[u8], + ) -> Self { + let mut look_up_table = ManuallyDrop::new(look_up_table.to_vec()); + Self(CompositionChannelInfoInterop { + weight, + enable_tinting, + tinting_color_r, + tinting_color_g, + tinting_color_b, + black_point, + white_point, + look_up_table_element_count, + ptr_look_up_table: look_up_table.as_mut_ptr(), + }) + } + pub fn get_weight(&self) -> f32 { + self.0.weight + } + pub fn get_enable_tinting(&self) -> u8 { + self.0.enable_tinting + } + pub fn get_tinting_color_r(&self) -> u8 { + self.0.tinting_color_r + } + pub fn get_tinting_color_g(&self) -> u8 { + self.0.tinting_color_g + } + pub fn get_tinting_color_b(&self) -> u8 { + self.0.tinting_color_b + } + pub fn get_black_point(&self) -> f32 { + self.0.black_point + } + pub fn get_white_point(&self) -> f32 { + self.0.white_point + } + pub fn get_look_up_table_element_count(&self) -> i32 { + self.0.look_up_table_element_count + } + pub fn get_look_up_table(&self) -> Vec<u8> { + unsafe { + Vec::from_raw_parts( + self.0.ptr_look_up_table, + self.0.look_up_table_element_count as usize, + self.0.look_up_table_element_count as usize, + ) + } + } + pub fn set_weight(&mut self, weight: f32) { + self.0.weight = weight + } + pub fn set_enable_tinting(&mut self, enable_tinting: u8) { + self.0.enable_tinting = enable_tinting + } + pub fn set_tinting_color_r(&mut self, tinting_color_r: u8) { + self.0.tinting_color_r = tinting_color_r + } + pub fn set_tinting_color_g(&mut self, tinting_color_g: u8) { + self.0.tinting_color_g = tinting_color_g + } + pub fn set_tinting_color_b(&mut self, tinting_color_b: u8) { + self.0.tinting_color_b = tinting_color_b + } + pub fn set_black_point(&mut self, black_point: f32) { + self.0.black_point = black_point + } + pub fn set_white_point(&mut self, white_point: f32) { + self.0.white_point = white_point + } + pub fn set_look_up_table_element_count(&mut self, look_up_table_element_count: i32) { + self.0.look_up_table_element_count = look_up_table_element_count + } + pub fn set_look_up_table(&mut self, look_up_table: &[u8]) { + let mut look_up_table = ManuallyDrop::new(look_up_table.to_vec()); + self.0.ptr_look_up_table = look_up_table.as_mut_ptr(); + self.0.look_up_table_element_count = look_up_table.len() as i32; + } +} + +impl ScalingInfo { + pub fn new(scale_x: f64, scale_y: f64, scale_z: f64) -> Self { + Self(ScalingInfoInterop { + scale_x, + scale_y, + scale_z, + }) + } + pub fn get_scale_x(&self) -> f64 { + self.0.scale_x + } + pub fn get_scale_y(&self) -> f64 { + self.0.scale_y + } + pub fn get_scale_z(&self) -> f64 { + self.0.scale_z + } + pub fn set_scale_x(&mut self, scale_x: f64) { + self.0.scale_x = scale_x + } + pub fn set_scale_y(&mut self, scale_y: f64) { + self.0.scale_y = scale_y + } + pub fn set_scale_z(&mut self, scale_z: f64) { + self.0.scale_z = scale_z + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4be7082 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,83 @@ +mod functions; +mod handle; +mod interop; +mod misc; +pub mod sys; + +pub use functions::*; +pub use handle::*; +pub use interop::*; +pub use misc::{LibCZIApiError, RawDataType, PixelType}; + +#[cfg(test)] +mod tests { + use crate::handle::{CziReader, InputStream}; + use crate::interop::{LibCZIBuildInformation, ReaderOpenInfo}; + use anyhow::{Error, Result}; + use std::env; + + #[test] + fn test_libczi_xml() -> 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 metadata_segment = czi.get_metadata_segment()?; + let xml = metadata_segment.get_metadata_as_xml()?; + let s = String::try_from(&xml)?; + println!("xml: {}", &s[..s.len().min(100)]); + Ok(()) + } + + #[test] + fn test_libczi_pyramid_statistics() -> 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 s = czi.get_pyramid_statistics()?; + println!("xml: {}", &s[..s.len().min(100)]); + Ok(()) + } + + #[test] + fn test_libczi_document_info() -> 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 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)]); + Ok(()) + } + + #[test] + fn test_lib_czi_build_information() -> Result<()> { + let build_info = LibCZIBuildInformation::get()?; + 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 tag: {:?}", build_info.get_repository_tag()); + Ok(()) + } +} diff --git a/src/misc.rs b/src/misc.rs new file mode 100644 index 0000000..5781ecb --- /dev/null +++ b/src/misc.rs @@ -0,0 +1,115 @@ +use anyhow::{anyhow, Error, Result}; +use std::fmt; +use std::mem::MaybeUninit; +use std::os::raw::c_int; + +/// the error type for libCZIAPI +#[derive(Clone, Debug)] +pub enum LibCZIApiError { + OK, + InvalidArgument, + InvalidHandle, + OutOfMemory, + IndexOutOfRange, + LockUnlockSemanticViolated, + UnspecifiedError, +} + +impl std::error::Error for LibCZIApiError {} + +impl TryFrom<c_int> for LibCZIApiError { + type Error = Error; + + fn try_from(code: c_int) -> Result<Self> { + match code { + 0 => Ok(LibCZIApiError::OK), + 1 => Err(Error::from(LibCZIApiError::InvalidArgument)), + 2 => Err(Error::from(LibCZIApiError::InvalidHandle)), + 3 => Err(Error::from(LibCZIApiError::OutOfMemory)), + 4 => Err(Error::from(LibCZIApiError::IndexOutOfRange)), + 20 => Err(Error::from(LibCZIApiError::LockUnlockSemanticViolated)), + 50 => Err(Error::from(LibCZIApiError::UnspecifiedError)), + _ => Err(anyhow!("Unknown error code {}", code)), + } + } +} + +impl fmt::Display for LibCZIApiError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "LibCZIApi {self:?}") + } +} + +/// enum for SubBlock.get_raw_data +pub enum RawDataType { + Data, + Metadata, +} + +/// pixel type +pub enum PixelType { + Gray8, + Gray16, + Gray32Float, + Bgr24, + Bgr48, + Bgr96Float, + Bgra32, + Gray64ComplexFloat, + Bgr192ComplexFloat, + Gray32, + Gray64Float, +} + +impl TryFrom<i32> for PixelType { + type Error = Error; + + fn try_from(pixel_type: i32) -> Result<Self> { + match pixel_type { + 0 => Ok(PixelType::Gray8), + 1 => Ok(PixelType::Gray16), + 2 => Ok(PixelType::Gray32Float), + 3 => Ok(PixelType::Bgr24), + 4 => Ok(PixelType::Bgr48), + 8 => Ok(PixelType::Bgr96Float), + 9 => Ok(PixelType::Bgra32), + 10 => Ok(PixelType::Gray64ComplexFloat), + 11 => Ok(PixelType::Bgr192ComplexFloat), + 12 => Ok(PixelType::Gray32), + 13 => Ok(PixelType::Gray64Float), + _ => Err(anyhow!("Unknown pixel type {}", pixel_type)), + } + } +} + +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 { + type Pointer; + + unsafe fn assume_init(ptr: MaybeUninit<Self::Pointer>) -> Self; + + fn as_mut_ptr(&self) -> *mut Self::Pointer + where + Self: Sized; + + fn as_ptr(&self) -> *const Self::Pointer + where + Self: Sized; +} diff --git a/src/sys.rs b/src/sys.rs new file mode 100644 index 0000000..8d2f549 --- /dev/null +++ b/src/sys.rs @@ -0,0 +1,9 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(unsafe_op_in_unsafe_fn)] +#![allow(dead_code)] +#![allow(rustdoc::broken_intra_doc_links)] +#![allow(rustdoc::invalid_html_tags)] + +include!(concat!(env!("OUT_DIR"), "/lib_czi_api.rs"));