Skip to main content

azul_layout/
image.rs

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