elgato_streamdeck/
images.rs1#[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
11pub fn convert_image(kind: Kind, image: DynamicImage) -> Result<Vec<u8>, ImageError> {
13 convert_image_with_format(kind.key_image_format(), image)
14}
15
16pub fn convert_image_with_format(image_format: ImageFormat, image: DynamicImage) -> Result<Vec<u8>, ImageError> {
18 let (ws, hs) = image_format.size;
20
21 let image = image.resize_exact(ws as u32, hs as u32, FilterType::Nearest);
22
23 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 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 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#[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#[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
73pub struct ImageRect {
75 pub w: u16,
77
78 pub h: u16,
80
81 pub data: Vec<u8>,
83}
84
85impl ImageRect {
86 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 #[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}