nvdialog_rs/
image.rs

1/*
2 *  The MIT License (MIT)
3 *
4 *  Copyright (c) 2022-2025 Aggelos Tselios
5 *
6 *  Permission is hereby granted, free of charge, to any person obtaining a copy
7 *  of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 *  furnished to do so, subject to the following conditions:
12 *
13 *  The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25use nvdialog_sys::ffi::*;
26use std::path::PathBuf;
27use thiserror::Error;
28
29use crate::cstr;
30
31/// A simple image loaded from NvDialog. This is the equivalent to `NvdImage`, although the implementation
32/// makes a few extra safety checks from the Rust side.
33pub struct Image {
34    pub raw: *mut NvdImage,
35    path: Option<PathBuf>,
36}
37
38#[derive(Debug, Error)]
39pub enum ImageError {
40    #[error("Path does not exist in the filesystem")]
41    PathNotFound,
42    #[error("Could not read the image's contents")]
43    ReadingError,
44    #[error("Image is in invalid format and cannot be read")]
45    InvalidFormat,
46    #[error("Unknown error while reading an image - Try using nvd_get_error()")]
47    UnknownError,
48}
49
50impl Image {
51    /// Creates an `Image` from a specified filename.
52    ///
53    /// This function attempts to load an image from the given file path using
54    /// NvDialog's image loading facilities. It performs a few checks to ensure
55    /// robustness and safety from the Rust side.
56    ///
57    /// # Arguments
58    ///
59    /// * `filename` - A `PathBuf` specifying the file path of the image to be loaded.
60    ///
61    /// # Returns
62    ///
63    /// * `Result<Self, ImageError>` - Returns an `Ok` variant containing the `Image`
64    ///   if successful, or an `ImageError` if an error occurs during the process.
65    ///
66    /// # Errors
67    ///
68    /// * `ImageError::PathNotFound` - Returned if the file path does not exist.
69    /// * `ImageError::UnknownError` - Returned if the image cannot be created from
70    ///   the data.
71    ///
72    /// # Safety
73    ///
74    /// This function makes use of `unsafe` blocks to interface with the C functions
75    /// from NvDialog. It is assumed that `nvd_image_from_filename` and
76    /// `nvd_create_image` are safe to call with the provided arguments.
77    pub fn from_filename(filename: PathBuf) -> Result<Self, ImageError> {
78        if !filename.exists() {
79            return Err(ImageError::PathNotFound);
80        }
81        let mut w = 0;
82        let mut h = 0;
83        let data = unsafe {
84            nvd_image_from_filename(cstr!(filename.to_str().unwrap()).as_ptr(), &mut w, &mut h)
85        };
86        let raw = unsafe { nvd_create_image(data, w, h) };
87        if raw.is_null() {
88            return Err(ImageError::UnknownError);
89        }
90
91        Ok(Self {
92            raw,
93            path: Some(filename),
94        })
95    }
96
97    /// Creates an `Image` from the raw data of an image file.
98    ///
99    /// This function creates an `Image` from raw data that is assumed to be in a format
100    /// that NvDialog can read. The data is sent to the NvDialog library without any
101    /// checking on the Rust side, so it is left up to the user to ensure that the data
102    /// is valid.
103    ///
104    /// # Safety
105    ///
106    /// This function is marked `unsafe` because the data passed to it is not checked
107    /// in any way. If the data is not valid, it may cause undefined behavior when
108    /// passed to the C library.
109    ///
110    /// # Arguments
111    ///
112    /// * `data` - The raw image data. This is assumed to be the contents of an image
113    ///   file and should be in a format that NvDialog can read.
114    /// * `width` - The width of the image in pixels.
115    /// * `height` - The height of the image in pixels.
116    ///
117    /// # Returns
118    ///
119    /// * `Result<Self, ImageError>` - Returns an `Ok` variant containing the `Image` if
120    ///   successful, or an `ImageError` if an error occurs.
121    ///
122    /// # Errors
123    ///
124    /// * `ImageError::InvalidFormat` - Returned if the data is not in a valid format
125    ///   for NvDialog.
126    /// * `ImageError::UnknownError` - Returned if the image cannot be created from the
127    ///   data for some other reason.
128    pub unsafe fn from_data(data: &[u8], width: u32, height: u32) -> Result<Self, ImageError> {
129        if data.len() <= 1 {
130            return Err(ImageError::ReadingError);
131        }
132        if data.len() % 4 != 0 {
133            return Err(ImageError::InvalidFormat);
134        }
135
136        let raw = nvd_create_image(data.as_ptr(), width as i32, height as i32);
137        if raw.is_null() {
138            return Err(ImageError::UnknownError);
139        }
140
141        Ok(Self { raw, path: None })
142    }
143
144    pub(crate) fn get_raw(&self) -> *mut NvdImage {
145        self.raw
146    }
147}
148
149impl Drop for Image {
150    fn drop(&mut self) {
151        unsafe { nvd_destroy_image(self.raw) }
152    }
153}