openslide_rs/
wrapper.rs

1use crate::{OpenSlide, Properties, Region, Result, Size, bindings, errors::OpenSlideError};
2use std::path::Path;
3
4#[cfg(feature = "image")]
5use {
6    crate::{
7        Address,
8        utils::{
9            _bgra_to_rgb, _bgra_to_rgba_inplace, preserve_aspect_ratio, resize_rgb_image,
10            resize_rgba_image,
11        },
12    },
13    image::{RgbImage, RgbaImage},
14};
15
16#[cfg(feature = "openslide4")]
17use crate::cache::Cache;
18
19impl Drop for OpenSlide {
20    fn drop(&mut self) {
21        bindings::close(*self.osr);
22    }
23}
24
25impl OpenSlide {
26    /// Get the version of the `OpenSlide` library.
27    pub fn get_version() -> Result<String> {
28        bindings::get_version()
29    }
30
31    /// This method tries to open the slide at the given filename location.
32    ///
33    /// This function can be expensive; avoid calling it unnecessarily. For example, a tile server
34    /// should not create a new object on every tile request. Instead, it should maintain a cache
35    /// of `OpenSlide` objects and reuse them when possible.
36    pub fn new<T: AsRef<Path>>(path: T) -> Result<OpenSlide> {
37        let path = path.as_ref();
38        if !path.exists() {
39            return Err(OpenSlideError::MissingFile(path.display().to_string()));
40        }
41
42        let filename = path.display().to_string();
43        let osr = bindings::open(&filename)?;
44
45        let property_names = bindings::get_property_names(osr)?;
46
47        let property_iter = property_names.into_iter().filter_map(|name| {
48            bindings::get_property_value(osr, &name)
49                .map(|value| (name, value))
50                .ok()
51        });
52
53        let properties = Properties::new(property_iter);
54
55        Ok(OpenSlide {
56            osr: bindings::OpenSlideWrapper(osr),
57            properties,
58        })
59    }
60
61    #[cfg(feature = "openslide4")]
62    pub fn new_with_cache<T: AsRef<Path>>(path: T, capacity: usize) -> Result<OpenSlide> {
63        let osr = OpenSlide::new(path)?;
64        osr.set_cache(Cache::new(capacity)?);
65        Ok(osr)
66    }
67
68    #[cfg(feature = "openslide4")]
69    fn set_cache(&self, cache: Cache) {
70        bindings::set_cache(*self.osr, *cache.0);
71    }
72
73    /// Quickly determine whether a whole slide image is recognized.
74    pub fn detect_vendor(path: &Path) -> Result<String> {
75        if !path.exists() {
76            return Err(OpenSlideError::MissingFile(path.display().to_string()));
77        }
78        let filename = path.display().to_string();
79        bindings::detect_vendor(&filename)
80    }
81
82    #[must_use]
83    pub fn properties(&self) -> &Properties {
84        &self.properties
85    }
86
87    /// Get the number of levels in the whole slide image.
88    pub fn get_level_count(&self) -> Result<u32> {
89        let level_count = bindings::get_level_count(*self.osr)?;
90        let level_count: u32 = level_count.try_into()?;
91        Ok(level_count)
92    }
93
94    /// Get the dimensions of level 0 (the largest level).
95    ///
96    /// This method returns the Size { width, height } number of pixels of the whole slide image at the
97    /// specified level. Returns an error if the level is invalid
98    pub fn get_level_dimensions(&self, level: u32) -> Result<Size> {
99        let level: i32 = level.try_into()?;
100        let (width, height) = bindings::get_level_dimensions(*self.osr, level)?;
101        Ok(Size {
102            w: width.try_into()?,
103            h: height.try_into()?,
104        })
105    }
106
107    /// Get dimensions of all available levels
108    pub fn get_all_level_dimensions(&self) -> Result<Vec<Size>> {
109        let nb_levels = self.get_level_count()?;
110        let mut res = Vec::with_capacity(nb_levels as usize);
111        for level in 0..nb_levels {
112            let level: i32 = level.try_into()?;
113            let (width, height) = bindings::get_level_dimensions(*self.osr, level)?;
114            res.push(Size {
115                w: width.try_into()?,
116                h: height.try_into()?,
117            });
118        }
119        Ok(res)
120    }
121
122    /// Get the downsampling factor of a given level.
123    pub fn get_level_downsample(&self, level: u32) -> Result<f64> {
124        let level: i32 = level.try_into()?;
125        bindings::get_level_downsample(*self.osr, level)
126    }
127
128    /// Get all downsampling factors for all available levels.
129    pub fn get_all_level_downsample(&self) -> Result<Vec<f64>> {
130        let nb_levels = self.get_level_count()?;
131        let mut res = Vec::with_capacity(nb_levels as usize);
132        for level in 0..nb_levels {
133            let downsample = bindings::get_level_downsample(*self.osr, level as i32)?;
134            res.push(downsample);
135        }
136        Ok(res)
137    }
138
139    /// Get the best level to use for displaying the given downsample factor.
140    pub fn get_best_level_for_downsample(&self, downsample: f64) -> Result<u32> {
141        Ok(bindings::get_best_level_for_downsample(*self.osr, downsample)? as u32)
142    }
143
144    /// Get the list of all available properties.
145    #[must_use]
146    pub fn get_property_names(&self) -> Vec<String> {
147        bindings::get_property_names(*self.osr).unwrap_or_else(|_| vec![])
148    }
149
150    /// Get the value of a single property.
151    pub fn get_property_value(&self, name: &str) -> Result<String> {
152        bindings::get_property_value(*self.osr, name)
153    }
154
155    /// Copy pre-multiplied ARGB data from a whole slide image.
156    ///
157    /// This function reads and decompresses a region of a whole slide image into a Vec
158    ///
159    /// Args:
160    ///     offset: (x, y) coordinate (increasing downwards/to the right) of top left pixel position
161    ///     level: At which level to grab the region from
162    ///     size: (width, height) in pixels of the outputted region
163    ///
164    /// Size of output Vec is Width * Height * 4 (RGBA pixels)
165    pub fn read_region(&self, region: &Region) -> Result<Vec<u8>> {
166        bindings::read_region(
167            *self.osr,
168            i64::from(region.address.x),
169            i64::from(region.address.y),
170            region.level.try_into()?,
171            i64::from(region.size.w),
172            i64::from(region.size.h),
173        )
174    }
175
176    /// Get the list name of all available associated image.
177    pub fn get_associated_image_names(&self) -> Result<Vec<String>> {
178        bindings::get_associated_image_names(*self.osr)
179    }
180
181    /// Copy pre-multiplied ARGB data from a whole slide image.
182    ///
183    /// This function reads and decompresses an associated image into an Vec
184    ///
185    /// Args:
186    ///     name: name of the associated image we want to read
187    ///
188    /// Size of output Vec is width * height * 4 (RGBA pixels)
189    pub fn read_associated_buffer(&self, name: &str) -> Result<(Size, Vec<u8>)> {
190        let ((width, height), buffer) = bindings::read_associated_image(*self.osr, name)?;
191        let size = Size {
192            w: width.try_into()?,
193            h: height.try_into()?,
194        };
195        Ok((size, buffer))
196    }
197
198    /// Get the size of an associated image
199    pub fn get_associated_image_dimensions(&self, name: &str) -> Result<Size> {
200        let (width, height) = bindings::get_associated_image_dimensions(*self.osr, name)?;
201        Ok(Size {
202            w: width.try_into()?,
203            h: height.try_into()?,
204        })
205    }
206
207    /// Copy pre-multiplied ARGB data from a whole slide image.
208    ///
209    /// This function reads and decompresses a region of a whole slide image into an `RgbImage`
210    ///
211    /// Args:
212    ///     offset: (x, y) coordinate (increasing downwards/to the right) of top left pixel position
213    ///     level: At which level to grab the region from
214    ///     size: (width, height) in pixels of the outputted region
215    #[cfg(feature = "image")]
216    pub fn read_image_rgba(&self, region: &Region) -> Result<RgbaImage> {
217        let buffer = self.read_region(region)?;
218        let size = region.size;
219        let mut image = RgbaImage::from_vec(size.w, size.h, buffer).unwrap(); // Should be safe because buffer is big enough
220        _bgra_to_rgba_inplace(&mut image);
221        Ok(image)
222    }
223
224    /// Copy pre-multiplied ARGB data from from an associated image..
225    ///
226    /// This function reads and decompresses a region of a whole slide image into an `RgbaImage`
227    ///
228    /// Args:
229    ///     offset: (x, y) coordinate (increasing downwards/to the right) of top left pixel position
230    ///     level: At which level to grab the region from
231    ///     size: (width, height) in pixels of the outputted region
232    #[cfg(feature = "image")]
233    pub fn read_image_rgb(&self, region: &Region) -> Result<RgbImage> {
234        let buffer = self.read_region(region)?;
235        let size = region.size;
236        let image = RgbaImage::from_vec(size.w, size.h, buffer).unwrap(); // Should be safe because buffer is big enough
237        Ok(_bgra_to_rgb(&image))
238    }
239
240    /// Copy pre-multiplied ARGB data from an associated image.
241    ///
242    /// This function reads and decompresses an associated image into an `RgbaImage`
243    ///
244    /// Args:
245    ///     name: name of the associated image we want to read
246    #[cfg(feature = "image")]
247    pub fn read_associated_image_rgba(&self, name: &str) -> Result<RgbaImage> {
248        let (size, buffer) = self.read_associated_buffer(name)?;
249        let mut image = RgbaImage::from_vec(size.w, size.h, buffer).unwrap(); // Should be safe because buffer is big enough
250        _bgra_to_rgba_inplace(&mut image);
251        Ok(image)
252    }
253
254    /// Copy pre-multiplied ARGB data from an associated image.
255    ///
256    /// This function reads and decompresses an associated image into an `RgbaImage`
257    ///
258    /// Args:
259    ///     name: name of the associated image we want to read
260    #[cfg(feature = "image")]
261    pub fn read_associated_image_rgb(&self, name: &str) -> Result<RgbImage> {
262        let (size, buffer) = self.read_associated_buffer(name)?;
263        let image = RgbaImage::from_vec(size.w, size.h, buffer).unwrap(); // Should be safe because buffer is big enough
264        Ok(_bgra_to_rgb(&image))
265    }
266
267    /// Get a RGBA image thumbnail of desired size of the whole slide image.
268    /// Args:
269    ///     size: (width, height) in pixels of the thumbnail
270    #[cfg(feature = "image")]
271    pub fn thumbnail_rgba(&self, size: &Size) -> Result<RgbaImage> {
272        let dimension_level0 = self.get_level_dimensions(0)?;
273
274        let downsample = (
275            f64::from(dimension_level0.w) / f64::from(size.w),
276            f64::from(dimension_level0.h) / f64::from(size.h),
277        );
278        let downsample = f64::max(downsample.0, downsample.1);
279
280        let level = self.get_best_level_for_downsample(downsample)?;
281
282        let region = Region {
283            size: self.get_level_dimensions(level)?,
284            level,
285            address: Address { x: 0, y: 0 },
286        };
287        let image = self.read_image_rgba(&region)?;
288        let size = preserve_aspect_ratio(size, &dimension_level0);
289        let image = resize_rgba_image(image, &size)?;
290
291        Ok(image)
292    }
293
294    /// Get a RGB image thumbnail of desired size of the whole slide image.
295    /// Args:
296    ///     size: (width, height) in pixels of the thumbnail
297    #[cfg(feature = "image")]
298    pub fn thumbnail_rgb(&self, size: &Size) -> Result<RgbImage> {
299        let dimension_level0 = self.get_level_dimensions(0)?;
300
301        let downsample = (
302            f64::from(dimension_level0.w) / f64::from(size.w),
303            f64::from(dimension_level0.h) / f64::from(size.h),
304        );
305        let downsample = f64::max(downsample.0, downsample.1);
306
307        let level = self.get_best_level_for_downsample(downsample)?;
308
309        let region = Region {
310            size: self.get_level_dimensions(level)?,
311            level,
312            address: Address { x: 0, y: 0 },
313        };
314
315        let image = self.read_image_rgb(&region)?;
316        let size = preserve_aspect_ratio(size, &dimension_level0);
317        let image = resize_rgb_image(image, &size)?;
318
319        Ok(image)
320    }
321
322    #[cfg(feature = "openslide4")]
323    pub fn icc_profile(&self) -> Result<Vec<u8>> {
324        bindings::read_icc_profile(*self.osr)
325    }
326
327    #[cfg(feature = "openslide4")]
328    pub fn associated_image_icc_profile(&self, name: &str) -> Result<Vec<u8>> {
329        bindings::read_associated_image_icc_profile(*self.osr, name)
330    }
331}
332
333#[cfg(feature = "deepzoom")]
334use {crate::deepzoom::Bounds, crate::traits::Slide};
335
336#[cfg(feature = "deepzoom")]
337impl Slide for OpenSlide {
338    fn get_bounds(&self) -> Bounds {
339        let properties = &self.properties().openslide_properties;
340        Bounds {
341            x: properties.bounds_x,
342            y: properties.bounds_y,
343            width: properties.bounds_width,
344            height: properties.bounds_height,
345        }
346    }
347
348    fn get_level_count(&self) -> Result<u32> {
349        self.get_level_count()
350    }
351
352    fn get_level_dimensions(&self, level: u32) -> Result<Size> {
353        self.get_level_dimensions(level)
354    }
355
356    fn get_level_downsample(&self, level: u32) -> Result<f64> {
357        self.get_level_downsample(level)
358    }
359
360    fn get_best_level_for_downsample(&self, downsample: f64) -> Result<u32> {
361        self.get_best_level_for_downsample(downsample)
362    }
363
364    fn read_image_rgba(&self, region: &Region) -> Result<RgbaImage> {
365        self.read_image_rgba(region)
366    }
367
368    fn read_image_rgb(&self, region: &Region) -> Result<RgbImage> {
369        self.read_image_rgb(region)
370    }
371}