1#[cfg(feature = "std")]
8pub mod decode {
9 use core::fmt;
10
11 use azul_core::resources::{RawImage, RawImageFormat};
12 use azul_css::{impl_result, impl_result_inner, U8Vec};
13 use image::{
14 error::{ImageError, LimitError, LimitErrorKind},
15 DynamicImage,
16 };
17
18 #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
20 #[repr(C)]
21 pub enum DecodeImageError {
22 InsufficientMemory,
23 DimensionError,
24 UnsupportedImageFormat,
25 Unknown,
26 }
27
28 impl fmt::Display for DecodeImageError {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 match self {
31 Self::InsufficientMemory => write!(
32 f,
33 "Error decoding image: Not enough memory available to perform encoding \
34 operation"
35 ),
36 Self::DimensionError => {
37 write!(f, "Error decoding image: Wrong dimensions")
38 }
39 Self::UnsupportedImageFormat => {
40 write!(f, "Error decoding image: Invalid data format")
41 }
42 Self::Unknown => write!(f, "Error decoding image: Unknown error"),
43 }
44 }
45 }
46
47 fn translate_image_error_decode(i: ImageError) -> DecodeImageError {
48 match i {
49 ImageError::Limits(l) => match l.kind() {
50 LimitErrorKind::InsufficientMemory => DecodeImageError::InsufficientMemory,
51 LimitErrorKind::DimensionError => DecodeImageError::DimensionError,
52 _ => DecodeImageError::Unknown,
53 },
54 _ => DecodeImageError::Unknown,
55 }
56 }
57
58 impl_result!(
59 RawImage,
60 DecodeImageError,
61 ResultRawImageDecodeImageError,
62 copy = false,
63 [Debug, Clone]
64 );
65
66 #[must_use] pub fn decode_raw_image_from_any_bytes(image_bytes: &[u8]) -> ResultRawImageDecodeImageError {
71 use azul_core::resources::RawImageData;
72
73 let image_format = match image::guess_format(image_bytes) {
74 Ok(o) => o,
75 Err(e) => {
76 return ResultRawImageDecodeImageError::Err(translate_image_error_decode(e));
77 }
78 };
79
80 let decoded = match image::load_from_memory_with_format(image_bytes, image_format) {
81 Ok(o) => o,
82 Err(e) => {
83 return ResultRawImageDecodeImageError::Err(translate_image_error_decode(e));
84 }
85 };
86
87 let ((width, height), data_format, pixels) = match decoded {
88 DynamicImage::ImageLuma8(i) => (
89 i.dimensions(),
90 RawImageFormat::R8,
91 RawImageData::U8(i.into_vec().into()),
92 ),
93 DynamicImage::ImageLumaA8(i) => (
94 i.dimensions(),
95 RawImageFormat::RG8,
96 RawImageData::U8(i.into_vec().into()),
97 ),
98 DynamicImage::ImageRgb8(i) => (
99 i.dimensions(),
100 RawImageFormat::RGB8,
101 RawImageData::U8(i.into_vec().into()),
102 ),
103 DynamicImage::ImageRgba8(i) => (
104 i.dimensions(),
105 RawImageFormat::RGBA8,
106 RawImageData::U8(i.into_vec().into()),
107 ),
108 DynamicImage::ImageLuma16(i) => (
109 i.dimensions(),
110 RawImageFormat::R16,
111 RawImageData::U16(i.into_vec().into()),
112 ),
113 DynamicImage::ImageLumaA16(i) => (
114 i.dimensions(),
115 RawImageFormat::RG16,
116 RawImageData::U16(i.into_vec().into()),
117 ),
118 DynamicImage::ImageRgb16(i) => (
119 i.dimensions(),
120 RawImageFormat::RGB16,
121 RawImageData::U16(i.into_vec().into()),
122 ),
123 DynamicImage::ImageRgba16(i) => (
124 i.dimensions(),
125 RawImageFormat::RGBA16,
126 RawImageData::U16(i.into_vec().into()),
127 ),
128 DynamicImage::ImageRgb32F(i) => (
129 i.dimensions(),
130 RawImageFormat::RGBF32,
131 RawImageData::F32(i.into_vec().into()),
132 ),
133 DynamicImage::ImageRgba32F(i) => (
134 i.dimensions(),
135 RawImageFormat::RGBAF32,
136 RawImageData::F32(i.into_vec().into()),
137 ),
138 _ => {
139 return ResultRawImageDecodeImageError::Err(DecodeImageError::Unknown);
140 }
141 };
142
143 ResultRawImageDecodeImageError::Ok(RawImage {
144 tag: Vec::new().into(),
145 pixels,
146 width: width as usize,
147 height: height as usize,
148 premultiplied_alpha: false,
149 data_format,
150 })
151 }
152}
153#[cfg(feature = "std")]
154pub mod encode {
155 use alloc::vec::Vec;
156 use core::fmt;
157 use std::io::Cursor;
158
159 use azul_core::resources::{RawImage, RawImageFormat};
160 use azul_css::{impl_result, impl_result_inner, U8Vec};
161 #[cfg(feature = "bmp")]
162 use image::codecs::bmp::BmpEncoder;
163 #[cfg(feature = "gif")]
164 use image::codecs::gif::GifEncoder;
165#[cfg(feature = "jpeg")]
166 use image::codecs::jpeg::JpegEncoder;
167 #[cfg(feature = "png")]
168 use image::codecs::png::PngEncoder;
169 #[cfg(feature = "pnm")]
170 use image::codecs::pnm::PnmEncoder;
171 #[cfg(feature = "tga")]
172 use image::codecs::tga::TgaEncoder;
173 #[cfg(feature = "tiff")]
174 use image::codecs::tiff::TiffEncoder;
175 use image::error::{ImageError, LimitError, LimitErrorKind};
176
177 #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
179 #[repr(C)]
180 pub enum EncodeImageError {
181 EncoderNotAvailable,
183 InsufficientMemory,
184 DimensionError,
185 InvalidData,
186 Unknown,
187 }
188
189 impl fmt::Display for EncodeImageError {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 use self::EncodeImageError::{EncoderNotAvailable, InsufficientMemory, DimensionError, InvalidData, Unknown};
192 match self {
193 EncoderNotAvailable => write!(
194 f,
195 "Missing encoder (library was not compiled with given codec)"
196 ),
197 InsufficientMemory => write!(
198 f,
199 "Error encoding image: Not enough memory available to perform encoding \
200 operation"
201 ),
202 DimensionError => write!(f, "Error encoding image: Wrong dimensions"),
203 InvalidData => write!(f, "Error encoding image: Invalid data format"),
204 Unknown => write!(f, "Error encoding image: Unknown error"),
205 }
206 }
207 }
208
209 const fn translate_rawimage_colortype(i: RawImageFormat) -> image::ColorType {
210 match i {
211 RawImageFormat::R8 => image::ColorType::L8,
212 RawImageFormat::RG8 => image::ColorType::La8,
213 RawImageFormat::RGB8 | RawImageFormat::BGR8 => image::ColorType::Rgb8,
214 RawImageFormat::RGBA8 | RawImageFormat::BGRA8 => image::ColorType::Rgba8,
215 RawImageFormat::R16 => image::ColorType::L16,
216 RawImageFormat::RG16 => image::ColorType::La16,
217 RawImageFormat::RGB16 => image::ColorType::Rgb16,
218 RawImageFormat::RGBA16 => image::ColorType::Rgba16,
219 RawImageFormat::RGBF32 => image::ColorType::Rgb32F,
220 RawImageFormat::RGBAF32 => image::ColorType::Rgba32F,
221 }
222 }
223
224 fn bgr_to_rgb_swap(pixels: &[u8], format: RawImageFormat) -> Option<Vec<u8>> {
225 match format {
226 RawImageFormat::BGR8 => {
227 let mut out = pixels.to_vec();
228 for chunk in out.chunks_exact_mut(3) {
229 chunk.swap(0, 2);
230 }
231 Some(out)
232 }
233 RawImageFormat::BGRA8 => {
234 let mut out = pixels.to_vec();
235 for chunk in out.chunks_exact_mut(4) {
236 chunk.swap(0, 2);
237 }
238 Some(out)
239 }
240 _ => None,
241 }
242 }
243
244 fn translate_image_error_encode(i: ImageError) -> EncodeImageError {
245 match i {
246 ImageError::Limits(l) => match l.kind() {
247 LimitErrorKind::InsufficientMemory => EncodeImageError::InsufficientMemory,
248 LimitErrorKind::DimensionError => EncodeImageError::DimensionError,
249 _ => EncodeImageError::Unknown,
250 },
251 _ => EncodeImageError::Unknown,
252 }
253 }
254
255 impl_result!(
256 U8Vec,
257 EncodeImageError,
258 ResultU8VecEncodeImageError,
259 copy = false,
260 [Debug, Clone]
261 );
262
263 macro_rules! encode_func {
264 ($func:ident, $encoder:ident, $feature:expr) => {
265 #[cfg(feature = $feature)]
266 pub fn $func(image: &RawImage) -> ResultU8VecEncodeImageError {
267 let width = match u32::try_from(image.width) {
268 Ok(w) => w,
269 Err(_) => return ResultU8VecEncodeImageError::Err(EncodeImageError::DimensionError),
270 };
271 let height = match u32::try_from(image.height) {
272 Ok(h) => h,
273 Err(_) => return ResultU8VecEncodeImageError::Err(EncodeImageError::DimensionError),
274 };
275 let mut result = Vec::<u8>::new();
276
277 {
278 let mut cursor = Cursor::new(&mut result);
279 let mut encoder = $encoder::new(&mut cursor);
280 let pixels = match image.pixels.get_u8_vec_ref() {
281 Some(s) => s,
282 None => {
283 return ResultU8VecEncodeImageError::Err(EncodeImageError::InvalidData);
284 }
285 };
286
287 let swapped = bgr_to_rgb_swap(pixels.as_ref(), image.data_format);
288 let pixel_bytes = swapped.as_deref().unwrap_or(pixels.as_ref());
289
290 if let Err(e) = encoder.encode(
291 pixel_bytes,
292 width,
293 height,
294 translate_rawimage_colortype(image.data_format).into(),
295 ) {
296 return ResultU8VecEncodeImageError::Err(translate_image_error_encode(e));
297 }
298 }
299
300 ResultU8VecEncodeImageError::Ok(result.into())
301 }
302
303 #[cfg(not(feature = $feature))]
304 #[must_use] pub const fn $func(image: &RawImage) -> ResultU8VecEncodeImageError {
305 ResultU8VecEncodeImageError::Err(EncodeImageError::EncoderNotAvailable)
306 }
307 };
308 }
309
310 encode_func!(encode_bmp, BmpEncoder, "bmp");
311 encode_func!(encode_tga, TgaEncoder, "tga");
312 encode_func!(encode_tiff, TiffEncoder, "tiff");
313 encode_func!(encode_gif, GifEncoder, "gif");
314 encode_func!(encode_pnm, PnmEncoder, "pnm");
315
316 #[cfg(feature = "png")]
317 pub fn encode_png(image: &RawImage) -> ResultU8VecEncodeImageError {
318 use image::ImageEncoder;
319
320 let width = match u32::try_from(image.width) {
321 Ok(w) => w,
322 Err(_) => return ResultU8VecEncodeImageError::Err(EncodeImageError::DimensionError),
323 };
324 let height = match u32::try_from(image.height) {
325 Ok(h) => h,
326 Err(_) => return ResultU8VecEncodeImageError::Err(EncodeImageError::DimensionError),
327 };
328 let mut result = Vec::<u8>::new();
329
330 {
331 let mut cursor = Cursor::new(&mut result);
332 let mut encoder = PngEncoder::new_with_quality(
333 &mut cursor,
334 image::codecs::png::CompressionType::Best,
335 image::codecs::png::FilterType::Adaptive,
336 );
337 let pixels = match image.pixels.get_u8_vec_ref() {
338 Some(s) => s,
339 None => {
340 return ResultU8VecEncodeImageError::Err(EncodeImageError::InvalidData);
341 }
342 };
343
344 let swapped = bgr_to_rgb_swap(pixels.as_ref(), image.data_format);
345 let pixel_bytes = swapped.as_deref().unwrap_or(pixels.as_ref());
346
347 if let Err(e) = encoder.write_image(
348 pixel_bytes,
349 width,
350 height,
351 translate_rawimage_colortype(image.data_format).into(),
352 ) {
353 return ResultU8VecEncodeImageError::Err(translate_image_error_encode(e));
354 }
355 }
356
357 ResultU8VecEncodeImageError::Ok(result.into())
358 }
359
360 #[cfg(not(feature = "png"))]
361 #[must_use] pub const fn encode_png(image: &RawImage) -> ResultU8VecEncodeImageError {
362 ResultU8VecEncodeImageError::Err(EncodeImageError::EncoderNotAvailable)
363 }
364
365 #[cfg(feature = "jpeg")]
366 pub fn encode_jpeg(image: &RawImage, quality: u8) -> ResultU8VecEncodeImageError {
367 let width = match u32::try_from(image.width) {
368 Ok(w) => w,
369 Err(_) => return ResultU8VecEncodeImageError::Err(EncodeImageError::DimensionError),
370 };
371 let height = match u32::try_from(image.height) {
372 Ok(h) => h,
373 Err(_) => return ResultU8VecEncodeImageError::Err(EncodeImageError::DimensionError),
374 };
375 let mut result = Vec::<u8>::new();
376
377 {
378 let mut cursor = Cursor::new(&mut result);
379 let mut encoder = JpegEncoder::new_with_quality(&mut cursor, quality);
380 let pixels = match image.pixels.get_u8_vec_ref() {
381 Some(s) => s,
382 None => {
383 return ResultU8VecEncodeImageError::Err(EncodeImageError::InvalidData);
384 }
385 };
386
387 let swapped = bgr_to_rgb_swap(pixels.as_ref(), image.data_format);
388 let pixel_bytes = swapped.as_deref().unwrap_or(pixels.as_ref());
389
390 if let Err(e) = encoder.encode(
391 pixel_bytes,
392 width,
393 height,
394 translate_rawimage_colortype(image.data_format).into(),
395 ) {
396 return ResultU8VecEncodeImageError::Err(translate_image_error_encode(e));
397 }
398 }
399
400 ResultU8VecEncodeImageError::Ok(result.into())
401 }
402
403 #[cfg(not(feature = "jpeg"))]
404 #[must_use] pub const fn encode_jpeg(image: &RawImage, quality: u8) -> ResultU8VecEncodeImageError {
405 ResultU8VecEncodeImageError::Err(EncodeImageError::EncoderNotAvailable)
406 }
407}