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}