1use crate::{Bitmap, EncodedImageFormat, Pixmap};
2
3pub mod jpeg_encoder;
4pub mod png_encoder;
5#[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}