Skip to main content

skia_safe/
encode_.rs

1use crate::{Bitmap, EncodedImageFormat, Pixmap};
2
3#[cfg(feature = "jpeg")]
4pub mod jpeg_encoder;
5pub mod png_encoder;
6// TODO: May support with an optional feature, gn flag `skia_use_rust_png_encode`.
7// pub mod png_rust_encoder;
8#[cfg(feature = "webp-encode")]
9pub mod webp_encoder;
10
11impl Pixmap<'_> {
12    pub fn encode(
13        &self,
14        format: EncodedImageFormat,
15        quality: impl Into<Option<u32>>,
16    ) -> Option<Vec<u8>> {
17        crate::encode::pixmap(self, format, quality)
18    }
19}
20
21impl Bitmap {
22    pub fn encode(
23        &self,
24        format: EncodedImageFormat,
25        quality: impl Into<Option<u32>>,
26    ) -> Option<Vec<u8>> {
27        crate::encode::bitmap(self, format, quality)
28    }
29}
30
31impl crate::Image {
32    pub fn encode<'a>(
33        &self,
34        context: impl Into<Option<&'a mut crate::gpu::DirectContext>>,
35        format: EncodedImageFormat,
36        quality: impl Into<Option<u32>>,
37    ) -> Option<crate::Data> {
38        crate::encode::image(context, self, format, quality)
39    }
40}
41
42pub mod encode {
43    #[cfg(feature = "jpeg")]
44    use super::jpeg_encoder;
45    use super::png_encoder;
46    use crate::{Bitmap, EncodedImageFormat, Pixmap};
47
48    pub fn pixmap(
49        bitmap: &Pixmap,
50        format: EncodedImageFormat,
51        quality: impl Into<Option<u32>>,
52    ) -> Option<Vec<u8>> {
53        let mut data = Vec::new();
54        let quality = quality.into().unwrap_or(100).clamp(0, 100);
55        let _ = quality;
56        match format {
57            #[cfg(feature = "jpeg")]
58            EncodedImageFormat::JPEG => {
59                let opts = jpeg_encoder::Options {
60                    quality,
61                    ..jpeg_encoder::Options::default()
62                };
63                jpeg_encoder::encode(bitmap, &mut data, &opts)
64            }
65            EncodedImageFormat::PNG => {
66                let opts = png_encoder::Options::default();
67                png_encoder::encode(bitmap, &mut data, &opts)
68            }
69            #[cfg(feature = "webp-encode")]
70            EncodedImageFormat::WEBP => {
71                use super::webp_encoder;
72                let mut opts = webp_encoder::Options::default();
73                if quality == 100 {
74                    opts.compression = webp_encoder::Compression::Lossless;
75                    opts.quality = 75.0;
76                } else {
77                    opts.compression = webp_encoder::Compression::Lossy;
78                    opts.quality = quality as _;
79                }
80                webp_encoder::encode(bitmap, &mut data, &opts)
81            }
82            _ => false,
83        }
84        .then_some(data)
85    }
86
87    pub fn bitmap(
88        bitmap: &Bitmap,
89        format: EncodedImageFormat,
90        quality: impl Into<Option<u32>>,
91    ) -> Option<Vec<u8>> {
92        let pixels = bitmap.peek_pixels()?;
93        pixmap(&pixels, format, quality)
94    }
95
96    pub fn image<'a>(
97        context: impl Into<Option<&'a mut crate::gpu::DirectContext>>,
98        image: &crate::Image,
99        image_format: EncodedImageFormat,
100        quality: impl Into<Option<u32>>,
101    ) -> Option<crate::Data> {
102        let quality = quality.into().unwrap_or(100).clamp(0, 100);
103        let _ = quality;
104        match image_format {
105            #[cfg(feature = "jpeg")]
106            EncodedImageFormat::JPEG => {
107                let opts = jpeg_encoder::Options {
108                    quality,
109                    ..jpeg_encoder::Options::default()
110                };
111                jpeg_encoder::encode_image(context, image, &opts)
112            }
113            EncodedImageFormat::PNG => {
114                let opts = png_encoder::Options::default();
115                png_encoder::encode_image(context, image, &opts)
116            }
117            #[cfg(feature = "webp-encode")]
118            EncodedImageFormat::WEBP => {
119                use super::webp_encoder;
120                let mut opts = webp_encoder::Options::default();
121                if quality == 100 {
122                    opts.compression = webp_encoder::Compression::Lossless;
123                    opts.quality = 75.0;
124                } else {
125                    opts.compression = webp_encoder::Compression::Lossy;
126                    opts.quality = quality as _;
127                }
128                webp_encoder::encode_image(context, image, &opts)
129            }
130            _ => None,
131        }
132    }
133
134    #[derive(Debug, Clone, PartialEq, Eq)]
135    pub struct Comment {
136        pub keyword: String,
137        pub text: String,
138    }
139
140    impl Comment {
141        pub fn new(keyword: impl Into<String>, text: impl Into<String>) -> Self {
142            Self {
143                keyword: keyword.into(),
144                text: text.into(),
145            }
146        }
147    }
148
149    pub(crate) mod comments {
150        use std::ffi::CString;
151
152        use crate::DataTable;
153
154        use super::Comment;
155
156        pub fn to_data_table(comments: &[Comment]) -> Option<DataTable> {
157            let mut comments_c = Vec::with_capacity(comments.len() * 2);
158            for comment in comments {
159                comments_c.push(CString::new(comment.keyword.as_str()).ok()?);
160                comments_c.push(CString::new(comment.text.as_str()).ok()?);
161            }
162            let slices: Vec<&[u8]> = comments_c.iter().map(|c| c.as_bytes_with_nul()).collect();
163            Some(DataTable::from_slices(&slices))
164        }
165    }
166}