skia_safe/
encode_.rs

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