streamduck_core/
images.rs

1use 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/// Enum that represents various types of images Streamduck will use
16#[derive(Clone, Debug)]
17pub enum SDImage {
18    /// Single normal image
19    SingleImage(DynamicImage),
20
21    /// Animated image
22    AnimatedImage(Vec<AnimationFrame>)
23}
24
25impl SDImage {
26    /// Converts [DynamicImage] to [SDImage]
27    pub fn from_dynamic_image(image: DynamicImage, size: (usize, usize)) -> SDImage {
28        SDImage::SingleImage(
29            resize_for_streamdeck(size, image)
30        )
31    }
32
33    /// Converts [Vec<Frame>] to [SDImage]
34    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    /// Attempts to decode base64 image to [SDImage]
41    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                // Only png and gif that need special handling
49                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    /// Checks if image is animated
81    pub fn is_animated(&self) -> bool {
82        match self {
83            SDImage::SingleImage(_) => false,
84            SDImage::AnimatedImage(_) => true,
85        }
86    }
87
88    /// Retrieves image or first frame
89    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/// Enum that represents serialized variant of [SDImage]
98#[derive(Serialize, Deserialize, Hash, Debug, Clone)]
99pub enum SDSerializedImage {
100    /// Single base64 image
101    SingleImage(String),
102    /// Animation encoded in base64
103    AnimatedImage(Vec<SerializedFrame>)
104}
105
106impl SDSerializedImage {
107    /// Gets image blob
108    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    /// Checks if image is animated
122    pub fn is_animated(&self) -> bool {
123        match self {
124            SDSerializedImage::SingleImage(_) => false,
125            SDSerializedImage::AnimatedImage(_) => true,
126        }
127    }
128}
129
130/// Frame of animated image
131#[derive(Clone, Debug)]
132pub struct AnimationFrame {
133    /// Contents of the frame
134    pub image: DynamicImage,
135    /// Index of the frame in the animation
136    pub index: usize,
137    /// Delay of the frame
138    pub delay: f32,
139}
140
141/// Converts [Frame] vector to [AnimationFrame]
142pub 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/// Serialized version of a frame
161#[derive(Serialize, Deserialize, Clone, Debug)]
162pub struct SerializedFrame {
163    /// Contents of the frame
164    pub image: String,
165    /// Index of the frame in the animation
166    pub index: usize,
167    /// Delay of the frame
168    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
284/// Error for deserializing images
285pub enum ImageDeserializationError {
286    /// Failed to decode base64
287    Base64Error(base64::DecodeError),
288    /// Failed to read an image
289    IoError(std::io::Error),
290    /// Failed to decode the image
291    ImageError(image::ImageError),
292    /// Invalid byte buffer
293    InvalidByteBuffer,
294    /// Image format is not supported
295    UnrecognizedFormat,
296    /// Failed to spawn a blocking task
297    JoinError(tokio::task::JoinError),
298    /// No frame
299    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
326/// Converts image to device image
327pub 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}