1use image::imageops::FilterType;
2use image::{ColorType, DynamicImage, GenericImageView, ImageFormat, RgbaImage};
3use std::error::Error;
4use std::io::Cursor;
5pub type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync + 'static>>;
6
7pub trait ContentData {
8 fn get_format(&self) -> ContentFormat;
9
10 fn as_bytes(&self) -> &[u8];
11
12 fn as_str(&self) -> Result<&str>;
13}
14
15pub trait ClipboardHandler {
16 fn on_clipboard_change(&mut self);
17}
18
19pub enum ClipboardContent {
20 Text(String),
21 Rtf(String),
22 Html(String),
23 Image(RustImageData),
24 Files(Vec<String>),
25 Other(String, Vec<u8>),
26}
27
28impl ContentData for ClipboardContent {
29 fn get_format(&self) -> ContentFormat {
30 match self {
31 ClipboardContent::Text(_) => ContentFormat::Text,
32 ClipboardContent::Rtf(_) => ContentFormat::Rtf,
33 ClipboardContent::Html(_) => ContentFormat::Html,
34 ClipboardContent::Image(_) => ContentFormat::Image,
35 ClipboardContent::Files(_) => ContentFormat::Files,
36 ClipboardContent::Other(format, _) => ContentFormat::Other(format.clone()),
37 }
38 }
39
40 fn as_bytes(&self) -> &[u8] {
41 match self {
42 ClipboardContent::Text(data) => data.as_bytes(),
43 ClipboardContent::Rtf(data) => data.as_bytes(),
44 ClipboardContent::Html(data) => data.as_bytes(),
45 ClipboardContent::Image(_) => &[],
47 ClipboardContent::Files(data) => {
48 if let Some(path) = data.first() {
50 path.as_bytes()
51 } else {
52 &[]
53 }
54 }
55 ClipboardContent::Other(_, data) => data.as_slice(),
56 }
57 }
58
59 fn as_str(&self) -> Result<&str> {
60 match self {
61 ClipboardContent::Text(data) => Ok(data),
62 ClipboardContent::Rtf(data) => Ok(data),
63 ClipboardContent::Html(data) => Ok(data),
64 ClipboardContent::Image(_) => Err("can't convert image to string".into()),
65 ClipboardContent::Files(data) => {
66 if let Some(path) = data.first() {
68 Ok(path)
69 } else {
70 Err("content is empty".into())
71 }
72 }
73 ClipboardContent::Other(_, data) => std::str::from_utf8(data).map_err(|e| e.into()),
74 }
75 }
76}
77
78#[derive(Clone)]
79pub enum ContentFormat {
80 Text,
81 Rtf,
82 Html,
83 Image,
84 Files,
85 Other(String),
86}
87
88pub struct RustImageData {
89 width: u32,
90 height: u32,
91 data: Option<DynamicImage>,
92}
93
94pub struct RustImageBuffer(Vec<u8>);
96
97pub trait RustImage: Sized {
98 fn empty() -> Self;
100
101 fn is_empty(&self) -> bool;
102
103 fn from_path(path: &str) -> Result<Self>;
105
106 fn from_bytes(bytes: &[u8]) -> Result<Self>;
108
109 fn from_dynamic_image(image: DynamicImage) -> Self;
110
111 fn get_size(&self) -> (u32, u32);
113
114 fn thumbnail(&self, width: u32, height: u32) -> Result<Self>;
123
124 fn resize(&self, width: u32, height: u32, filter: FilterType) -> Result<Self>;
127
128 fn encode_image(
129 &self,
130 target_color_type: ColorType,
131 format: ImageFormat,
132 ) -> Result<RustImageBuffer>;
133
134 fn to_jpeg(&self) -> Result<RustImageBuffer>;
135
136 fn to_png(&self) -> Result<RustImageBuffer>;
139
140 #[cfg(target_os = "windows")]
141 fn to_bitmap(&self) -> Result<RustImageBuffer>;
142
143 fn save_to_path(&self, path: &str) -> Result<()>;
144
145 fn get_dynamic_image(&self) -> Result<DynamicImage>;
146
147 fn to_rgba8(&self) -> Result<RgbaImage>;
148}
149
150impl RustImage for RustImageData {
151 fn empty() -> Self {
152 RustImageData {
153 width: 0,
154 height: 0,
155 data: None,
156 }
157 }
158
159 fn is_empty(&self) -> bool {
160 self.data.is_none()
161 }
162
163 fn from_path(path: &str) -> Result<Self> {
164 let image = image::open(path)?;
165 let (width, height) = image.dimensions();
166 Ok(RustImageData {
167 width,
168 height,
169 data: Some(image),
170 })
171 }
172
173 fn from_bytes(bytes: &[u8]) -> Result<Self> {
174 let image = image::load_from_memory(bytes)?;
175 let (width, height) = image.dimensions();
176 Ok(RustImageData {
177 width,
178 height,
179 data: Some(image),
180 })
181 }
182
183 fn from_dynamic_image(image: DynamicImage) -> Self {
184 let (width, height) = image.dimensions();
185 RustImageData {
186 width,
187 height,
188 data: Some(image),
189 }
190 }
191
192 fn get_size(&self) -> (u32, u32) {
193 (self.width, self.height)
194 }
195
196 fn thumbnail(&self, width: u32, height: u32) -> Result<Self> {
197 match &self.data {
198 Some(image) => {
199 let resized = image.thumbnail(width, height);
200 Ok(RustImageData {
201 width: resized.width(),
202 height: resized.height(),
203 data: Some(resized),
204 })
205 }
206 None => Err("image is empty".into()),
207 }
208 }
209
210 fn resize(&self, width: u32, height: u32, filter: FilterType) -> Result<Self> {
211 match &self.data {
212 Some(image) => {
213 let resized = image.resize_exact(width, height, filter);
214 Ok(RustImageData {
215 width: resized.width(),
216 height: resized.height(),
217 data: Some(resized),
218 })
219 }
220 None => Err("image is empty".into()),
221 }
222 }
223
224 fn save_to_path(&self, path: &str) -> Result<()> {
225 match &self.data {
226 Some(image) => {
227 image.save(path)?;
228 Ok(())
229 }
230 None => Err("image is empty".into()),
231 }
232 }
233
234 fn get_dynamic_image(&self) -> Result<DynamicImage> {
235 match &self.data {
236 Some(image) => Ok(image.clone()),
237 None => Err("image is empty".into()),
238 }
239 }
240
241 fn to_rgba8(&self) -> Result<RgbaImage> {
242 match &self.data {
243 Some(image) => Ok(image.to_rgba8()),
244 None => Err("image is empty".into()),
245 }
246 }
247
248 fn encode_image(
250 &self,
251 target_color_type: ColorType,
252 format: ImageFormat,
253 ) -> Result<RustImageBuffer> {
254 let image = self.data.as_ref().ok_or("image is empty")?;
255
256 let mut bytes = Vec::new();
257 match (image.color(), target_color_type) {
258 (ColorType::Rgba8, ColorType::Rgb8) => image
259 .to_rgb8()
260 .write_to(&mut Cursor::new(&mut bytes), format)?,
261 (_, ColorType::Rgba8) => image
262 .to_rgba8()
263 .write_to(&mut Cursor::new(&mut bytes), format)?,
264 _ => image.write_to(&mut Cursor::new(&mut bytes), format)?,
265 };
266 Ok(RustImageBuffer(bytes))
267 }
268
269 fn to_jpeg(&self) -> Result<RustImageBuffer> {
270 self.encode_image(ColorType::Rgb8, ImageFormat::Jpeg)
272 }
273
274 fn to_png(&self) -> Result<RustImageBuffer> {
275 self.encode_image(ColorType::Rgba8, ImageFormat::Png)
277 }
278
279 #[cfg(target_os = "windows")]
280 fn to_bitmap(&self) -> Result<RustImageBuffer> {
281 self.encode_image(ColorType::Rgba8, ImageFormat::Bmp)
283 }
284}
285
286impl RustImageBuffer {
287 pub fn get_bytes(&self) -> &[u8] {
288 &self.0
289 }
290
291 pub fn save_to_path(&self, path: &str) -> Result<()> {
292 std::fs::write(path, &self.0)?;
293 Ok(())
294 }
295}