Coverage Report

Created: 2026-02-26 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}