openslide-rs 2.4.0

Rust bindings of OpenSlide C library
Documentation
//! This module contains the bindings of the `OpenSlide` library and its rust wrappers.
//!
//! `https://openslide.org/api/openslide_8h.html`.
//!

use crate::{Result, errors::OpenSlideError};

use std::{ffi, ops::Deref};

use openslide_sys::sys;

/// wrapper around `OpenSlideT`, this is usefull for implementing Send and Sync
#[derive(Debug)]
pub(crate) struct OpenSlideWrapper(pub(crate) *mut sys::openslide_t);

impl Deref for OpenSlideWrapper {
    type Target = *mut sys::openslide_t;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

unsafe impl Send for OpenSlideWrapper {}
unsafe impl Sync for OpenSlideWrapper {}

#[cfg(feature = "openslide4")]
#[derive(Debug)]
pub(crate) struct CacheWrapper(pub(crate) *mut sys::openslide_cache_t);

#[cfg(feature = "openslide4")]
impl Deref for CacheWrapper {
    type Target = *mut sys::openslide_cache_t;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[cfg(feature = "openslide4")]
unsafe impl Send for CacheWrapper {}

pub fn get_version() -> Result<String> {
    let version = unsafe { sys::openslide_get_version() };
    if !version.is_null() {
        let vendor = unsafe { ffi::CStr::from_ptr(version).to_string_lossy().into_owned() };
        Ok(vendor)
    } else {
        Err(OpenSlideError::CoreError("Cannot get version".to_string()))
    }
}

pub fn detect_vendor(filename: &str) -> Result<String> {
    let c_filename = ffi::CString::new(filename)?;
    unsafe {
        let c_vendor = sys::openslide_detect_vendor(c_filename.as_ptr());
        if !c_vendor.is_null() {
            let vendor = ffi::CStr::from_ptr(c_vendor).to_string_lossy().into_owned();
            Ok(vendor)
        } else {
            Err(OpenSlideError::UnsupportedFile(filename.to_string()))
        }
    }
}

pub fn open(filename: &str) -> Result<*mut sys::openslide_t> {
    let c_filename = ffi::CString::new(filename)?;
    let slide = unsafe { sys::openslide_open(c_filename.as_ptr()) };
    if !slide.is_null() {
        get_error(slide)?;
        Ok(slide)
    } else {
        Err(OpenSlideError::UnsupportedFile(filename.to_string()))
    }
}

pub fn close(osr: *mut sys::openslide_t) {
    unsafe {
        sys::openslide_close(osr);
    }
}

pub fn get_level_count(osr: *mut sys::openslide_t) -> Result<i32> {
    let num_levels = unsafe { sys::openslide_get_level_count(osr) };
    if num_levels == -1 {
        get_error(osr)?;
        return Err(OpenSlideError::CoreError(
            "Cannot get level count".to_string(),
        ));
    }
    Ok(num_levels)
}

pub fn get_level_dimensions(osr: *mut sys::openslide_t, level: i32) -> Result<(i64, i64)> {
    let mut width: i64 = 0;
    let mut height: i64 = 0;
    unsafe {
        sys::openslide_get_level_dimensions(osr, level, &mut width, &mut height);
    }
    if width == -1 || height == -1 {
        get_error(osr)?;
        return Err(OpenSlideError::CoreError(format!("Invalid level {level}")));
    }
    Ok((width, height))
}

pub fn get_level_downsample(osr: *mut sys::openslide_t, level: i32) -> Result<f64> {
    let downsampling_factor = unsafe { sys::openslide_get_level_downsample(osr, level) };
    if downsampling_factor == -1.0 {
        get_error(osr)?;
        return Err(OpenSlideError::CoreError(format!(
            "Cannot compute downsample for level {level}"
        )));
    }
    Ok(downsampling_factor)
}

pub fn get_best_level_for_downsample(osr: *mut sys::openslide_t, downsample: f64) -> Result<i32> {
    let level = unsafe { sys::openslide_get_best_level_for_downsample(osr, downsample) };
    if level == -1 {
        get_error(osr)?;
        return Err(OpenSlideError::CoreError(format!(
            "Cannot compute level for downsample {downsample}"
        )));
    }
    Ok(level)
}

pub fn read_region(
    osr: *mut sys::openslide_t,
    x: i64,
    y: i64,
    level: i32,
    w: i64,
    h: i64,
) -> Result<Vec<u8>> {
    let size = (h * w * 4) as usize;
    let mut buffer: Vec<u8> = Vec::with_capacity(size);
    let p_buffer = buffer.as_mut_ptr();
    unsafe {
        let p_buffer = p_buffer.cast::<u32>();
        sys::openslide_read_region(osr, p_buffer, x, y, level, w, h);
        get_error(osr)?;
        buffer.set_len(size);
    }
    Ok(buffer)
}

pub fn get_property_names(osr: *mut sys::openslide_t) -> Result<Vec<String>> {
    let string_values = unsafe {
        let null_terminated_array_ptr = sys::openslide_get_property_names(osr);
        if null_terminated_array_ptr.is_null() {
            get_error(osr)?;
            return Err(OpenSlideError::CoreError(
                "Cannot get property names".to_string(),
            ));
        }
        let mut counter = 0;
        let mut loc = null_terminated_array_ptr;
        while !(*loc).is_null() {
            counter += 1;
            loc = loc.offset(1);
        }

        let values = std::slice::from_raw_parts(null_terminated_array_ptr, counter as usize);
        values
            .iter()
            .map(|&p| ffi::CStr::from_ptr(p)) // iterator of &CStr
            .map(std::ffi::CStr::to_bytes) // iterator of &[u8]
            .filter_map(|bs| std::str::from_utf8(bs).ok()) // iterator of &str
            .map(std::borrow::ToOwned::to_owned)
            .collect()
    };
    Ok(string_values)
}

pub fn get_property_value(osr: *mut sys::openslide_t, name: &str) -> Result<String> {
    let c_name = ffi::CString::new(name)?;
    let value = unsafe {
        let c_value = sys::openslide_get_property_value(osr, c_name.as_ptr());
        if c_value.is_null() {
            get_error(osr)?;
            return Err(OpenSlideError::CoreError(format!(
                "Error with property named {name}"
            )));
        } else {
            ffi::CStr::from_ptr(c_value).to_string_lossy().into_owned()
        }
    };
    Ok(value)
}

pub fn get_associated_image_names(osr: *mut sys::openslide_t) -> Result<Vec<String>> {
    let string_values = unsafe {
        let null_terminated_array_ptr = sys::openslide_get_associated_image_names(osr);
        if null_terminated_array_ptr.is_null() {
            get_error(osr)?;
            return Err(OpenSlideError::CoreError(
                "Cannot get associated image names".to_string(),
            ));
        }
        let mut counter = 0;
        let mut loc = null_terminated_array_ptr;
        while !(*loc).is_null() {
            counter += 1;
            loc = loc.offset(1);
        }

        let values = std::slice::from_raw_parts(null_terminated_array_ptr, counter as usize);
        values
            .iter()
            .map(|&p| ffi::CStr::from_ptr(p)) // iterator of &CStr
            .map(std::ffi::CStr::to_bytes) // iterator of &[u8]
            .filter_map(|bs| std::str::from_utf8(bs).ok()) // iterator of &str
            .map(std::borrow::ToOwned::to_owned)
            .collect()
    };
    Ok(string_values)
}

pub fn get_associated_image_dimensions(
    osr: *mut sys::openslide_t,
    name: &str,
) -> Result<(i64, i64)> {
    let c_name = ffi::CString::new(name)?;
    let mut width: i64 = 0;
    let mut height: i64 = 0;
    unsafe {
        sys::openslide_get_associated_image_dimensions(
            osr,
            c_name.as_ptr(),
            &mut width,
            &mut height,
        );
    }
    if width == -1 || height == -1 {
        get_error(osr)?;
        return Err(OpenSlideError::CoreError(
            "Unknown associated image".to_string(),
        ));
    }
    Ok((width, height))
}

pub fn read_associated_image(
    osr: *mut sys::openslide_t,
    name: &str,
) -> Result<((i64, i64), Vec<u8>)> {
    let c_name = ffi::CString::new(name)?;
    let (width, height) = get_associated_image_dimensions(osr, name)?;
    let size = (width * height * 4) as usize;
    let mut buffer: Vec<u8> = Vec::with_capacity(size);
    let p_buffer = buffer.as_mut_ptr();
    unsafe {
        let p_buffer = p_buffer.cast::<u32>();
        sys::openslide_read_associated_image(osr, c_name.as_ptr(), p_buffer);
        get_error(osr)?;
        buffer.set_len(size);
    }
    Ok(((width, height), buffer))
}

pub fn get_error(osr: *mut sys::openslide_t) -> Result<()> {
    unsafe {
        let c_value = sys::openslide_get_error(osr);
        if c_value.is_null() {
            Ok(())
        } else {
            let error = ffi::CStr::from_ptr(c_value).to_string_lossy().into_owned();
            Err(OpenSlideError::CoreError(error))
        }
    }
}

#[cfg(feature = "openslide4")]
pub fn get_icc_profile_size(osr: *mut sys::openslide_t) -> Result<i64> {
    let size = unsafe { sys::openslide_get_icc_profile_size(osr) };
    // TODO: check if size == 0 => no ICC profile
    if size == -1 {
        get_error(osr)?;
        return Err(OpenSlideError::CoreError(
            "Cannot get ICC profile size".to_string(),
        ));
    }
    Ok(size)
}

#[cfg(feature = "openslide4")]
pub fn read_icc_profile(osr: *mut sys::openslide_t) -> Result<Vec<u8>> {
    let size = get_icc_profile_size(osr)?;
    let mut buffer: Vec<u8> = Vec::with_capacity(size as usize);
    let p_buffer = buffer.as_mut_ptr().cast::<std::ffi::c_void>();
    unsafe {
        sys::openslide_read_icc_profile(osr, p_buffer);
        get_error(osr)?;
        buffer.set_len(size as usize);
    }
    Ok(buffer)
}

#[cfg(feature = "openslide4")]
pub fn get_associated_image_icc_profile_size(
    osr: *mut sys::openslide_t,
    name: &str,
) -> Result<i64> {
    let c_name = ffi::CString::new(name)?;
    let size =
        unsafe { sys::openslide_get_associated_image_icc_profile_size(osr, c_name.as_ptr()) };
    // TODO: check if size == 0 => no ICC profile
    if size == -1 {
        get_error(osr)?;
        return Err(OpenSlideError::CoreError(
            "Cannot get ICC profile size".to_string(),
        ));
    }
    Ok(size)
}

#[cfg(feature = "openslide4")]
pub fn read_associated_image_icc_profile(
    osr: *mut sys::openslide_t,
    name: &str,
) -> Result<Vec<u8>> {
    let c_name = ffi::CString::new(name)?;
    let size = get_associated_image_icc_profile_size(osr, name)?;
    let mut buffer: Vec<u8> = Vec::with_capacity(size as usize);
    let p_buffer = buffer.as_mut_ptr().cast::<std::ffi::c_void>();
    unsafe {
        sys::openslide_read_associated_image_icc_profile(osr, c_name.as_ptr(), p_buffer);
        get_error(osr)?;
        buffer.set_len(size as usize);
    }
    Ok(buffer)
}

#[cfg(feature = "openslide4")]
pub fn cache_create(capacity: usize) -> Result<*mut sys::openslide_cache_t> {
    let cache = unsafe { sys::openslide_cache_create(capacity) };
    if cache.is_null() {
        Err(OpenSlideError::CoreError("Cannot create cache".to_string()))
    } else {
        Ok(cache)
    }
}

#[cfg(feature = "openslide4")]
pub fn set_cache(osr: *mut sys::openslide_t, cache: *mut sys::openslide_cache_t) {
    unsafe { sys::openslide_set_cache(osr, cache) };
}

#[cfg(feature = "openslide4")]
pub fn cache_release(cache: *mut sys::openslide_cache_t) {
    unsafe { sys::openslide_cache_release(cache) };
}