streamduck_core/
images.rs1use std::hash::{Hash, Hasher};
2use std::io::Cursor;
3use std::time::Duration;
4use image::{AnimationDecoder, DynamicImage, Frame, ImageFormat};
5use image::codecs::gif::GifDecoder;
6use image::codecs::png::PngDecoder;
7use image::io::Reader;
8use itertools::Itertools;
9use rayon::iter::*;
10use serde::{Serialize, Deserialize};
11use streamdeck::{DeviceImage, ImageMode, Kind};
12use tokio::task::{JoinError, spawn_blocking};
13use crate::thread::util::resize_for_streamdeck;
14
15#[derive(Clone, Debug)]
17pub enum SDImage {
18 SingleImage(DynamicImage),
20
21 AnimatedImage(Vec<AnimationFrame>)
23}
24
25impl SDImage {
26 pub fn from_dynamic_image(image: DynamicImage, size: (usize, usize)) -> SDImage {
28 SDImage::SingleImage(
29 resize_for_streamdeck(size, image)
30 )
31 }
32
33 pub async fn from_frames(frames: Vec<Frame>, size: (usize, usize)) -> SDImage {
35 SDImage::AnimatedImage(
36 convert_frames(frames, size).await
37 )
38 }
39
40 pub async fn from_base64(image: &str, size: (usize, usize)) -> Result<SDImage, ImageDeserializationError> {
42 let bytes = base64::decode(image)?;
43
44 let decoder = Reader::new(Cursor::new(bytes)).with_guessed_format()?;
45
46 if let Some(format) = decoder.format() {
47 match format {
48 ImageFormat::Png => {
50 let decoder = PngDecoder::new(decoder.into_inner())?;
51
52 if decoder.is_apng() {
53 let frames = spawn_blocking(|| decoder.apng().into_frames().collect_frames()).await??;
54
55 Ok(SDImage::AnimatedImage(convert_frames(frames, size).await))
56 } else {
57 Ok(SDImage::SingleImage(resize_for_streamdeck(size, DynamicImage::from_decoder(decoder)?)))
58 }
59 }
60
61 ImageFormat::Gif => {
62 let decoder = GifDecoder::new(decoder.into_inner())?;
63
64 println!("starting gif");
65 let frames = spawn_blocking(|| decoder.into_frames().collect_frames()).await??;
66 println!("converting frames");
67
68 Ok(SDImage::AnimatedImage(convert_frames(frames, size).await))
69 }
70
71 _ => {
72 Ok(SDImage::SingleImage(resize_for_streamdeck(size, decoder.decode()?)))
73 }
74 }
75 } else {
76 Err(ImageDeserializationError::UnrecognizedFormat)
77 }
78 }
79
80 pub fn is_animated(&self) -> bool {
82 match self {
83 SDImage::SingleImage(_) => false,
84 SDImage::AnimatedImage(_) => true,
85 }
86 }
87
88 pub fn get_image(&self) -> DynamicImage {
90 match self {
91 SDImage::SingleImage(img) => img.clone(),
92 SDImage::AnimatedImage(frames) => frames[0].image.clone()
93 }
94 }
95}
96
97#[derive(Serialize, Deserialize, Hash, Debug, Clone)]
99pub enum SDSerializedImage {
100 SingleImage(String),
102 AnimatedImage(Vec<SerializedFrame>)
104}
105
106impl SDSerializedImage {
107 pub fn as_image_blob(&self) -> Result<String, ImageDeserializationError> {
109 match self {
110 SDSerializedImage::SingleImage(image) => Ok(image.clone()),
111 SDSerializedImage::AnimatedImage(frames) => {
112 if let Some(frame) = frames.get(0) {
113 Ok(frame.image.clone())
114 } else {
115 Err(ImageDeserializationError::NoFrame)
116 }
117 }
118 }
119 }
120
121 pub fn is_animated(&self) -> bool {
123 match self {
124 SDSerializedImage::SingleImage(_) => false,
125 SDSerializedImage::AnimatedImage(_) => true,
126 }
127 }
128}
129
130#[derive(Clone, Debug)]
132pub struct AnimationFrame {
133 pub image: DynamicImage,
135 pub index: usize,
137 pub delay: f32,
139}
140
141pub async fn convert_frames(frames: Vec<Frame>, size: (usize, usize)) -> Vec<AnimationFrame> {
143 let frames = spawn_blocking(move || frames.into_par_iter()
144 .enumerate()
145 .map(|(i, x)| {
146 let delay = Duration::from(x.delay()).as_secs_f32();
147 AnimationFrame {
148 image: resize_for_streamdeck(size, DynamicImage::from(x.into_buffer())),
149 index: i,
150 delay
151 }
152 })
153 .collect()).await.unwrap_or_default();
154
155 println!("processed frames");
156
157 return frames;
158}
159
160#[derive(Serialize, Deserialize, Clone, Debug)]
162pub struct SerializedFrame {
163 pub image: String,
165 pub index: usize,
167 pub delay: f32,
169}
170
171impl Hash for SerializedFrame {
172 fn hash<H: Hasher>(&self, state: &mut H) {
173 self.image.hash(state);
174 self.index.hash(state);
175 ((self.delay * 100.0) as i32).hash(state);
176 }
177}
178
179impl From<AnimationFrame> for SerializedFrame {
180 fn from(frame: AnimationFrame) -> Self {
181 SerializedFrame::from(&frame)
182 }
183}
184
185impl From<&AnimationFrame> for SerializedFrame {
186 fn from(frame: &AnimationFrame) -> Self {
187 let mut buffer = vec![];
188 frame.image.write_to(&mut Cursor::new(&mut buffer), ImageFormat::Png).ok();
189
190 SerializedFrame {
191 image: base64::encode(buffer),
192 index: frame.index,
193 delay: frame.delay,
194 }
195 }
196}
197
198impl TryFrom<SerializedFrame> for AnimationFrame {
199 type Error = ImageDeserializationError;
200
201 fn try_from(value: SerializedFrame) -> Result<Self, Self::Error> {
202 AnimationFrame::try_from(&value)
203 }
204}
205
206impl TryFrom<&SerializedFrame> for AnimationFrame {
207 type Error = ImageDeserializationError;
208
209 fn try_from(value: &SerializedFrame) -> Result<Self, Self::Error> {
210 let bytes = base64::decode(&value.image)?;
211
212 let image = Reader::new(Cursor::new(bytes)).with_guessed_format()?.decode()?;
213
214 Ok(AnimationFrame {
215 image,
216 index: value.index,
217 delay: value.delay
218 })
219 }
220}
221
222impl From<SDImage> for SDSerializedImage {
223 fn from(image: SDImage) -> Self {
224 SDSerializedImage::from(&image)
225 }
226}
227
228impl From<&SDImage> for SDSerializedImage {
229 fn from(image: &SDImage) -> Self {
230 match image {
231 SDImage::SingleImage(image) => {
232 SDSerializedImage::SingleImage({
233 let mut buffer = vec![];
234 image.write_to(&mut Cursor::new(&mut buffer), ImageFormat::Png).ok();
235 base64::encode(buffer)
236 })
237 }
238
239 SDImage::AnimatedImage(frames) => {
240 SDSerializedImage::AnimatedImage({
241 frames.into_iter()
242 .map_into()
243 .collect()
244 })
245 }
246 }
247 }
248}
249
250impl TryFrom<SDSerializedImage> for SDImage {
251 type Error = ImageDeserializationError;
252
253 fn try_from(value: SDSerializedImage) -> Result<Self, Self::Error> {
254 SDImage::try_from(&value)
255 }
256}
257
258impl TryFrom<&SDSerializedImage> for SDImage {
259 type Error = ImageDeserializationError;
260
261 fn try_from(value: &SDSerializedImage) -> Result<Self, Self::Error> {
262 match value {
263 SDSerializedImage::SingleImage(image) => {
264 let bytes = base64::decode(image)?;
265
266 Ok(SDImage::SingleImage(Reader::new(Cursor::new(bytes)).with_guessed_format()?.decode()?))
267 }
268
269 SDSerializedImage::AnimatedImage(serialized_frames) => {
270 Ok(SDImage::AnimatedImage({
271 let mut frames = vec![];
272
273 for serialized_frame in serialized_frames {
274 frames.push(serialized_frame.try_into()?)
275 }
276
277 frames
278 }))
279 }
280 }
281 }
282}
283
284pub enum ImageDeserializationError {
286 Base64Error(base64::DecodeError),
288 IoError(std::io::Error),
290 ImageError(image::ImageError),
292 InvalidByteBuffer,
294 UnrecognizedFormat,
296 JoinError(tokio::task::JoinError),
298 NoFrame
300}
301
302impl From<base64::DecodeError> for ImageDeserializationError {
303 fn from(err: base64::DecodeError) -> Self {
304 ImageDeserializationError::Base64Error(err)
305 }
306}
307
308impl From<std::io::Error> for ImageDeserializationError {
309 fn from(err: std::io::Error) -> Self {
310 ImageDeserializationError::IoError(err)
311 }
312}
313
314impl From<image::ImageError> for ImageDeserializationError {
315 fn from(err: image::ImageError) -> Self {
316 ImageDeserializationError::ImageError(err)
317 }
318}
319
320impl From<tokio::task::JoinError> for ImageDeserializationError {
321 fn from(err: JoinError) -> Self {
322 ImageDeserializationError::JoinError(err)
323 }
324}
325
326pub fn convert_image(kind: &Kind, image: DynamicImage) -> DeviceImage {
328 let mut buffer = vec![];
329
330 image.rotate180().to_rgba8().write_to(&mut Cursor::new(&mut buffer), match kind.image_mode() {
331 ImageMode::Bmp => ImageFormat::Bmp,
332 ImageMode::Jpeg => ImageFormat::Jpeg,
333 }).ok();
334
335 DeviceImage::from(buffer)
336}