1use crate::{Bitmap, EncodedImageFormat, Pixmap};
2
3#[cfg(feature = "jpeg")]
4pub mod jpeg_encoder;
5pub mod png_encoder;
6#[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}