azul_layout/image/
encode.rs

1use alloc::vec::Vec;
2use core::fmt;
3use std::io::Cursor;
4
5use azul_core::app_resources::{RawImage, RawImageFormat};
6use azul_css::{impl_result, impl_result_inner, U8Vec};
7#[cfg(feature = "bmp")]
8use image::codecs::bmp::BmpEncoder;
9#[cfg(feature = "gif")]
10use image::codecs::gif::GifEncoder;
11#[cfg(feature = "hdr")]
12use image::codecs::hdr::HdrEncoder;
13#[cfg(feature = "jpeg")]
14use image::codecs::jpeg::JpegEncoder;
15#[cfg(feature = "png")]
16use image::codecs::png::PngEncoder;
17#[cfg(feature = "pnm")]
18use image::codecs::pnm::PnmEncoder;
19#[cfg(feature = "tga")]
20use image::codecs::tga::TgaEncoder;
21#[cfg(feature = "tiff")]
22use image::codecs::tiff::TiffEncoder;
23use image::error::{ImageError, LimitError, LimitErrorKind};
24
25#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
26#[repr(C)]
27pub enum EncodeImageError {
28    /// Crate was not compiled with the given encoder flags
29    EncoderNotAvailable,
30    InsufficientMemory,
31    DimensionError,
32    InvalidData,
33    Unknown,
34}
35
36impl fmt::Display for EncodeImageError {
37    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38        use self::EncodeImageError::*;
39        match self {
40            EncoderNotAvailable => write!(
41                f,
42                "Missing encoder (library was not compiled with given codec)"
43            ),
44            InsufficientMemory => write!(
45                f,
46                "Error encoding image: Not enough memory available to perform encoding operation"
47            ),
48            DimensionError => write!(f, "Error encoding image: Wrong dimensions"),
49            InvalidData => write!(f, "Error encoding image: Invalid data format"),
50            Unknown => write!(f, "Error encoding image: Unknown error"),
51        }
52    }
53}
54
55const fn translate_rawimage_colortype(i: RawImageFormat) -> image::ColorType {
56    match i {
57        RawImageFormat::R8 => image::ColorType::L8,
58        RawImageFormat::RG8 => image::ColorType::La8,
59        RawImageFormat::RGB8 => image::ColorType::Rgb8,
60        RawImageFormat::RGBA8 => image::ColorType::Rgba8,
61        RawImageFormat::BGR8 => image::ColorType::Rgb8, // TODO: ???
62        RawImageFormat::BGRA8 => image::ColorType::Rgba8, // TODO: ???
63        RawImageFormat::R16 => image::ColorType::L16,
64        RawImageFormat::RG16 => image::ColorType::La16,
65        RawImageFormat::RGB16 => image::ColorType::Rgb16,
66        RawImageFormat::RGBA16 => image::ColorType::Rgba16,
67        RawImageFormat::RGBF32 => image::ColorType::Rgb32F,
68        RawImageFormat::RGBAF32 => image::ColorType::Rgba32F,
69    }
70}
71
72fn translate_image_error_encode(i: ImageError) -> EncodeImageError {
73    match i {
74        ImageError::Limits(l) => match l.kind() {
75            LimitErrorKind::InsufficientMemory => EncodeImageError::InsufficientMemory,
76            LimitErrorKind::DimensionError => EncodeImageError::DimensionError,
77            _ => EncodeImageError::Unknown,
78        },
79        _ => EncodeImageError::Unknown,
80    }
81}
82
83impl_result!(
84    U8Vec,
85    EncodeImageError,
86    ResultU8VecEncodeImageError,
87    copy = false,
88    [Debug, Clone]
89);
90
91macro_rules! encode_func {
92    ($func:ident, $encoder:ident, $feature:expr) => {
93        #[cfg(feature = $feature)]
94        pub fn $func(image: &RawImage) -> ResultU8VecEncodeImageError {
95            let mut result = Vec::<u8>::new();
96
97            {
98                let mut cursor = Cursor::new(&mut result);
99                let mut encoder = $encoder::new(&mut cursor);
100                let pixels = match image.pixels.get_u8_vec_ref() {
101                    Some(s) => s,
102                    None => {
103                        return ResultU8VecEncodeImageError::Err(EncodeImageError::InvalidData);
104                    }
105                };
106
107                if let Err(e) = encoder.encode(
108                    pixels.as_ref(),
109                    image.width as u32,
110                    image.height as u32,
111                    translate_rawimage_colortype(image.data_format).into(),
112                ) {
113                    return ResultU8VecEncodeImageError::Err(translate_image_error_encode(e));
114                }
115            }
116
117            ResultU8VecEncodeImageError::Ok(result.into())
118        }
119
120        #[cfg(not(feature = $feature))]
121        pub fn $func(image: &RawImage) -> ResultU8VecEncodeImageError {
122            ResultU8VecEncodeImageError::Err(EncodeImageError::EncoderNotAvailable)
123        }
124    };
125}
126
127encode_func!(encode_bmp, BmpEncoder, "bmp");
128encode_func!(encode_tga, TgaEncoder, "tga");
129encode_func!(encode_tiff, TiffEncoder, "tiff");
130encode_func!(encode_gif, GifEncoder, "gif");
131encode_func!(encode_pnm, PnmEncoder, "pnm");
132
133#[cfg(feature = "png")]
134pub fn encode_png(image: &RawImage) -> ResultU8VecEncodeImageError {
135    use image::ImageEncoder;
136
137    let mut result = Vec::<u8>::new();
138
139    {
140        let mut cursor = Cursor::new(&mut result);
141        let mut encoder = PngEncoder::new_with_quality(
142            &mut cursor,
143            image::codecs::png::CompressionType::Best,
144            image::codecs::png::FilterType::Adaptive,
145        );
146        let pixels = match image.pixels.get_u8_vec_ref() {
147            Some(s) => s,
148            None => {
149                return ResultU8VecEncodeImageError::Err(EncodeImageError::InvalidData);
150            }
151        };
152
153        if let Err(e) = encoder.write_image(
154            pixels.as_ref(),
155            image.width as u32,
156            image.height as u32,
157            translate_rawimage_colortype(image.data_format).into(),
158        ) {
159            println!("{:?}", e);
160            return ResultU8VecEncodeImageError::Err(translate_image_error_encode(e));
161        }
162    }
163
164    ResultU8VecEncodeImageError::Ok(result.into())
165}
166
167#[cfg(not(feature = "png"))]
168pub fn encode_png(image: &RawImage) -> ResultU8VecEncodeImageError {
169    ResultU8VecEncodeImageError::Err(EncodeImageError::EncoderNotAvailable)
170}
171
172#[cfg(feature = "jpeg")]
173pub fn encode_jpeg(image: &RawImage, quality: u8) -> ResultU8VecEncodeImageError {
174    let mut result = Vec::<u8>::new();
175
176    {
177        let mut cursor = Cursor::new(&mut result);
178        let mut encoder = JpegEncoder::new_with_quality(&mut cursor, quality);
179        let pixels = match image.pixels.get_u8_vec_ref() {
180            Some(s) => s,
181            None => {
182                return ResultU8VecEncodeImageError::Err(EncodeImageError::InvalidData);
183            }
184        };
185
186        if let Err(e) = encoder.encode(
187            pixels.as_ref(),
188            image.width as u32,
189            image.height as u32,
190            translate_rawimage_colortype(image.data_format).into(),
191        ) {
192            println!("{:?}", e);
193            return ResultU8VecEncodeImageError::Err(translate_image_error_encode(e));
194        }
195    }
196
197    ResultU8VecEncodeImageError::Ok(result.into())
198}
199
200#[cfg(not(feature = "jpeg"))]
201pub fn encode_jpeg(image: &RawImage) -> ResultU8VecEncodeImageError {
202    ResultU8VecEncodeImageError::Err(EncodeImageError::EncoderNotAvailable)
203}