/rust/registry/src/index.crates.io-1949cf8c6b5b557f/image-webp-0.2.4/src/encoder.rs
Line | Count | Source |
1 | | //! Encoding of WebP images. |
2 | | use std::collections::BinaryHeap; |
3 | | use std::io::{self, Write}; |
4 | | use std::slice::ChunksExact; |
5 | | |
6 | | use quick_error::quick_error; |
7 | | |
8 | | /// Color type of the image. |
9 | | /// |
10 | | /// Note that the WebP format doesn't have a concept of color type. All images are encoded as RGBA |
11 | | /// and some decoders may treat them as such. This enum is used to indicate the color type of the |
12 | | /// input data provided to the encoder, which can help improve compression ratio. |
13 | | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
14 | | pub enum ColorType { |
15 | | /// Opaque image with a single luminance byte per pixel. |
16 | | L8, |
17 | | /// Image with a luminance and alpha byte per pixel. |
18 | | La8, |
19 | | /// Opaque image with a red, green, and blue byte per pixel. |
20 | | Rgb8, |
21 | | /// Image with a red, green, blue, and alpha byte per pixel. |
22 | | Rgba8, |
23 | | } |
24 | | |
25 | | quick_error! { |
26 | | /// Error that can occur during encoding. |
27 | | #[derive(Debug)] |
28 | | #[non_exhaustive] |
29 | | pub enum EncodingError { |
30 | | /// An IO error occurred. |
31 | | IoError(err: io::Error) { |
32 | | from() |
33 | | display("IO error: {}", err) |
34 | | source(err) |
35 | | } |
36 | | |
37 | | /// The image dimensions are not allowed by the WebP format. |
38 | | InvalidDimensions { |
39 | | display("Invalid dimensions") |
40 | | } |
41 | | } |
42 | | } |
43 | | |
44 | | struct BitWriter<W> { |
45 | | writer: W, |
46 | | buffer: u64, |
47 | | nbits: u8, |
48 | | } |
49 | | |
50 | | impl<W: Write> BitWriter<W> { |
51 | 17.9M | fn write_bits(&mut self, bits: u64, nbits: u8) -> io::Result<()> { |
52 | 17.9M | debug_assert!(nbits <= 64); |
53 | | |
54 | 17.9M | self.buffer |= bits << self.nbits; |
55 | 17.9M | self.nbits += nbits; |
56 | | |
57 | 17.9M | if self.nbits >= 64 { |
58 | 3.91M | self.writer.write_all(&self.buffer.to_le_bytes())?; |
59 | 3.91M | self.nbits -= 64; |
60 | 3.91M | self.buffer = bits.checked_shr(u32::from(nbits - self.nbits)).unwrap_or(0); |
61 | 14.0M | } |
62 | 17.9M | debug_assert!(self.nbits < 64); |
63 | 17.9M | Ok(()) |
64 | 17.9M | } Unexecuted instantiation: <image_webp::encoder::BitWriter<_>>::write_bits <image_webp::encoder::BitWriter<&mut alloc::vec::Vec<u8>>>::write_bits Line | Count | Source | 51 | 17.9M | fn write_bits(&mut self, bits: u64, nbits: u8) -> io::Result<()> { | 52 | 17.9M | debug_assert!(nbits <= 64); | 53 | | | 54 | 17.9M | self.buffer |= bits << self.nbits; | 55 | 17.9M | self.nbits += nbits; | 56 | | | 57 | 17.9M | if self.nbits >= 64 { | 58 | 3.91M | self.writer.write_all(&self.buffer.to_le_bytes())?; | 59 | 3.91M | self.nbits -= 64; | 60 | 3.91M | self.buffer = bits.checked_shr(u32::from(nbits - self.nbits)).unwrap_or(0); | 61 | 14.0M | } | 62 | 17.9M | debug_assert!(self.nbits < 64); | 63 | 17.9M | Ok(()) | 64 | 17.9M | } |
|
65 | | |
66 | 1.91k | fn flush(&mut self) -> io::Result<()> { |
67 | 1.91k | if self.nbits % 8 != 0 { |
68 | 1.66k | self.write_bits(0, 8 - self.nbits % 8)?; |
69 | 250 | } |
70 | 1.91k | if self.nbits > 0 { |
71 | 1.63k | self.writer |
72 | 1.63k | .write_all(&self.buffer.to_le_bytes()[..self.nbits as usize / 8]) |
73 | 1.63k | .unwrap(); |
74 | 1.63k | self.buffer = 0; |
75 | 1.63k | self.nbits = 0; |
76 | 1.63k | } |
77 | 1.91k | Ok(()) |
78 | 1.91k | } Unexecuted instantiation: <image_webp::encoder::BitWriter<_>>::flush <image_webp::encoder::BitWriter<&mut alloc::vec::Vec<u8>>>::flush Line | Count | Source | 66 | 1.91k | fn flush(&mut self) -> io::Result<()> { | 67 | 1.91k | if self.nbits % 8 != 0 { | 68 | 1.66k | self.write_bits(0, 8 - self.nbits % 8)?; | 69 | 250 | } | 70 | 1.91k | if self.nbits > 0 { | 71 | 1.63k | self.writer | 72 | 1.63k | .write_all(&self.buffer.to_le_bytes()[..self.nbits as usize / 8]) | 73 | 1.63k | .unwrap(); | 74 | 1.63k | self.buffer = 0; | 75 | 1.63k | self.nbits = 0; | 76 | 1.63k | } | 77 | 1.91k | Ok(()) | 78 | 1.91k | } |
|
79 | | } |
80 | | |
81 | 12.0k | fn write_single_entry_huffman_tree<W: Write>(w: &mut BitWriter<W>, symbol: u8) -> io::Result<()> { |
82 | 12.0k | w.write_bits(1, 2)?; |
83 | 12.0k | if symbol <= 1 { |
84 | 9.91k | w.write_bits(0, 1)?; |
85 | 9.91k | w.write_bits(u64::from(symbol), 1)?; |
86 | | } else { |
87 | 2.11k | w.write_bits(1, 1)?; |
88 | 2.11k | w.write_bits(u64::from(symbol), 8)?; |
89 | | } |
90 | 12.0k | Ok(()) |
91 | 12.0k | } Unexecuted instantiation: image_webp::encoder::write_single_entry_huffman_tree::<_> image_webp::encoder::write_single_entry_huffman_tree::<&mut alloc::vec::Vec<u8>> Line | Count | Source | 81 | 12.0k | fn write_single_entry_huffman_tree<W: Write>(w: &mut BitWriter<W>, symbol: u8) -> io::Result<()> { | 82 | 12.0k | w.write_bits(1, 2)?; | 83 | 12.0k | if symbol <= 1 { | 84 | 9.91k | w.write_bits(0, 1)?; | 85 | 9.91k | w.write_bits(u64::from(symbol), 1)?; | 86 | | } else { | 87 | 2.11k | w.write_bits(1, 1)?; | 88 | 2.11k | w.write_bits(u64::from(symbol), 8)?; | 89 | | } | 90 | 12.0k | Ok(()) | 91 | 12.0k | } |
|
92 | | |
93 | 14.7k | fn build_huffman_tree( |
94 | 14.7k | frequencies: &[u32], |
95 | 14.7k | lengths: &mut [u8], |
96 | 14.7k | codes: &mut [u16], |
97 | 14.7k | length_limit: u8, |
98 | 14.7k | ) -> bool { |
99 | 14.7k | assert_eq!(frequencies.len(), lengths.len()); |
100 | 14.7k | assert_eq!(frequencies.len(), codes.len()); |
101 | | |
102 | 2.12M | if frequencies.iter().filter(|&&f| f > 0).count() <= 1 { |
103 | 584 | lengths.fill(0); |
104 | 584 | codes.fill(0); |
105 | 584 | return false; |
106 | 14.2k | } |
107 | | |
108 | | #[derive(Eq, PartialEq, Copy, Clone, Debug)] |
109 | | struct Item(u32, u16); |
110 | | impl Ord for Item { |
111 | 14.7M | fn cmp(&self, other: &Self) -> std::cmp::Ordering { |
112 | 14.7M | other.0.cmp(&self.0) |
113 | 14.7M | } |
114 | | } |
115 | | impl PartialOrd for Item { |
116 | 14.7M | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { |
117 | 14.7M | Some(self.cmp(other)) |
118 | 14.7M | } |
119 | | } |
120 | | |
121 | | // Build a huffman tree |
122 | 14.2k | let mut internal_nodes = Vec::new(); |
123 | 14.2k | let mut nodes = BinaryHeap::from_iter( |
124 | 14.2k | frequencies |
125 | 14.2k | .iter() |
126 | 14.2k | .enumerate() |
127 | 1.98M | .filter(|(_, &frequency)| frequency > 0) |
128 | 888k | .map(|(i, &frequency)| Item(frequency, i as u16)), |
129 | | ); |
130 | 888k | while nodes.len() > 1 { |
131 | 874k | let Item(frequency1, index1) = nodes.pop().unwrap(); |
132 | 874k | let mut root = nodes.peek_mut().unwrap(); |
133 | 874k | internal_nodes.push((index1, root.1)); |
134 | 874k | *root = Item( |
135 | 874k | frequency1 + root.0, |
136 | 874k | internal_nodes.len() as u16 + frequencies.len() as u16 - 1, |
137 | 874k | ); |
138 | 874k | } |
139 | | |
140 | | // Walk the tree to assign code lengths |
141 | 14.2k | lengths.fill(0); |
142 | 14.2k | let mut stack = Vec::new(); |
143 | 14.2k | stack.push((nodes.pop().unwrap().1, 0)); |
144 | 1.77M | while let Some((node, depth)) = stack.pop() { |
145 | 1.76M | let node = node as usize; |
146 | 1.76M | if node < frequencies.len() { |
147 | 888k | lengths[node] = depth as u8; |
148 | 888k | } else { |
149 | 874k | let (left, right) = internal_nodes[node - frequencies.len()]; |
150 | 874k | stack.push((left, depth + 1)); |
151 | 874k | stack.push((right, depth + 1)); |
152 | 874k | } |
153 | | } |
154 | | |
155 | | // Limit the codes to length length_limit |
156 | 14.2k | let mut max_length = 0; |
157 | 1.98M | for &length in lengths.iter() { |
158 | 1.98M | max_length = max_length.max(length); |
159 | 1.98M | } |
160 | 14.2k | if max_length > length_limit { |
161 | 2.40k | let mut counts = [0u32; 16]; |
162 | 254k | for &length in lengths.iter() { |
163 | 254k | counts[length.min(length_limit) as usize] += 1; |
164 | 254k | } |
165 | | |
166 | 2.40k | let mut total = 0; |
167 | 23.7k | for (i, count) in counts |
168 | 2.40k | .iter() |
169 | 2.40k | .enumerate() |
170 | 2.40k | .skip(1) |
171 | 2.40k | .take(length_limit as usize) |
172 | 23.7k | { |
173 | 23.7k | total += count << (length_limit as usize - i); |
174 | 23.7k | } |
175 | | |
176 | 9.92k | while total > 1u32 << length_limit { |
177 | 7.51k | let mut i = length_limit as usize - 1; |
178 | 8.71k | while counts[i] == 0 { |
179 | 1.20k | i -= 1; |
180 | 1.20k | } |
181 | 7.51k | counts[i] -= 1; |
182 | 7.51k | counts[length_limit as usize] -= 1; |
183 | 7.51k | counts[i + 1] += 2; |
184 | 7.51k | total -= 1; |
185 | | } |
186 | | |
187 | | // assign new lengths |
188 | 2.40k | let mut len = length_limit; |
189 | 2.40k | let mut indexes = frequencies.iter().copied().enumerate().collect::<Vec<_>>(); |
190 | 2.40k | indexes.sort_unstable_by_key(|&(_, frequency)| frequency); |
191 | 256k | for &(i, frequency) in &indexes { |
192 | 254k | if frequency > 0 { |
193 | 184k | while counts[len as usize] == 0 { |
194 | 20.5k | len -= 1; |
195 | 20.5k | } |
196 | 163k | lengths[i] = len; |
197 | 163k | counts[len as usize] -= 1; |
198 | 90.4k | } |
199 | | } |
200 | 11.8k | } |
201 | | |
202 | | // Assign codes |
203 | 14.2k | codes.fill(0); |
204 | 14.2k | let mut code = 0u32; |
205 | 156k | for len in 1..=length_limit { |
206 | 28.8M | for (i, &length) in lengths.iter().enumerate() { |
207 | 28.8M | if length == len { |
208 | 888k | codes[i] = (code as u16).reverse_bits() >> (16 - len); |
209 | 888k | code += 1; |
210 | 27.9M | } |
211 | | } |
212 | 156k | code <<= 1; |
213 | | } |
214 | 14.2k | assert_eq!(code, 2 << length_limit); |
215 | | |
216 | 14.2k | true |
217 | 14.7k | } |
218 | | |
219 | 7.66k | fn write_huffman_tree<W: Write>( |
220 | 7.66k | w: &mut BitWriter<W>, |
221 | 7.66k | frequencies: &[u32], |
222 | 7.66k | lengths: &mut [u8], |
223 | 7.66k | codes: &mut [u16], |
224 | 7.66k | ) -> io::Result<()> { |
225 | 7.66k | if !build_huffman_tree(frequencies, lengths, codes, 15) { |
226 | 530 | let symbol = frequencies |
227 | 530 | .iter() |
228 | 26.6k | .position(|&frequency| frequency > 0) Unexecuted instantiation: image_webp::encoder::write_huffman_tree::<_>::{closure#0}image_webp::encoder::write_huffman_tree::<&mut alloc::vec::Vec<u8>>::{closure#0}Line | Count | Source | 228 | 26.6k | .position(|&frequency| frequency > 0) |
|
229 | 530 | .unwrap_or(0); |
230 | 530 | return write_single_entry_huffman_tree(w, symbol as u8); |
231 | 7.13k | } |
232 | | |
233 | 7.13k | let mut code_length_lengths = [0u8; 16]; |
234 | 7.13k | let mut code_length_codes = [0u16; 16]; |
235 | 7.13k | let mut code_length_frequencies = [0u32; 16]; |
236 | 1.86M | for &length in lengths.iter() { |
237 | 1.86M | code_length_frequencies[length as usize] += 1; |
238 | 1.86M | } |
239 | 7.13k | let single_code_length_length = !build_huffman_tree( |
240 | 7.13k | &code_length_frequencies, |
241 | 7.13k | &mut code_length_lengths, |
242 | 7.13k | &mut code_length_codes, |
243 | 7.13k | 7, |
244 | 7.13k | ); |
245 | | |
246 | | const CODE_LENGTH_ORDER: [usize; 19] = [ |
247 | | 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, |
248 | | ]; |
249 | | |
250 | | // Write the huffman tree |
251 | 7.13k | w.write_bits(0, 1)?; // normal huffman tree |
252 | 7.13k | w.write_bits(19 - 4, 4)?; // num_code_lengths - 4 |
253 | | |
254 | 142k | for i in CODE_LENGTH_ORDER { |
255 | 135k | if i > 15 || code_length_frequencies[i] == 0 { |
256 | 80.3k | w.write_bits(0, 3)?; |
257 | 55.1k | } else if single_code_length_length { |
258 | 54 | w.write_bits(1, 3)?; |
259 | | } else { |
260 | 55.1k | w.write_bits(u64::from(code_length_lengths[i]), 3)?; |
261 | | } |
262 | | } |
263 | | |
264 | 7.13k | match lengths.len() { |
265 | | 256 => { |
266 | 5.34k | w.write_bits(1, 1)?; // max_symbol is stored |
267 | 5.34k | w.write_bits(3, 3)?; // max_symbol_nbits / 2 - 2 |
268 | 5.34k | w.write_bits(254, 8)?; // max_symbol - 2 |
269 | | } |
270 | 1.79k | 280 => w.write_bits(0, 1)?, |
271 | 0 | _ => unreachable!(), |
272 | | } |
273 | | |
274 | | // Write the huffman codes |
275 | 7.13k | if !single_code_length_length { |
276 | 1.85M | for &len in lengths.iter() { |
277 | 1.85M | w.write_bits( |
278 | 1.85M | u64::from(code_length_codes[len as usize]), |
279 | 1.85M | code_length_lengths[len as usize], |
280 | 0 | )?; |
281 | | } |
282 | 54 | } |
283 | | |
284 | 7.13k | Ok(()) |
285 | 7.66k | } Unexecuted instantiation: image_webp::encoder::write_huffman_tree::<_> image_webp::encoder::write_huffman_tree::<&mut alloc::vec::Vec<u8>> Line | Count | Source | 219 | 7.66k | fn write_huffman_tree<W: Write>( | 220 | 7.66k | w: &mut BitWriter<W>, | 221 | 7.66k | frequencies: &[u32], | 222 | 7.66k | lengths: &mut [u8], | 223 | 7.66k | codes: &mut [u16], | 224 | 7.66k | ) -> io::Result<()> { | 225 | 7.66k | if !build_huffman_tree(frequencies, lengths, codes, 15) { | 226 | 530 | let symbol = frequencies | 227 | 530 | .iter() | 228 | 530 | .position(|&frequency| frequency > 0) | 229 | 530 | .unwrap_or(0); | 230 | 530 | return write_single_entry_huffman_tree(w, symbol as u8); | 231 | 7.13k | } | 232 | | | 233 | 7.13k | let mut code_length_lengths = [0u8; 16]; | 234 | 7.13k | let mut code_length_codes = [0u16; 16]; | 235 | 7.13k | let mut code_length_frequencies = [0u32; 16]; | 236 | 1.86M | for &length in lengths.iter() { | 237 | 1.86M | code_length_frequencies[length as usize] += 1; | 238 | 1.86M | } | 239 | 7.13k | let single_code_length_length = !build_huffman_tree( | 240 | 7.13k | &code_length_frequencies, | 241 | 7.13k | &mut code_length_lengths, | 242 | 7.13k | &mut code_length_codes, | 243 | 7.13k | 7, | 244 | 7.13k | ); | 245 | | | 246 | | const CODE_LENGTH_ORDER: [usize; 19] = [ | 247 | | 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, | 248 | | ]; | 249 | | | 250 | | // Write the huffman tree | 251 | 7.13k | w.write_bits(0, 1)?; // normal huffman tree | 252 | 7.13k | w.write_bits(19 - 4, 4)?; // num_code_lengths - 4 | 253 | | | 254 | 142k | for i in CODE_LENGTH_ORDER { | 255 | 135k | if i > 15 || code_length_frequencies[i] == 0 { | 256 | 80.3k | w.write_bits(0, 3)?; | 257 | 55.1k | } else if single_code_length_length { | 258 | 54 | w.write_bits(1, 3)?; | 259 | | } else { | 260 | 55.1k | w.write_bits(u64::from(code_length_lengths[i]), 3)?; | 261 | | } | 262 | | } | 263 | | | 264 | 7.13k | match lengths.len() { | 265 | | 256 => { | 266 | 5.34k | w.write_bits(1, 1)?; // max_symbol is stored | 267 | 5.34k | w.write_bits(3, 3)?; // max_symbol_nbits / 2 - 2 | 268 | 5.34k | w.write_bits(254, 8)?; // max_symbol - 2 | 269 | | } | 270 | 1.79k | 280 => w.write_bits(0, 1)?, | 271 | 0 | _ => unreachable!(), | 272 | | } | 273 | | | 274 | | // Write the huffman codes | 275 | 7.13k | if !single_code_length_length { | 276 | 1.85M | for &len in lengths.iter() { | 277 | 1.85M | w.write_bits( | 278 | 1.85M | u64::from(code_length_codes[len as usize]), | 279 | 1.85M | code_length_lengths[len as usize], | 280 | 0 | )?; | 281 | | } | 282 | 54 | } | 283 | | | 284 | 7.13k | Ok(()) | 285 | 7.66k | } |
|
286 | | |
287 | 59.7k | const fn length_to_symbol(len: u16) -> (u16, u8) { |
288 | 59.7k | let len = len - 1; |
289 | 59.7k | let highest_bit = len.ilog2() as u16; |
290 | 59.7k | let second_highest_bit = (len >> (highest_bit - 1)) & 1; |
291 | 59.7k | let extra_bits = highest_bit - 1; |
292 | 59.7k | let symbol = 2 * highest_bit + second_highest_bit; |
293 | 59.7k | (symbol, extra_bits as u8) |
294 | 59.7k | } |
295 | | |
296 | | #[inline(always)] |
297 | 15.7M | fn count_run( |
298 | 15.7M | pixel: &[u8], |
299 | 15.7M | it: &mut std::iter::Peekable<ChunksExact<u8>>, |
300 | 15.7M | frequencies1: &mut [u32; 280], |
301 | 15.7M | ) { |
302 | 15.7M | let mut run_length = 0; |
303 | 17.1M | while run_length < 4096 && it.peek() == Some(&pixel) { |
304 | 1.45M | run_length += 1; |
305 | 1.45M | it.next(); |
306 | 1.45M | } |
307 | 15.7M | if run_length > 0 { |
308 | 105k | if run_length <= 4 { |
309 | 75.8k | let symbol = 256 + run_length - 1; |
310 | 75.8k | frequencies1[symbol] += 1; |
311 | 75.8k | } else { |
312 | 29.8k | let (symbol, _extra_bits) = length_to_symbol(run_length as u16); |
313 | 29.8k | frequencies1[256 + symbol as usize] += 1; |
314 | 29.8k | } |
315 | 15.6M | } |
316 | 15.7M | } |
317 | | |
318 | | #[inline(always)] |
319 | 15.7M | fn write_run<W: Write>( |
320 | 15.7M | w: &mut BitWriter<W>, |
321 | 15.7M | pixel: &[u8], |
322 | 15.7M | it: &mut std::iter::Peekable<ChunksExact<u8>>, |
323 | 15.7M | codes1: &[u16; 280], |
324 | 15.7M | lengths1: &[u8; 280], |
325 | 15.7M | ) -> io::Result<()> { |
326 | 15.7M | let mut run_length = 0; |
327 | 17.1M | while run_length < 4096 && it.peek() == Some(&pixel) { |
328 | 1.45M | run_length += 1; |
329 | 1.45M | it.next(); |
330 | 1.45M | } |
331 | 15.7M | if run_length > 0 { |
332 | 105k | if run_length <= 4 { |
333 | 75.8k | let symbol = 256 + run_length - 1; |
334 | 75.8k | w.write_bits(u64::from(codes1[symbol]), lengths1[symbol])?; |
335 | | } else { |
336 | 29.8k | let (symbol, extra_bits) = length_to_symbol(run_length as u16); |
337 | 29.8k | w.write_bits( |
338 | 29.8k | u64::from(codes1[256 + symbol as usize]), |
339 | 29.8k | lengths1[256 + symbol as usize], |
340 | 0 | )?; |
341 | 29.8k | w.write_bits( |
342 | 29.8k | (run_length as u64 - 1) & ((1 << extra_bits) - 1), |
343 | 29.8k | extra_bits, |
344 | 0 | )?; |
345 | | } |
346 | 15.6M | } |
347 | 15.7M | Ok(()) |
348 | 15.7M | } Unexecuted instantiation: image_webp::encoder::write_run::<_> image_webp::encoder::write_run::<&mut alloc::vec::Vec<u8>> Line | Count | Source | 319 | 15.7M | fn write_run<W: Write>( | 320 | 15.7M | w: &mut BitWriter<W>, | 321 | 15.7M | pixel: &[u8], | 322 | 15.7M | it: &mut std::iter::Peekable<ChunksExact<u8>>, | 323 | 15.7M | codes1: &[u16; 280], | 324 | 15.7M | lengths1: &[u8; 280], | 325 | 15.7M | ) -> io::Result<()> { | 326 | 15.7M | let mut run_length = 0; | 327 | 17.1M | while run_length < 4096 && it.peek() == Some(&pixel) { | 328 | 1.45M | run_length += 1; | 329 | 1.45M | it.next(); | 330 | 1.45M | } | 331 | 15.7M | if run_length > 0 { | 332 | 105k | if run_length <= 4 { | 333 | 75.8k | let symbol = 256 + run_length - 1; | 334 | 75.8k | w.write_bits(u64::from(codes1[symbol]), lengths1[symbol])?; | 335 | | } else { | 336 | 29.8k | let (symbol, extra_bits) = length_to_symbol(run_length as u16); | 337 | 29.8k | w.write_bits( | 338 | 29.8k | u64::from(codes1[256 + symbol as usize]), | 339 | 29.8k | lengths1[256 + symbol as usize], | 340 | 0 | )?; | 341 | 29.8k | w.write_bits( | 342 | 29.8k | (run_length as u64 - 1) & ((1 << extra_bits) - 1), | 343 | 29.8k | extra_bits, | 344 | 0 | )?; | 345 | | } | 346 | 15.6M | } | 347 | 15.7M | Ok(()) | 348 | 15.7M | } |
|
349 | | |
350 | | /// Allows fine-tuning some encoder parameters. |
351 | | /// |
352 | | /// Pass to [`WebPEncoder::set_params()`]. |
353 | | #[non_exhaustive] |
354 | | #[derive(Clone, Debug)] |
355 | | pub struct EncoderParams { |
356 | | /// Use a predictor transform. Enabled by default. |
357 | | pub use_predictor_transform: bool, |
358 | | } |
359 | | |
360 | | impl Default for EncoderParams { |
361 | 1.91k | fn default() -> Self { |
362 | 1.91k | Self { |
363 | 1.91k | use_predictor_transform: true, |
364 | 1.91k | } |
365 | 1.91k | } |
366 | | } |
367 | | |
368 | | /// Encode image data with the indicated color type. |
369 | | /// |
370 | | /// # Panics |
371 | | /// |
372 | | /// Panics if the image data is not of the indicated dimensions. |
373 | 1.91k | fn encode_frame<W: Write>( |
374 | 1.91k | writer: W, |
375 | 1.91k | data: &[u8], |
376 | 1.91k | width: u32, |
377 | 1.91k | height: u32, |
378 | 1.91k | color: ColorType, |
379 | 1.91k | params: EncoderParams, |
380 | 1.91k | ) -> Result<(), EncodingError> { |
381 | 1.91k | let w = &mut BitWriter { |
382 | 1.91k | writer, |
383 | 1.91k | buffer: 0, |
384 | 1.91k | nbits: 0, |
385 | 1.91k | }; |
386 | | |
387 | 1.91k | let (is_color, is_alpha, bytes_per_pixel) = match color { |
388 | 0 | ColorType::L8 => (false, false, 1), |
389 | 0 | ColorType::La8 => (false, true, 2), |
390 | 0 | ColorType::Rgb8 => (true, false, 3), |
391 | 1.91k | ColorType::Rgba8 => (true, true, 4), |
392 | | }; |
393 | | |
394 | 1.91k | assert_eq!( |
395 | 1.91k | (u64::from(width) * u64::from(height)).saturating_mul(bytes_per_pixel), |
396 | 1.91k | data.len() as u64 |
397 | | ); |
398 | | |
399 | 1.91k | if width == 0 || width > 16384 || height == 0 || height > 16384 { |
400 | 0 | return Err(EncodingError::InvalidDimensions); |
401 | 1.91k | } |
402 | | |
403 | 1.91k | w.write_bits(0x2f, 8)?; // signature |
404 | 1.91k | w.write_bits(u64::from(width) - 1, 14)?; |
405 | 1.91k | w.write_bits(u64::from(height) - 1, 14)?; |
406 | | |
407 | 1.91k | w.write_bits(u64::from(is_alpha), 1)?; // alpha used |
408 | 1.91k | w.write_bits(0x0, 3)?; // version |
409 | | |
410 | | // subtract green transform |
411 | 1.91k | w.write_bits(0b101, 3)?; |
412 | | |
413 | | // predictor transform |
414 | 1.91k | if params.use_predictor_transform { |
415 | 1.91k | w.write_bits(0b111001, 6)?; |
416 | 1.91k | w.write_bits(0x0, 1)?; // no color cache |
417 | 1.91k | write_single_entry_huffman_tree(w, 2)?; |
418 | 9.58k | for _ in 0..4 { |
419 | 7.66k | write_single_entry_huffman_tree(w, 0)?; |
420 | | } |
421 | 0 | } |
422 | | |
423 | | // transforms done |
424 | 1.91k | w.write_bits(0x0, 1)?; |
425 | | |
426 | | // color cache |
427 | 1.91k | w.write_bits(0x0, 1)?; |
428 | | |
429 | | // meta-huffman codes |
430 | 1.91k | w.write_bits(0x0, 1)?; |
431 | | |
432 | | // expand to RGBA |
433 | 1.91k | let mut pixels = match color { |
434 | 0 | ColorType::L8 => data.iter().flat_map(|&p| [p, p, p, 255]).collect(), Unexecuted instantiation: image_webp::encoder::encode_frame::<_>::{closure#0}Unexecuted instantiation: image_webp::encoder::encode_frame::<&mut alloc::vec::Vec<u8>>::{closure#0} |
435 | 0 | ColorType::La8 => data |
436 | 0 | .chunks_exact(2) |
437 | 0 | .flat_map(|p| [p[0], p[0], p[0], p[1]]) Unexecuted instantiation: image_webp::encoder::encode_frame::<_>::{closure#1}Unexecuted instantiation: image_webp::encoder::encode_frame::<&mut alloc::vec::Vec<u8>>::{closure#1} |
438 | 0 | .collect(), |
439 | 0 | ColorType::Rgb8 => data |
440 | 0 | .chunks_exact(3) |
441 | 0 | .flat_map(|p| [p[0], p[1], p[2], 255]) Unexecuted instantiation: image_webp::encoder::encode_frame::<_>::{closure#2}Unexecuted instantiation: image_webp::encoder::encode_frame::<&mut alloc::vec::Vec<u8>>::{closure#2} |
442 | 0 | .collect(), |
443 | 1.91k | ColorType::Rgba8 => data.to_vec(), |
444 | | }; |
445 | | |
446 | | // compute subtract green transform |
447 | 17.1M | for pixel in pixels.chunks_exact_mut(4) { |
448 | 17.1M | pixel[0] = pixel[0].wrapping_sub(pixel[1]); |
449 | 17.1M | pixel[2] = pixel[2].wrapping_sub(pixel[1]); |
450 | 17.1M | } |
451 | | |
452 | | // compute predictor transform |
453 | 1.91k | if params.use_predictor_transform { |
454 | 1.91k | let row_bytes = width as usize * 4; |
455 | 170k | for y in (1..height as usize).rev() { |
456 | 170k | let (prev, current) = |
457 | 170k | pixels[(y - 1) * row_bytes..][..row_bytes * 2].split_at_mut(row_bytes); |
458 | 68.1M | for (c, p) in current.iter_mut().zip(prev) { |
459 | 68.1M | *c = c.wrapping_sub(*p); |
460 | 68.1M | } |
461 | | } |
462 | 580k | for i in (4..row_bytes).rev() { |
463 | 580k | pixels[i] = pixels[i].wrapping_sub(pixels[i - 4]); |
464 | 580k | } |
465 | 1.91k | pixels[3] = pixels[3].wrapping_sub(255); |
466 | 0 | } |
467 | | |
468 | | // compute frequencies |
469 | 1.91k | let mut frequencies0 = [0u32; 256]; |
470 | 1.91k | let mut frequencies1 = [0u32; 280]; |
471 | 1.91k | let mut frequencies2 = [0u32; 256]; |
472 | 1.91k | let mut frequencies3 = [0u32; 256]; |
473 | 1.91k | let mut it = pixels.chunks_exact(4).peekable(); |
474 | 1.91k | match color { |
475 | | ColorType::L8 => { |
476 | 0 | frequencies0[0] = 1; |
477 | 0 | frequencies2[0] = 1; |
478 | 0 | frequencies3[0] = 1; |
479 | 0 | while let Some(pixel) = it.next() { |
480 | 0 | frequencies1[pixel[1] as usize] += 1; |
481 | 0 | count_run(pixel, &mut it, &mut frequencies1); |
482 | 0 | } |
483 | | } |
484 | | ColorType::La8 => { |
485 | 0 | frequencies0[0] = 1; |
486 | 0 | frequencies2[0] = 1; |
487 | 0 | while let Some(pixel) = it.next() { |
488 | 0 | frequencies1[pixel[1] as usize] += 1; |
489 | 0 | frequencies3[pixel[3] as usize] += 1; |
490 | 0 | count_run(pixel, &mut it, &mut frequencies1); |
491 | 0 | } |
492 | | } |
493 | | ColorType::Rgb8 => { |
494 | 0 | frequencies3[0] = 1; |
495 | 0 | while let Some(pixel) = it.next() { |
496 | 0 | frequencies0[pixel[0] as usize] += 1; |
497 | 0 | frequencies1[pixel[1] as usize] += 1; |
498 | 0 | frequencies2[pixel[2] as usize] += 1; |
499 | 0 | count_run(pixel, &mut it, &mut frequencies1); |
500 | 0 | } |
501 | | } |
502 | | ColorType::Rgba8 => { |
503 | 15.7M | while let Some(pixel) = it.next() { |
504 | 15.7M | frequencies0[pixel[0] as usize] += 1; |
505 | 15.7M | frequencies1[pixel[1] as usize] += 1; |
506 | 15.7M | frequencies2[pixel[2] as usize] += 1; |
507 | 15.7M | frequencies3[pixel[3] as usize] += 1; |
508 | 15.7M | count_run(pixel, &mut it, &mut frequencies1); |
509 | 15.7M | } |
510 | | } |
511 | | } |
512 | | |
513 | | // compute and write huffman codes |
514 | 1.91k | let mut lengths0 = [0u8; 256]; |
515 | 1.91k | let mut lengths1 = [0u8; 280]; |
516 | 1.91k | let mut lengths2 = [0u8; 256]; |
517 | 1.91k | let mut lengths3 = [0u8; 256]; |
518 | 1.91k | let mut codes0 = [0u16; 256]; |
519 | 1.91k | let mut codes1 = [0u16; 280]; |
520 | 1.91k | let mut codes2 = [0u16; 256]; |
521 | 1.91k | let mut codes3 = [0u16; 256]; |
522 | 1.91k | write_huffman_tree(w, &frequencies1, &mut lengths1, &mut codes1)?; |
523 | 1.91k | if is_color { |
524 | 1.91k | write_huffman_tree(w, &frequencies0, &mut lengths0, &mut codes0)?; |
525 | 1.91k | write_huffman_tree(w, &frequencies2, &mut lengths2, &mut codes2)?; |
526 | | } else { |
527 | 0 | write_single_entry_huffman_tree(w, 0)?; |
528 | 0 | write_single_entry_huffman_tree(w, 0)?; |
529 | | } |
530 | 1.91k | if is_alpha { |
531 | 1.91k | write_huffman_tree(w, &frequencies3, &mut lengths3, &mut codes3)?; |
532 | 0 | } else if params.use_predictor_transform { |
533 | 0 | write_single_entry_huffman_tree(w, 0)?; |
534 | | } else { |
535 | 0 | write_single_entry_huffman_tree(w, 255)?; |
536 | | } |
537 | 1.91k | write_single_entry_huffman_tree(w, 1)?; |
538 | | |
539 | | // Write image data |
540 | 1.91k | let mut it = pixels.chunks_exact(4).peekable(); |
541 | 1.91k | match color { |
542 | | ColorType::L8 => { |
543 | 0 | while let Some(pixel) = it.next() { |
544 | 0 | w.write_bits( |
545 | 0 | u64::from(codes1[pixel[1] as usize]), |
546 | 0 | lengths1[pixel[1] as usize], |
547 | 0 | )?; |
548 | 0 | write_run(w, pixel, &mut it, &codes1, &lengths1)?; |
549 | | } |
550 | | } |
551 | | ColorType::La8 => { |
552 | 0 | while let Some(pixel) = it.next() { |
553 | 0 | let len1 = lengths1[pixel[1] as usize]; |
554 | 0 | let len3 = lengths3[pixel[3] as usize]; |
555 | | |
556 | 0 | let code = u64::from(codes1[pixel[1] as usize]) |
557 | 0 | | (u64::from(codes3[pixel[3] as usize]) << len1); |
558 | | |
559 | 0 | w.write_bits(code, len1 + len3)?; |
560 | 0 | write_run(w, pixel, &mut it, &codes1, &lengths1)?; |
561 | | } |
562 | | } |
563 | | ColorType::Rgb8 => { |
564 | 0 | while let Some(pixel) = it.next() { |
565 | 0 | let len1 = lengths1[pixel[1] as usize]; |
566 | 0 | let len0 = lengths0[pixel[0] as usize]; |
567 | 0 | let len2 = lengths2[pixel[2] as usize]; |
568 | | |
569 | 0 | let code = u64::from(codes1[pixel[1] as usize]) |
570 | 0 | | (u64::from(codes0[pixel[0] as usize]) << len1) |
571 | 0 | | (u64::from(codes2[pixel[2] as usize]) << (len1 + len0)); |
572 | | |
573 | 0 | w.write_bits(code, len1 + len0 + len2)?; |
574 | 0 | write_run(w, pixel, &mut it, &codes1, &lengths1)?; |
575 | | } |
576 | | } |
577 | | ColorType::Rgba8 => { |
578 | 15.7M | while let Some(pixel) = it.next() { |
579 | 15.7M | let len1 = lengths1[pixel[1] as usize]; |
580 | 15.7M | let len0 = lengths0[pixel[0] as usize]; |
581 | 15.7M | let len2 = lengths2[pixel[2] as usize]; |
582 | 15.7M | let len3 = lengths3[pixel[3] as usize]; |
583 | | |
584 | 15.7M | let code = u64::from(codes1[pixel[1] as usize]) |
585 | 15.7M | | (u64::from(codes0[pixel[0] as usize]) << len1) |
586 | 15.7M | | (u64::from(codes2[pixel[2] as usize]) << (len1 + len0)) |
587 | 15.7M | | (u64::from(codes3[pixel[3] as usize]) << (len1 + len0 + len2)); |
588 | | |
589 | 15.7M | w.write_bits(code, len1 + len0 + len2 + len3)?; |
590 | 15.7M | write_run(w, pixel, &mut it, &codes1, &lengths1)?; |
591 | | } |
592 | | } |
593 | | } |
594 | | |
595 | 1.91k | w.flush()?; |
596 | 1.91k | Ok(()) |
597 | 1.91k | } Unexecuted instantiation: image_webp::encoder::encode_frame::<_> image_webp::encoder::encode_frame::<&mut alloc::vec::Vec<u8>> Line | Count | Source | 373 | 1.91k | fn encode_frame<W: Write>( | 374 | 1.91k | writer: W, | 375 | 1.91k | data: &[u8], | 376 | 1.91k | width: u32, | 377 | 1.91k | height: u32, | 378 | 1.91k | color: ColorType, | 379 | 1.91k | params: EncoderParams, | 380 | 1.91k | ) -> Result<(), EncodingError> { | 381 | 1.91k | let w = &mut BitWriter { | 382 | 1.91k | writer, | 383 | 1.91k | buffer: 0, | 384 | 1.91k | nbits: 0, | 385 | 1.91k | }; | 386 | | | 387 | 1.91k | let (is_color, is_alpha, bytes_per_pixel) = match color { | 388 | 0 | ColorType::L8 => (false, false, 1), | 389 | 0 | ColorType::La8 => (false, true, 2), | 390 | 0 | ColorType::Rgb8 => (true, false, 3), | 391 | 1.91k | ColorType::Rgba8 => (true, true, 4), | 392 | | }; | 393 | | | 394 | 1.91k | assert_eq!( | 395 | 1.91k | (u64::from(width) * u64::from(height)).saturating_mul(bytes_per_pixel), | 396 | 1.91k | data.len() as u64 | 397 | | ); | 398 | | | 399 | 1.91k | if width == 0 || width > 16384 || height == 0 || height > 16384 { | 400 | 0 | return Err(EncodingError::InvalidDimensions); | 401 | 1.91k | } | 402 | | | 403 | 1.91k | w.write_bits(0x2f, 8)?; // signature | 404 | 1.91k | w.write_bits(u64::from(width) - 1, 14)?; | 405 | 1.91k | w.write_bits(u64::from(height) - 1, 14)?; | 406 | | | 407 | 1.91k | w.write_bits(u64::from(is_alpha), 1)?; // alpha used | 408 | 1.91k | w.write_bits(0x0, 3)?; // version | 409 | | | 410 | | // subtract green transform | 411 | 1.91k | w.write_bits(0b101, 3)?; | 412 | | | 413 | | // predictor transform | 414 | 1.91k | if params.use_predictor_transform { | 415 | 1.91k | w.write_bits(0b111001, 6)?; | 416 | 1.91k | w.write_bits(0x0, 1)?; // no color cache | 417 | 1.91k | write_single_entry_huffman_tree(w, 2)?; | 418 | 9.58k | for _ in 0..4 { | 419 | 7.66k | write_single_entry_huffman_tree(w, 0)?; | 420 | | } | 421 | 0 | } | 422 | | | 423 | | // transforms done | 424 | 1.91k | w.write_bits(0x0, 1)?; | 425 | | | 426 | | // color cache | 427 | 1.91k | w.write_bits(0x0, 1)?; | 428 | | | 429 | | // meta-huffman codes | 430 | 1.91k | w.write_bits(0x0, 1)?; | 431 | | | 432 | | // expand to RGBA | 433 | 1.91k | let mut pixels = match color { | 434 | 0 | ColorType::L8 => data.iter().flat_map(|&p| [p, p, p, 255]).collect(), | 435 | 0 | ColorType::La8 => data | 436 | 0 | .chunks_exact(2) | 437 | 0 | .flat_map(|p| [p[0], p[0], p[0], p[1]]) | 438 | 0 | .collect(), | 439 | 0 | ColorType::Rgb8 => data | 440 | 0 | .chunks_exact(3) | 441 | 0 | .flat_map(|p| [p[0], p[1], p[2], 255]) | 442 | 0 | .collect(), | 443 | 1.91k | ColorType::Rgba8 => data.to_vec(), | 444 | | }; | 445 | | | 446 | | // compute subtract green transform | 447 | 17.1M | for pixel in pixels.chunks_exact_mut(4) { | 448 | 17.1M | pixel[0] = pixel[0].wrapping_sub(pixel[1]); | 449 | 17.1M | pixel[2] = pixel[2].wrapping_sub(pixel[1]); | 450 | 17.1M | } | 451 | | | 452 | | // compute predictor transform | 453 | 1.91k | if params.use_predictor_transform { | 454 | 1.91k | let row_bytes = width as usize * 4; | 455 | 170k | for y in (1..height as usize).rev() { | 456 | 170k | let (prev, current) = | 457 | 170k | pixels[(y - 1) * row_bytes..][..row_bytes * 2].split_at_mut(row_bytes); | 458 | 68.1M | for (c, p) in current.iter_mut().zip(prev) { | 459 | 68.1M | *c = c.wrapping_sub(*p); | 460 | 68.1M | } | 461 | | } | 462 | 580k | for i in (4..row_bytes).rev() { | 463 | 580k | pixels[i] = pixels[i].wrapping_sub(pixels[i - 4]); | 464 | 580k | } | 465 | 1.91k | pixels[3] = pixels[3].wrapping_sub(255); | 466 | 0 | } | 467 | | | 468 | | // compute frequencies | 469 | 1.91k | let mut frequencies0 = [0u32; 256]; | 470 | 1.91k | let mut frequencies1 = [0u32; 280]; | 471 | 1.91k | let mut frequencies2 = [0u32; 256]; | 472 | 1.91k | let mut frequencies3 = [0u32; 256]; | 473 | 1.91k | let mut it = pixels.chunks_exact(4).peekable(); | 474 | 1.91k | match color { | 475 | | ColorType::L8 => { | 476 | 0 | frequencies0[0] = 1; | 477 | 0 | frequencies2[0] = 1; | 478 | 0 | frequencies3[0] = 1; | 479 | 0 | while let Some(pixel) = it.next() { | 480 | 0 | frequencies1[pixel[1] as usize] += 1; | 481 | 0 | count_run(pixel, &mut it, &mut frequencies1); | 482 | 0 | } | 483 | | } | 484 | | ColorType::La8 => { | 485 | 0 | frequencies0[0] = 1; | 486 | 0 | frequencies2[0] = 1; | 487 | 0 | while let Some(pixel) = it.next() { | 488 | 0 | frequencies1[pixel[1] as usize] += 1; | 489 | 0 | frequencies3[pixel[3] as usize] += 1; | 490 | 0 | count_run(pixel, &mut it, &mut frequencies1); | 491 | 0 | } | 492 | | } | 493 | | ColorType::Rgb8 => { | 494 | 0 | frequencies3[0] = 1; | 495 | 0 | while let Some(pixel) = it.next() { | 496 | 0 | frequencies0[pixel[0] as usize] += 1; | 497 | 0 | frequencies1[pixel[1] as usize] += 1; | 498 | 0 | frequencies2[pixel[2] as usize] += 1; | 499 | 0 | count_run(pixel, &mut it, &mut frequencies1); | 500 | 0 | } | 501 | | } | 502 | | ColorType::Rgba8 => { | 503 | 15.7M | while let Some(pixel) = it.next() { | 504 | 15.7M | frequencies0[pixel[0] as usize] += 1; | 505 | 15.7M | frequencies1[pixel[1] as usize] += 1; | 506 | 15.7M | frequencies2[pixel[2] as usize] += 1; | 507 | 15.7M | frequencies3[pixel[3] as usize] += 1; | 508 | 15.7M | count_run(pixel, &mut it, &mut frequencies1); | 509 | 15.7M | } | 510 | | } | 511 | | } | 512 | | | 513 | | // compute and write huffman codes | 514 | 1.91k | let mut lengths0 = [0u8; 256]; | 515 | 1.91k | let mut lengths1 = [0u8; 280]; | 516 | 1.91k | let mut lengths2 = [0u8; 256]; | 517 | 1.91k | let mut lengths3 = [0u8; 256]; | 518 | 1.91k | let mut codes0 = [0u16; 256]; | 519 | 1.91k | let mut codes1 = [0u16; 280]; | 520 | 1.91k | let mut codes2 = [0u16; 256]; | 521 | 1.91k | let mut codes3 = [0u16; 256]; | 522 | 1.91k | write_huffman_tree(w, &frequencies1, &mut lengths1, &mut codes1)?; | 523 | 1.91k | if is_color { | 524 | 1.91k | write_huffman_tree(w, &frequencies0, &mut lengths0, &mut codes0)?; | 525 | 1.91k | write_huffman_tree(w, &frequencies2, &mut lengths2, &mut codes2)?; | 526 | | } else { | 527 | 0 | write_single_entry_huffman_tree(w, 0)?; | 528 | 0 | write_single_entry_huffman_tree(w, 0)?; | 529 | | } | 530 | 1.91k | if is_alpha { | 531 | 1.91k | write_huffman_tree(w, &frequencies3, &mut lengths3, &mut codes3)?; | 532 | 0 | } else if params.use_predictor_transform { | 533 | 0 | write_single_entry_huffman_tree(w, 0)?; | 534 | | } else { | 535 | 0 | write_single_entry_huffman_tree(w, 255)?; | 536 | | } | 537 | 1.91k | write_single_entry_huffman_tree(w, 1)?; | 538 | | | 539 | | // Write image data | 540 | 1.91k | let mut it = pixels.chunks_exact(4).peekable(); | 541 | 1.91k | match color { | 542 | | ColorType::L8 => { | 543 | 0 | while let Some(pixel) = it.next() { | 544 | 0 | w.write_bits( | 545 | 0 | u64::from(codes1[pixel[1] as usize]), | 546 | 0 | lengths1[pixel[1] as usize], | 547 | 0 | )?; | 548 | 0 | write_run(w, pixel, &mut it, &codes1, &lengths1)?; | 549 | | } | 550 | | } | 551 | | ColorType::La8 => { | 552 | 0 | while let Some(pixel) = it.next() { | 553 | 0 | let len1 = lengths1[pixel[1] as usize]; | 554 | 0 | let len3 = lengths3[pixel[3] as usize]; | 555 | | | 556 | 0 | let code = u64::from(codes1[pixel[1] as usize]) | 557 | 0 | | (u64::from(codes3[pixel[3] as usize]) << len1); | 558 | | | 559 | 0 | w.write_bits(code, len1 + len3)?; | 560 | 0 | write_run(w, pixel, &mut it, &codes1, &lengths1)?; | 561 | | } | 562 | | } | 563 | | ColorType::Rgb8 => { | 564 | 0 | while let Some(pixel) = it.next() { | 565 | 0 | let len1 = lengths1[pixel[1] as usize]; | 566 | 0 | let len0 = lengths0[pixel[0] as usize]; | 567 | 0 | let len2 = lengths2[pixel[2] as usize]; | 568 | | | 569 | 0 | let code = u64::from(codes1[pixel[1] as usize]) | 570 | 0 | | (u64::from(codes0[pixel[0] as usize]) << len1) | 571 | 0 | | (u64::from(codes2[pixel[2] as usize]) << (len1 + len0)); | 572 | | | 573 | 0 | w.write_bits(code, len1 + len0 + len2)?; | 574 | 0 | write_run(w, pixel, &mut it, &codes1, &lengths1)?; | 575 | | } | 576 | | } | 577 | | ColorType::Rgba8 => { | 578 | 15.7M | while let Some(pixel) = it.next() { | 579 | 15.7M | let len1 = lengths1[pixel[1] as usize]; | 580 | 15.7M | let len0 = lengths0[pixel[0] as usize]; | 581 | 15.7M | let len2 = lengths2[pixel[2] as usize]; | 582 | 15.7M | let len3 = lengths3[pixel[3] as usize]; | 583 | | | 584 | 15.7M | let code = u64::from(codes1[pixel[1] as usize]) | 585 | 15.7M | | (u64::from(codes0[pixel[0] as usize]) << len1) | 586 | 15.7M | | (u64::from(codes2[pixel[2] as usize]) << (len1 + len0)) | 587 | 15.7M | | (u64::from(codes3[pixel[3] as usize]) << (len1 + len0 + len2)); | 588 | | | 589 | 15.7M | w.write_bits(code, len1 + len0 + len2 + len3)?; | 590 | 15.7M | write_run(w, pixel, &mut it, &codes1, &lengths1)?; | 591 | | } | 592 | | } | 593 | | } | 594 | | | 595 | 1.91k | w.flush()?; | 596 | 1.91k | Ok(()) | 597 | 1.91k | } |
|
598 | | |
599 | 1.91k | const fn chunk_size(inner_bytes: usize) -> u32 { |
600 | 1.91k | if inner_bytes % 2 == 1 { |
601 | 966 | (inner_bytes + 1) as u32 + 8 |
602 | | } else { |
603 | 950 | inner_bytes as u32 + 8 |
604 | | } |
605 | 1.91k | } |
606 | | |
607 | 1.91k | fn write_chunk<W: Write>(mut w: W, name: &[u8], data: &[u8]) -> io::Result<()> { |
608 | 1.91k | debug_assert!(name.len() == 4); |
609 | | |
610 | 1.91k | w.write_all(name)?; |
611 | 1.91k | w.write_all(&(data.len() as u32).to_le_bytes())?; |
612 | 1.91k | w.write_all(data)?; |
613 | 1.91k | if data.len() % 2 == 1 { |
614 | 966 | w.write_all(&[0])?; |
615 | 950 | } |
616 | 1.91k | Ok(()) |
617 | 1.91k | } Unexecuted instantiation: image_webp::encoder::write_chunk::<_> image_webp::encoder::write_chunk::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> Line | Count | Source | 607 | 1.91k | fn write_chunk<W: Write>(mut w: W, name: &[u8], data: &[u8]) -> io::Result<()> { | 608 | 1.91k | debug_assert!(name.len() == 4); | 609 | | | 610 | 1.91k | w.write_all(name)?; | 611 | 1.91k | w.write_all(&(data.len() as u32).to_le_bytes())?; | 612 | 1.91k | w.write_all(data)?; | 613 | 1.91k | if data.len() % 2 == 1 { | 614 | 966 | w.write_all(&[0])?; | 615 | 950 | } | 616 | 1.91k | Ok(()) | 617 | 1.91k | } |
|
618 | | |
619 | | /// WebP Encoder. |
620 | | pub struct WebPEncoder<W> { |
621 | | writer: W, |
622 | | icc_profile: Vec<u8>, |
623 | | exif_metadata: Vec<u8>, |
624 | | xmp_metadata: Vec<u8>, |
625 | | params: EncoderParams, |
626 | | } |
627 | | |
628 | | impl<W: Write> WebPEncoder<W> { |
629 | | /// Create a new encoder that writes its output to `w`. |
630 | | /// |
631 | | /// Only supports "VP8L" lossless encoding. |
632 | 1.91k | pub fn new(w: W) -> Self { |
633 | 1.91k | Self { |
634 | 1.91k | writer: w, |
635 | 1.91k | icc_profile: Vec::new(), |
636 | 1.91k | exif_metadata: Vec::new(), |
637 | 1.91k | xmp_metadata: Vec::new(), |
638 | 1.91k | params: EncoderParams::default(), |
639 | 1.91k | } |
640 | 1.91k | } Unexecuted instantiation: <image_webp::encoder::WebPEncoder<_>>::new <image_webp::encoder::WebPEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::new Line | Count | Source | 632 | 1.91k | pub fn new(w: W) -> Self { | 633 | 1.91k | Self { | 634 | 1.91k | writer: w, | 635 | 1.91k | icc_profile: Vec::new(), | 636 | 1.91k | exif_metadata: Vec::new(), | 637 | 1.91k | xmp_metadata: Vec::new(), | 638 | 1.91k | params: EncoderParams::default(), | 639 | 1.91k | } | 640 | 1.91k | } |
|
641 | | |
642 | | /// Set the ICC profile to use for the image. |
643 | 0 | pub fn set_icc_profile(&mut self, icc_profile: Vec<u8>) { |
644 | 0 | self.icc_profile = icc_profile; |
645 | 0 | } Unexecuted instantiation: <image_webp::encoder::WebPEncoder<_>>::set_icc_profile Unexecuted instantiation: <image_webp::encoder::WebPEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::set_icc_profile |
646 | | |
647 | | /// Set the EXIF metadata to use for the image. |
648 | 0 | pub fn set_exif_metadata(&mut self, exif_metadata: Vec<u8>) { |
649 | 0 | self.exif_metadata = exif_metadata; |
650 | 0 | } Unexecuted instantiation: <image_webp::encoder::WebPEncoder<_>>::set_exif_metadata Unexecuted instantiation: <image_webp::encoder::WebPEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::set_exif_metadata |
651 | | |
652 | | /// Set the XMP metadata to use for the image. |
653 | 0 | pub fn set_xmp_metadata(&mut self, xmp_metadata: Vec<u8>) { |
654 | 0 | self.xmp_metadata = xmp_metadata; |
655 | 0 | } |
656 | | |
657 | | /// Set the `EncoderParams` to use. |
658 | 0 | pub fn set_params(&mut self, params: EncoderParams) { |
659 | 0 | self.params = params; |
660 | 0 | } |
661 | | |
662 | | /// Encode image data with the indicated color type. |
663 | | /// |
664 | | /// # Panics |
665 | | /// |
666 | | /// Panics if the image data is not of the indicated dimensions. |
667 | 1.91k | pub fn encode( |
668 | 1.91k | mut self, |
669 | 1.91k | data: &[u8], |
670 | 1.91k | width: u32, |
671 | 1.91k | height: u32, |
672 | 1.91k | color: ColorType, |
673 | 1.91k | ) -> Result<(), EncodingError> { |
674 | 1.91k | let mut frame = Vec::new(); |
675 | 1.91k | encode_frame(&mut frame, data, width, height, color, self.params)?; |
676 | | |
677 | | // If the image has no metadata, it can be encoded with the "simple" WebP container format. |
678 | 1.91k | if self.icc_profile.is_empty() |
679 | 1.91k | && self.exif_metadata.is_empty() |
680 | 1.91k | && self.xmp_metadata.is_empty() |
681 | | { |
682 | 1.91k | self.writer.write_all(b"RIFF")?; |
683 | 1.91k | self.writer |
684 | 1.91k | .write_all(&(chunk_size(frame.len()) + 4).to_le_bytes())?; |
685 | 1.91k | self.writer.write_all(b"WEBP")?; |
686 | 1.91k | write_chunk(&mut self.writer, b"VP8L", &frame)?; |
687 | | } else { |
688 | 0 | let mut total_bytes = 22 + chunk_size(frame.len()); |
689 | 0 | if !self.icc_profile.is_empty() { |
690 | 0 | total_bytes += chunk_size(self.icc_profile.len()); |
691 | 0 | } |
692 | 0 | if !self.exif_metadata.is_empty() { |
693 | 0 | total_bytes += chunk_size(self.exif_metadata.len()); |
694 | 0 | } |
695 | 0 | if !self.xmp_metadata.is_empty() { |
696 | 0 | total_bytes += chunk_size(self.xmp_metadata.len()); |
697 | 0 | } |
698 | | |
699 | 0 | let mut flags = 0; |
700 | 0 | if !self.xmp_metadata.is_empty() { |
701 | 0 | flags |= 1 << 2; |
702 | 0 | } |
703 | 0 | if !self.exif_metadata.is_empty() { |
704 | 0 | flags |= 1 << 3; |
705 | 0 | } |
706 | 0 | if let ColorType::La8 | ColorType::Rgba8 = color { |
707 | 0 | flags |= 1 << 4; |
708 | 0 | } |
709 | 0 | if !self.icc_profile.is_empty() { |
710 | 0 | flags |= 1 << 5; |
711 | 0 | } |
712 | | |
713 | 0 | self.writer.write_all(b"RIFF")?; |
714 | 0 | self.writer.write_all(&total_bytes.to_le_bytes())?; |
715 | 0 | self.writer.write_all(b"WEBP")?; |
716 | | |
717 | 0 | let mut vp8x = Vec::new(); |
718 | 0 | vp8x.write_all(&[flags])?; // flags |
719 | 0 | vp8x.write_all(&[0; 3])?; // reserved |
720 | 0 | vp8x.write_all(&(width - 1).to_le_bytes()[..3])?; // canvas width |
721 | 0 | vp8x.write_all(&(height - 1).to_le_bytes()[..3])?; // canvas height |
722 | 0 | write_chunk(&mut self.writer, b"VP8X", &vp8x)?; |
723 | | |
724 | 0 | if !self.icc_profile.is_empty() { |
725 | 0 | write_chunk(&mut self.writer, b"ICCP", &self.icc_profile)?; |
726 | 0 | } |
727 | | |
728 | 0 | write_chunk(&mut self.writer, b"VP8L", &frame)?; |
729 | | |
730 | 0 | if !self.exif_metadata.is_empty() { |
731 | 0 | write_chunk(&mut self.writer, b"EXIF", &self.exif_metadata)?; |
732 | 0 | } |
733 | | |
734 | 0 | if !self.xmp_metadata.is_empty() { |
735 | 0 | write_chunk(&mut self.writer, b"XMP ", &self.xmp_metadata)?; |
736 | 0 | } |
737 | | } |
738 | | |
739 | 1.91k | Ok(()) |
740 | 1.91k | } Unexecuted instantiation: <image_webp::encoder::WebPEncoder<_>>::encode <image_webp::encoder::WebPEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode Line | Count | Source | 667 | 1.91k | pub fn encode( | 668 | 1.91k | mut self, | 669 | 1.91k | data: &[u8], | 670 | 1.91k | width: u32, | 671 | 1.91k | height: u32, | 672 | 1.91k | color: ColorType, | 673 | 1.91k | ) -> Result<(), EncodingError> { | 674 | 1.91k | let mut frame = Vec::new(); | 675 | 1.91k | encode_frame(&mut frame, data, width, height, color, self.params)?; | 676 | | | 677 | | // If the image has no metadata, it can be encoded with the "simple" WebP container format. | 678 | 1.91k | if self.icc_profile.is_empty() | 679 | 1.91k | && self.exif_metadata.is_empty() | 680 | 1.91k | && self.xmp_metadata.is_empty() | 681 | | { | 682 | 1.91k | self.writer.write_all(b"RIFF")?; | 683 | 1.91k | self.writer | 684 | 1.91k | .write_all(&(chunk_size(frame.len()) + 4).to_le_bytes())?; | 685 | 1.91k | self.writer.write_all(b"WEBP")?; | 686 | 1.91k | write_chunk(&mut self.writer, b"VP8L", &frame)?; | 687 | | } else { | 688 | 0 | let mut total_bytes = 22 + chunk_size(frame.len()); | 689 | 0 | if !self.icc_profile.is_empty() { | 690 | 0 | total_bytes += chunk_size(self.icc_profile.len()); | 691 | 0 | } | 692 | 0 | if !self.exif_metadata.is_empty() { | 693 | 0 | total_bytes += chunk_size(self.exif_metadata.len()); | 694 | 0 | } | 695 | 0 | if !self.xmp_metadata.is_empty() { | 696 | 0 | total_bytes += chunk_size(self.xmp_metadata.len()); | 697 | 0 | } | 698 | | | 699 | 0 | let mut flags = 0; | 700 | 0 | if !self.xmp_metadata.is_empty() { | 701 | 0 | flags |= 1 << 2; | 702 | 0 | } | 703 | 0 | if !self.exif_metadata.is_empty() { | 704 | 0 | flags |= 1 << 3; | 705 | 0 | } | 706 | 0 | if let ColorType::La8 | ColorType::Rgba8 = color { | 707 | 0 | flags |= 1 << 4; | 708 | 0 | } | 709 | 0 | if !self.icc_profile.is_empty() { | 710 | 0 | flags |= 1 << 5; | 711 | 0 | } | 712 | | | 713 | 0 | self.writer.write_all(b"RIFF")?; | 714 | 0 | self.writer.write_all(&total_bytes.to_le_bytes())?; | 715 | 0 | self.writer.write_all(b"WEBP")?; | 716 | | | 717 | 0 | let mut vp8x = Vec::new(); | 718 | 0 | vp8x.write_all(&[flags])?; // flags | 719 | 0 | vp8x.write_all(&[0; 3])?; // reserved | 720 | 0 | vp8x.write_all(&(width - 1).to_le_bytes()[..3])?; // canvas width | 721 | 0 | vp8x.write_all(&(height - 1).to_le_bytes()[..3])?; // canvas height | 722 | 0 | write_chunk(&mut self.writer, b"VP8X", &vp8x)?; | 723 | | | 724 | 0 | if !self.icc_profile.is_empty() { | 725 | 0 | write_chunk(&mut self.writer, b"ICCP", &self.icc_profile)?; | 726 | 0 | } | 727 | | | 728 | 0 | write_chunk(&mut self.writer, b"VP8L", &frame)?; | 729 | | | 730 | 0 | if !self.exif_metadata.is_empty() { | 731 | 0 | write_chunk(&mut self.writer, b"EXIF", &self.exif_metadata)?; | 732 | 0 | } | 733 | | | 734 | 0 | if !self.xmp_metadata.is_empty() { | 735 | 0 | write_chunk(&mut self.writer, b"XMP ", &self.xmp_metadata)?; | 736 | 0 | } | 737 | | } | 738 | | | 739 | 1.91k | Ok(()) | 740 | 1.91k | } |
|
741 | | } |
742 | | |
743 | | #[cfg(test)] |
744 | | mod tests { |
745 | | use rand::RngCore; |
746 | | |
747 | | use super::*; |
748 | | |
749 | | #[test] |
750 | | fn write_webp() { |
751 | | let mut img = vec![0; 256 * 256 * 4]; |
752 | | rand::thread_rng().fill_bytes(&mut img); |
753 | | |
754 | | let mut output = Vec::new(); |
755 | | WebPEncoder::new(&mut output) |
756 | | .encode(&img, 256, 256, crate::ColorType::Rgba8) |
757 | | .unwrap(); |
758 | | |
759 | | let mut decoder = crate::WebPDecoder::new(std::io::Cursor::new(output)).unwrap(); |
760 | | let mut img2 = vec![0; 256 * 256 * 4]; |
761 | | decoder.read_image(&mut img2).unwrap(); |
762 | | assert_eq!(img, img2); |
763 | | } |
764 | | |
765 | | #[test] |
766 | | fn write_webp_exif() { |
767 | | let mut img = vec![0; 256 * 256 * 3]; |
768 | | rand::thread_rng().fill_bytes(&mut img); |
769 | | |
770 | | let mut exif = vec![0; 10]; |
771 | | rand::thread_rng().fill_bytes(&mut exif); |
772 | | |
773 | | let mut output = Vec::new(); |
774 | | let mut encoder = WebPEncoder::new(&mut output); |
775 | | encoder.set_exif_metadata(exif.clone()); |
776 | | encoder |
777 | | .encode(&img, 256, 256, crate::ColorType::Rgb8) |
778 | | .unwrap(); |
779 | | |
780 | | let mut decoder = crate::WebPDecoder::new(std::io::Cursor::new(output)).unwrap(); |
781 | | |
782 | | let mut img2 = vec![0; 256 * 256 * 3]; |
783 | | decoder.read_image(&mut img2).unwrap(); |
784 | | assert_eq!(img, img2); |
785 | | |
786 | | let exif2 = decoder.exif_metadata().unwrap(); |
787 | | assert_eq!(Some(exif), exif2); |
788 | | } |
789 | | |
790 | | #[test] |
791 | | fn roundtrip_libwebp() { |
792 | | roundtrip_libwebp_params(EncoderParams::default()); |
793 | | roundtrip_libwebp_params(EncoderParams { |
794 | | use_predictor_transform: false, |
795 | | ..Default::default() |
796 | | }); |
797 | | } |
798 | | |
799 | | fn roundtrip_libwebp_params(params: EncoderParams) { |
800 | | println!("Testing {params:?}"); |
801 | | |
802 | | let mut img = vec![0; 256 * 256 * 4]; |
803 | | rand::thread_rng().fill_bytes(&mut img); |
804 | | |
805 | | let mut output = Vec::new(); |
806 | | let mut encoder = WebPEncoder::new(&mut output); |
807 | | encoder.set_params(params.clone()); |
808 | | encoder |
809 | | .encode(&img[..256 * 256 * 3], 256, 256, crate::ColorType::Rgb8) |
810 | | .unwrap(); |
811 | | let decoded = webp::Decoder::new(&output).decode().unwrap(); |
812 | | assert_eq!(img[..256 * 256 * 3], *decoded); |
813 | | |
814 | | let mut output = Vec::new(); |
815 | | let mut encoder = WebPEncoder::new(&mut output); |
816 | | encoder.set_params(params.clone()); |
817 | | encoder |
818 | | .encode(&img, 256, 256, crate::ColorType::Rgba8) |
819 | | .unwrap(); |
820 | | let decoded = webp::Decoder::new(&output).decode().unwrap(); |
821 | | assert_eq!(img, *decoded); |
822 | | |
823 | | let mut output = Vec::new(); |
824 | | let mut encoder = WebPEncoder::new(&mut output); |
825 | | encoder.set_params(params.clone()); |
826 | | encoder.set_icc_profile(vec![0; 10]); |
827 | | encoder |
828 | | .encode(&img, 256, 256, crate::ColorType::Rgba8) |
829 | | .unwrap(); |
830 | | let decoded = webp::Decoder::new(&output).decode().unwrap(); |
831 | | assert_eq!(img, *decoded); |
832 | | |
833 | | let mut output = Vec::new(); |
834 | | let mut encoder = WebPEncoder::new(&mut output); |
835 | | encoder.set_params(params.clone()); |
836 | | encoder.set_exif_metadata(vec![0; 10]); |
837 | | encoder |
838 | | .encode(&img, 256, 256, crate::ColorType::Rgba8) |
839 | | .unwrap(); |
840 | | let decoded = webp::Decoder::new(&output).decode().unwrap(); |
841 | | assert_eq!(img, *decoded); |
842 | | |
843 | | let mut output = Vec::new(); |
844 | | let mut encoder = WebPEncoder::new(&mut output); |
845 | | encoder.set_params(params); |
846 | | encoder.set_xmp_metadata(vec![0; 7]); |
847 | | encoder.set_icc_profile(vec![0; 8]); |
848 | | encoder.set_icc_profile(vec![0; 9]); |
849 | | encoder |
850 | | .encode(&img, 256, 256, crate::ColorType::Rgba8) |
851 | | .unwrap(); |
852 | | let decoded = webp::Decoder::new(&output).decode().unwrap(); |
853 | | assert_eq!(img, *decoded); |
854 | | } |
855 | | } |