1use image::{ColorType, DynamicImage, GenericImageView, ImageError};
2use image::codecs::jpeg::JpegEncoder;
3use image::imageops::FilterType;
4
5use crate::{Kind, AjazzError};
6
7#[derive(Copy, Clone, Debug, Hash)]
9pub enum ImageRotation {
10 Rot0,
12 Rot90,
14 Rot180,
16 Rot270,
18}
19
20#[derive(Copy, Clone, Debug, Hash)]
22pub enum ImageMirroring {
23 None,
25 X,
27 Y,
29 Both,
31}
32
33#[derive(Copy, Clone, Debug, Hash)]
35pub enum ImageMode {
36 None,
38 JPEG,
40}
41
42#[derive(Copy, Clone, Debug, Hash)]
44pub struct ImageFormat {
45 pub mode: ImageMode,
47 pub size: (usize, usize),
49 pub rotation: ImageRotation,
51 pub mirror: ImageMirroring,
53}
54
55impl Default for ImageFormat {
56 fn default() -> Self {
57 Self {
58 mode: ImageMode::None,
59 size: (0, 0),
60 rotation: ImageRotation::Rot0,
61 mirror: ImageMirroring::None,
62 }
63 }
64}
65
66pub fn convert_image(kind: Kind, image: DynamicImage) -> Result<Vec<u8>, ImageError> {
68 convert_image_with_format(kind.key_image_format(), image)
69}
70
71pub fn convert_image_with_format(
73 image_format: ImageFormat,
74 image: DynamicImage,
75) -> Result<Vec<u8>, ImageError> {
76 let (ws, hs) = image_format.size;
78
79 let image = match image_format.rotation {
81 ImageRotation::Rot0 => image,
82 ImageRotation::Rot90 => image.rotate90(),
83 ImageRotation::Rot180 => image.rotate180(),
84 ImageRotation::Rot270 => image.rotate270(),
85 };
86
87 let image = image.resize_exact(ws as u32, hs as u32, FilterType::Triangle);
88
89 let image = match image_format.mirror {
91 ImageMirroring::None => image,
92 ImageMirroring::X => image.fliph(),
93 ImageMirroring::Y => image.flipv(),
94 ImageMirroring::Both => image.fliph().flipv(),
95 };
96
97 let image_data = image.into_rgb8().to_vec();
98
99 match image_format.mode {
101 ImageMode::None => Ok(vec![]),
102 ImageMode::JPEG => {
103 let mut buf = Vec::new();
104 let mut encoder = JpegEncoder::new_with_quality(&mut buf, 90);
105 encoder.encode(&image_data, ws as u32, hs as u32, ColorType::Rgb8.into())?;
106 Ok(buf)
107 }
108 }
109}
110
111#[cfg(feature = "async")]
113#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
114pub fn convert_image_async(kind: Kind, image: DynamicImage) -> Result<Vec<u8>, AjazzError> {
115 Ok(tokio::task::block_in_place(move || {
116 convert_image(kind, image)
117 })?)
118}
119
120#[cfg(feature = "async")]
122#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
123pub fn convert_image_with_format_async(
124 format: ImageFormat,
125 image: DynamicImage,
126) -> Result<Vec<u8>, AjazzError> {
127 Ok(tokio::task::block_in_place(move || {
128 convert_image_with_format(format, image)
129 })?)
130}
131
132pub struct ImageRect {
134 pub w: u16,
136
137 pub h: u16,
139
140 pub data: Vec<u8>,
142}
143
144impl ImageRect {
145 pub fn from_image(image: DynamicImage) -> Result<ImageRect, AjazzError> {
147 let (image_w, image_h) = image.dimensions();
148
149 let image_data = image.into_rgb8().to_vec();
150
151 let mut buf = Vec::new();
152 let mut encoder = JpegEncoder::new_with_quality(&mut buf, 90);
153 encoder.encode(&image_data, image_w, image_h, ColorType::Rgb8.into())?;
154
155 Ok(ImageRect {
156 w: image_w as u16,
157 h: image_h as u16,
158 data: buf,
159 })
160 }
161
162 #[cfg(feature = "async")]
164 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
165 pub fn from_image_async(image: DynamicImage) -> Result<ImageRect, AjazzError> {
166 tokio::task::block_in_place(move || ImageRect::from_image(image))
167 }
168}
169
170#[derive(Clone, Copy)]
171pub(crate) struct WriteImageParameters {
172 pub image_report_length: usize,
173 pub image_report_payload_length: usize,
174}
175
176impl WriteImageParameters {
177 pub fn for_kind(kind: Kind) -> Self {
178 let image_report_length = match kind {
179 kind if kind.is_v1_api() => 513,
180 kind if kind.is_v2_api() => 1025,
181 _ => 1024,
182 };
183
184 let image_report_header_length = 1;
185 let image_report_payload_length = image_report_length - image_report_header_length;
186
187 Self {
188 image_report_length,
189 image_report_payload_length,
190 }
191 }
192}