Skip to main content

image_webp/lossless/encoder/
mod.rs

1use std::io::{self, Write};
2use std::slice::ChunksExact;
3
4use crate::{ColorType, EncodingError};
5use huffman::{write_huffman_tree, write_single_entry_huffman_tree};
6
7mod huffman;
8
9struct BitWriter<W> {
10    writer: W,
11    buffer: u64,
12    nbits: u8,
13}
14
15impl<W: Write> BitWriter<W> {
16    #[inline(always)]
17    fn write_bits(&mut self, bits: u64, nbits: u8) -> io::Result<()> {
18        debug_assert!(nbits <= 64);
19
20        self.buffer |= bits << self.nbits;
21        self.nbits += nbits;
22
23        if self.nbits >= 64 {
24            self.write_bits_cold(bits, nbits)?;
25        }
26        debug_assert!(self.nbits < 64);
27        Ok(())
28    }
29
30    #[inline(never)]
31    fn write_bits_cold(&mut self, bits: u64, nbits: u8) -> io::Result<()> {
32        self.writer.write_all(&self.buffer.to_le_bytes())?;
33        self.nbits -= 64;
34        self.buffer = bits.checked_shr(u32::from(nbits - self.nbits)).unwrap_or(0);
35        Ok(())
36    }
37
38    fn flush(&mut self) -> io::Result<()> {
39        if self.nbits % 8 != 0 {
40            self.write_bits(0, 8 - self.nbits % 8)?;
41        }
42        if self.nbits > 0 {
43            self.writer
44                .write_all(&self.buffer.to_le_bytes()[..self.nbits as usize / 8])
45                .unwrap();
46            self.buffer = 0;
47            self.nbits = 0;
48        }
49        Ok(())
50    }
51}
52
53const fn length_to_symbol(len: u16) -> (u16, u8) {
54    let len = len - 1;
55    let highest_bit = len.ilog2() as u16;
56    let second_highest_bit = (len >> (highest_bit - 1)) & 1;
57    let extra_bits = highest_bit - 1;
58    let symbol = 2 * highest_bit + second_highest_bit;
59    (symbol, extra_bits as u8)
60}
61
62#[inline(always)]
63fn count_run(
64    pixel: &[u8],
65    it: &mut std::iter::Peekable<ChunksExact<u8>>,
66    frequencies1: &mut [u32; 280],
67) {
68    let mut run_length = 0;
69    while run_length < 4096 && it.peek() == Some(&pixel) {
70        run_length += 1;
71        it.next();
72    }
73    if run_length > 0 {
74        if run_length <= 4 {
75            let symbol = 256 + run_length - 1;
76            frequencies1[symbol] += 1;
77        } else {
78            let (symbol, _extra_bits) = length_to_symbol(run_length as u16);
79            frequencies1[256 + symbol as usize] += 1;
80        }
81    }
82}
83
84#[inline(always)]
85fn write_run<W: Write>(
86    w: &mut BitWriter<W>,
87    pixel: &[u8],
88    it: &mut std::iter::Peekable<ChunksExact<u8>>,
89    codes1: &[u16; 280],
90    lengths1: &[u8; 280],
91) -> io::Result<()> {
92    let mut run_length = 0;
93    while run_length < 4096 && it.peek() == Some(&pixel) {
94        run_length += 1;
95        it.next();
96    }
97    if run_length > 0 {
98        if run_length <= 4 {
99            let symbol = 256 + run_length - 1;
100            w.write_bits(u64::from(codes1[symbol]), lengths1[symbol])?;
101        } else {
102            let (symbol, extra_bits) = length_to_symbol(run_length as u16);
103            w.write_bits(
104                u64::from(codes1[256 + symbol as usize]),
105                lengths1[256 + symbol as usize],
106            )?;
107            w.write_bits(
108                (run_length as u64 - 1) & ((1 << extra_bits) - 1),
109                extra_bits,
110            )?;
111        }
112    }
113    Ok(())
114}
115
116/// Allows fine-tuning some encoder parameters.
117///
118/// Pass to [`WebPEncoder::set_params()`].
119#[non_exhaustive]
120#[derive(Clone, Debug)]
121pub struct EncoderParams {
122    /// Use a predictor transform. Enabled by default.
123    pub use_predictor_transform: bool,
124    /// Use the lossy encoding to encode the image using the VP8 compression format.
125    pub use_lossy: bool,
126    /// A quality value for the lossy encoding that must be between 0 and 100. Defaults to 95.
127    pub lossy_quality: u8,
128}
129
130impl Default for EncoderParams {
131    fn default() -> Self {
132        Self {
133            use_predictor_transform: true,
134            use_lossy: false,
135            lossy_quality: 95,
136        }
137    }
138}
139
140/// Encode image data losslessly with the indicated color type.
141///
142/// # Panics
143///
144/// Panics if the image data is not of the indicated dimensions.
145pub(crate) fn encode_frame_lossless<W: Write>(
146    writer: W,
147    data: &[u8],
148    width: u32,
149    height: u32,
150    color: ColorType,
151    params: EncoderParams,
152    implicit_dimensions: bool,
153) -> Result<(), EncodingError> {
154    let w = &mut BitWriter {
155        writer,
156        buffer: 0,
157        nbits: 0,
158    };
159
160    let (is_color, is_alpha, bytes_per_pixel) = match color {
161        ColorType::L8 => (false, false, 1),
162        ColorType::La8 => (false, true, 2),
163        ColorType::Rgb8 => (true, false, 3),
164        ColorType::Rgba8 => (true, true, 4),
165    };
166
167    assert_eq!(
168        (u64::from(width) * u64::from(height)).saturating_mul(bytes_per_pixel),
169        data.len() as u64
170    );
171
172    if width == 0 || width > 16384 || height == 0 || height > 16384 {
173        return Err(EncodingError::InvalidDimensions);
174    }
175
176    if !implicit_dimensions {
177        w.write_bits(0x2f, 8)?; // signature
178        w.write_bits(u64::from(width) - 1, 14)?;
179        w.write_bits(u64::from(height) - 1, 14)?;
180
181        w.write_bits(u64::from(is_alpha), 1)?; // alpha used
182        w.write_bits(0x0, 3)?; // version
183    }
184    // subtract green transform
185    w.write_bits(0b101, 3)?;
186
187    // predictor transform
188    if params.use_predictor_transform {
189        w.write_bits(0b111001, 6)?;
190        w.write_bits(0x0, 1)?; // no color cache
191        write_single_entry_huffman_tree(w, 2)?;
192        for _ in 0..4 {
193            write_single_entry_huffman_tree(w, 0)?;
194        }
195    }
196
197    // transforms done
198    w.write_bits(0x0, 1)?;
199
200    // color cache
201    w.write_bits(0x0, 1)?;
202
203    // meta-huffman codes
204    w.write_bits(0x0, 1)?;
205
206    // expand to RGBA
207    let mut pixels = match color {
208        ColorType::L8 => data.iter().flat_map(|&p| [p, p, p, 255]).collect(),
209        ColorType::La8 => data
210            .chunks_exact(2)
211            .flat_map(|p| [p[0], p[0], p[0], p[1]])
212            .collect(),
213        ColorType::Rgb8 => data
214            .chunks_exact(3)
215            .flat_map(|p| [p[0], p[1], p[2], 255])
216            .collect(),
217        ColorType::Rgba8 => data.to_vec(),
218    };
219
220    // compute subtract green transform
221    for pixel in pixels.chunks_exact_mut(4) {
222        pixel[0] = pixel[0].wrapping_sub(pixel[1]);
223        pixel[2] = pixel[2].wrapping_sub(pixel[1]);
224    }
225
226    // compute predictor transform
227    if params.use_predictor_transform {
228        let row_bytes = width as usize * 4;
229        for y in (1..height as usize).rev() {
230            let (prev, current) =
231                pixels[(y - 1) * row_bytes..][..row_bytes * 2].split_at_mut(row_bytes);
232            for (c, p) in current.iter_mut().zip(prev) {
233                *c = c.wrapping_sub(*p);
234            }
235        }
236        for i in (4..row_bytes).rev() {
237            pixels[i] = pixels[i].wrapping_sub(pixels[i - 4]);
238        }
239        pixels[3] = pixels[3].wrapping_sub(255);
240    }
241
242    // compute frequencies
243    let mut frequencies0 = [0u32; 256];
244    let mut frequencies1 = [0u32; 280];
245    let mut frequencies2 = [0u32; 256];
246    let mut frequencies3 = [0u32; 256];
247    let mut it = pixels.chunks_exact(4).peekable();
248    match color {
249        ColorType::L8 => {
250            frequencies0[0] = 1;
251            frequencies2[0] = 1;
252            frequencies3[0] = 1;
253            while let Some(pixel) = it.next() {
254                frequencies1[pixel[1] as usize] += 1;
255                count_run(pixel, &mut it, &mut frequencies1);
256            }
257        }
258        ColorType::La8 => {
259            frequencies0[0] = 1;
260            frequencies2[0] = 1;
261            while let Some(pixel) = it.next() {
262                frequencies1[pixel[1] as usize] += 1;
263                frequencies3[pixel[3] as usize] += 1;
264                count_run(pixel, &mut it, &mut frequencies1);
265            }
266        }
267        ColorType::Rgb8 => {
268            frequencies3[0] = 1;
269            while let Some(pixel) = it.next() {
270                frequencies0[pixel[0] as usize] += 1;
271                frequencies1[pixel[1] as usize] += 1;
272                frequencies2[pixel[2] as usize] += 1;
273                count_run(pixel, &mut it, &mut frequencies1);
274            }
275        }
276        ColorType::Rgba8 => {
277            while let Some(pixel) = it.next() {
278                frequencies0[pixel[0] as usize] += 1;
279                frequencies1[pixel[1] as usize] += 1;
280                frequencies2[pixel[2] as usize] += 1;
281                frequencies3[pixel[3] as usize] += 1;
282                count_run(pixel, &mut it, &mut frequencies1);
283            }
284        }
285    }
286
287    // compute and write huffman codes
288    let mut lengths0 = [0u8; 256];
289    let mut lengths1 = [0u8; 280];
290    let mut lengths2 = [0u8; 256];
291    let mut lengths3 = [0u8; 256];
292    let mut codes0 = [0u16; 256];
293    let mut codes1 = [0u16; 280];
294    let mut codes2 = [0u16; 256];
295    let mut codes3 = [0u16; 256];
296    write_huffman_tree(w, &frequencies1, &mut lengths1, &mut codes1)?;
297    if is_color {
298        write_huffman_tree(w, &frequencies0, &mut lengths0, &mut codes0)?;
299        write_huffman_tree(w, &frequencies2, &mut lengths2, &mut codes2)?;
300    } else {
301        write_single_entry_huffman_tree(w, 0)?;
302        write_single_entry_huffman_tree(w, 0)?;
303    }
304    if is_alpha {
305        write_huffman_tree(w, &frequencies3, &mut lengths3, &mut codes3)?;
306    } else if params.use_predictor_transform {
307        write_single_entry_huffman_tree(w, 0)?;
308    } else {
309        write_single_entry_huffman_tree(w, 255)?;
310    }
311    write_single_entry_huffman_tree(w, 1)?;
312
313    // Write image data
314    let mut it = pixels.chunks_exact(4).peekable();
315    match color {
316        ColorType::L8 => {
317            while let Some(pixel) = it.next() {
318                w.write_bits(
319                    u64::from(codes1[pixel[1] as usize]),
320                    lengths1[pixel[1] as usize],
321                )?;
322                write_run(w, pixel, &mut it, &codes1, &lengths1)?;
323            }
324        }
325        ColorType::La8 => {
326            while let Some(pixel) = it.next() {
327                let len1 = lengths1[pixel[1] as usize];
328                let len3 = lengths3[pixel[3] as usize];
329
330                let code = u64::from(codes1[pixel[1] as usize])
331                    | (u64::from(codes3[pixel[3] as usize]) << len1);
332
333                w.write_bits(code, len1 + len3)?;
334                write_run(w, pixel, &mut it, &codes1, &lengths1)?;
335            }
336        }
337        ColorType::Rgb8 => {
338            while let Some(pixel) = it.next() {
339                let len1 = lengths1[pixel[1] as usize];
340                let len0 = lengths0[pixel[0] as usize];
341                let len2 = lengths2[pixel[2] as usize];
342
343                let code = u64::from(codes1[pixel[1] as usize])
344                    | (u64::from(codes0[pixel[0] as usize]) << len1)
345                    | (u64::from(codes2[pixel[2] as usize]) << (len1 + len0));
346
347                w.write_bits(code, len1 + len0 + len2)?;
348                write_run(w, pixel, &mut it, &codes1, &lengths1)?;
349            }
350        }
351        ColorType::Rgba8 => {
352            while let Some(pixel) = it.next() {
353                let len1 = lengths1[pixel[1] as usize];
354                let len0 = lengths0[pixel[0] as usize];
355                let len2 = lengths2[pixel[2] as usize];
356                let len3 = lengths3[pixel[3] as usize];
357
358                let code = u64::from(codes1[pixel[1] as usize])
359                    | (u64::from(codes0[pixel[0] as usize]) << len1)
360                    | (u64::from(codes2[pixel[2] as usize]) << (len1 + len0))
361                    | (u64::from(codes3[pixel[3] as usize]) << (len1 + len0 + len2));
362
363                w.write_bits(code, len1 + len0 + len2 + len3)?;
364                write_run(w, pixel, &mut it, &codes1, &lengths1)?;
365            }
366        }
367    }
368
369    w.flush()?;
370    Ok(())
371}