elgato_streamdeck/
images.rs

1#[allow(unused_imports)]
2use std::sync::Arc;
3use image::{ColorType, DynamicImage, GenericImageView, ImageError};
4use image::codecs::bmp::BmpEncoder;
5use image::codecs::jpeg::JpegEncoder;
6use image::imageops::FilterType;
7
8use crate::{Kind, StreamDeckError};
9use crate::info::{ImageFormat, ImageMirroring, ImageMode, ImageRotation};
10
11/// Converts image into image data depending on provided kind of device
12pub fn convert_image(kind: Kind, image: DynamicImage) -> Result<Vec<u8>, ImageError> {
13    convert_image_with_format(kind.key_image_format(), image)
14}
15
16/// Converts image into image data depending on provided image format
17pub fn convert_image_with_format(image_format: ImageFormat, image: DynamicImage) -> Result<Vec<u8>, ImageError> {
18    // Ensuring size of the image
19    let (ws, hs) = image_format.size;
20
21    let image = image.resize_exact(ws as u32, hs as u32, FilterType::Nearest);
22
23    // Applying rotation
24    let image = match image_format.rotation {
25        ImageRotation::Rot0 => image,
26        ImageRotation::Rot90 => image.rotate90(),
27        ImageRotation::Rot180 => image.rotate180(),
28        ImageRotation::Rot270 => image.rotate270(),
29    };
30
31    // Applying mirroring
32    let image = match image_format.mirror {
33        ImageMirroring::None => image,
34        ImageMirroring::X => image.fliph(),
35        ImageMirroring::Y => image.flipv(),
36        ImageMirroring::Both => image.fliph().flipv(),
37    };
38
39    let image_data = image.into_rgb8().to_vec();
40
41    // Encoding image
42    match image_format.mode {
43        ImageMode::None => Ok(vec![]),
44        ImageMode::BMP => {
45            let mut buf = Vec::new();
46            let mut encoder = BmpEncoder::new(&mut buf);
47            encoder.encode(&image_data, ws as u32, hs as u32, ColorType::Rgb8.into())?;
48            Ok(buf)
49        }
50        ImageMode::JPEG => {
51            let mut buf = Vec::new();
52            let mut encoder = JpegEncoder::new_with_quality(&mut buf, 90);
53            encoder.encode(&image_data, ws as u32, hs as u32, ColorType::Rgb8.into())?;
54            Ok(buf)
55        }
56    }
57}
58
59/// Converts image into image data depending on provided kind of device, can be safely ran inside [multi_thread](tokio::runtime::Builder::new_multi_thread) runtime
60#[cfg(feature = "async")]
61#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
62pub fn convert_image_async(kind: Kind, image: DynamicImage) -> Result<Vec<u8>, StreamDeckError> {
63    Ok(tokio::task::block_in_place(move || convert_image(kind, image))?)
64}
65
66/// Converts image into image data depending on provided image format, can be safely ran inside [multi_thread](tokio::runtime::Builder::new_multi_thread) runtime
67#[cfg(feature = "async")]
68#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
69pub fn convert_image_with_format_async(format: ImageFormat, image: DynamicImage) -> Result<Vec<u8>, StreamDeckError> {
70    Ok(tokio::task::block_in_place(move || convert_image_with_format(format, image))?)
71}
72
73/// Rect to be used when trying to send image to lcd screen
74pub struct ImageRect {
75    /// Width of the image
76    pub w: u16,
77
78    /// Height of the image
79    pub h: u16,
80
81    /// Data of the image row by row as RGB
82    pub data: Vec<u8>,
83}
84
85impl ImageRect {
86    /// Converts image to image rect
87    pub fn from_image(image: DynamicImage) -> Result<ImageRect, StreamDeckError> {
88        let (image_w, image_h) = image.dimensions();
89
90        let image_data = image.into_rgb8().to_vec();
91
92        let mut buf = Vec::new();
93        let mut encoder = JpegEncoder::new_with_quality(&mut buf, 90);
94        encoder.encode(&image_data, image_w, image_h, ColorType::Rgb8.into())?;
95
96        Ok(ImageRect {
97            w: image_w as u16,
98            h: image_h as u16,
99            data: buf,
100        })
101    }
102
103    /// Converts image to image rect, can be safely ran inside [multi_thread](tokio::runtime::Builder::new_multi_thread) runtime
104    #[cfg(feature = "async")]
105    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
106    pub fn from_image_async(image: DynamicImage) -> Result<ImageRect, StreamDeckError> {
107        tokio::task::block_in_place(move || ImageRect::from_image(image))
108    }
109}