Skip to content

Commit f4d80bd

Browse files
authored
Further refactor lossless encoder (#177)
1 parent 5ba3d7b commit f4d80bd

8 files changed

Lines changed: 625 additions & 601 deletions

File tree

src/encoder.rs

Lines changed: 4 additions & 566 deletions
Large diffs are not rendered by default.

src/lib.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99
#[cfg(all(test, feature = "_benchmarks"))]
1010
extern crate test;
1111

12-
pub use self::decoder::{
13-
DecodingError, LoopCount, UpsamplingMethod, WebPDecodeOptions, WebPDecoder,
14-
};
15-
pub use self::encoder::{ColorType, EncoderParams, EncodingError, WebPEncoder};
12+
pub use decoder::{DecodingError, LoopCount, UpsamplingMethod, WebPDecodeOptions, WebPDecoder};
13+
pub use encoder::{ColorType, EncodingError, WebPEncoder};
14+
pub use lossless::EncoderParams;
1615

1716
mod alpha_blending;
1817
mod decoder;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::io::BufRead;
22

3-
use super::decoder::BitReader;
3+
use super::BitReader;
44
use crate::decoder::DecodingError;
55

66
const MAX_ALLOWED_CODE_LENGTH: usize = 15;
Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,19 @@
22
//!
33
//! [Lossless spec](https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification)
44
5+
mod huffman;
6+
mod reverse_transform;
7+
58
use std::io::BufRead;
69
use std::mem;
710

8-
use super::huffman::HuffmanTree;
9-
use super::reverse_transform::{
11+
use super::{CODE_LENGTH_CODES, CODE_LENGTH_CODE_ORDER, DISTANCE_MAP};
12+
use crate::decoder::DecodingError;
13+
use huffman::HuffmanTree;
14+
use reverse_transform::{
1015
apply_color_indexing_transform, apply_color_transform, apply_predictor_transform,
1116
apply_subtract_green_transform, TransformType,
1217
};
13-
use crate::decoder::DecodingError;
14-
15-
const CODE_LENGTH_CODES: usize = 19;
16-
const CODE_LENGTH_CODE_ORDER: [usize; CODE_LENGTH_CODES] = [
17-
17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
18-
];
19-
20-
#[rustfmt::skip]
21-
const DISTANCE_MAP: [(i8, i8); 120] = [
22-
(0, 1), (1, 0), (1, 1), (-1, 1), (0, 2), (2, 0), (1, 2), (-1, 2),
23-
(2, 1), (-2, 1), (2, 2), (-2, 2), (0, 3), (3, 0), (1, 3), (-1, 3),
24-
(3, 1), (-3, 1), (2, 3), (-2, 3), (3, 2), (-3, 2), (0, 4), (4, 0),
25-
(1, 4), (-1, 4), (4, 1), (-4, 1), (3, 3), (-3, 3), (2, 4), (-2, 4),
26-
(4, 2), (-4, 2), (0, 5), (3, 4), (-3, 4), (4, 3), (-4, 3), (5, 0),
27-
(1, 5), (-1, 5), (5, 1), (-5, 1), (2, 5), (-2, 5), (5, 2), (-5, 2),
28-
(4, 4), (-4, 4), (3, 5), (-3, 5), (5, 3), (-5, 3), (0, 6), (6, 0),
29-
(1, 6), (-1, 6), (6, 1), (-6, 1), (2, 6), (-2, 6), (6, 2), (-6, 2),
30-
(4, 5), (-4, 5), (5, 4), (-5, 4), (3, 6), (-3, 6), (6, 3), (-6, 3),
31-
(0, 7), (7, 0), (1, 7), (-1, 7), (5, 5), (-5, 5), (7, 1), (-7, 1),
32-
(4, 6), (-4, 6), (6, 4), (-6, 4), (2, 7), (-2, 7), (7, 2), (-7, 2),
33-
(3, 7), (-3, 7), (7, 3), (-7, 3), (5, 6), (-5, 6), (6, 5), (-6, 5),
34-
(8, 0), (4, 7), (-4, 7), (7, 4), (-7, 4), (8, 1), (8, 2), (6, 6),
35-
(-6, 6), (8, 3), (5, 7), (-5, 7), (7, 5), (-7, 5), (8, 4), (6, 7),
36-
(-6, 7), (7, 6), (-7, 6), (8, 5), (7, 7), (-7, 7), (8, 6), (8, 7)
37-
];
3818

3919
const GREEN: usize = 0;
4020
const RED: usize = 1;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::ops::Range;
22

33
use crate::decoder::DecodingError;
44

5-
use super::decoder::subsample_size;
5+
use super::subsample_size;
66

77
#[derive(Debug, Clone)]
88
pub(crate) enum TransformType {

src/lossless/encoder/huffman.rs

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
use std::collections::BinaryHeap;
2+
use std::io::{self, Write};
3+
4+
use super::BitWriter;
5+
use crate::lossless::CODE_LENGTH_CODE_ORDER;
6+
7+
pub(crate) fn write_single_entry_huffman_tree<W: Write>(
8+
w: &mut BitWriter<W>,
9+
symbol: u8,
10+
) -> io::Result<()> {
11+
w.write_bits(1, 2)?;
12+
if symbol <= 1 {
13+
w.write_bits(0, 1)?;
14+
w.write_bits(u64::from(symbol), 1)?;
15+
} else {
16+
w.write_bits(1, 1)?;
17+
w.write_bits(u64::from(symbol), 8)?;
18+
}
19+
Ok(())
20+
}
21+
22+
pub(crate) fn write_huffman_tree<W: Write>(
23+
w: &mut BitWriter<W>,
24+
frequencies: &[u32],
25+
lengths: &mut [u8],
26+
codes: &mut [u16],
27+
) -> io::Result<()> {
28+
if !build_huffman_tree(frequencies, lengths, codes, 15) {
29+
let symbol = frequencies
30+
.iter()
31+
.position(|&frequency| frequency > 0)
32+
.unwrap_or(0);
33+
return write_single_entry_huffman_tree(w, symbol as u8);
34+
}
35+
36+
let mut code_length_lengths = [0u8; 16];
37+
let mut code_length_codes = [0u16; 16];
38+
let mut code_length_frequencies = [0u32; 16];
39+
for &length in lengths.iter() {
40+
code_length_frequencies[length as usize] += 1;
41+
}
42+
let single_code_length_length = !build_huffman_tree(
43+
&code_length_frequencies,
44+
&mut code_length_lengths,
45+
&mut code_length_codes,
46+
7,
47+
);
48+
49+
// Write the huffman tree
50+
w.write_bits(0, 1)?; // normal huffman tree
51+
w.write_bits(19 - 4, 4)?; // num_code_lengths - 4
52+
53+
for i in CODE_LENGTH_CODE_ORDER {
54+
if i > 15 || code_length_frequencies[i] == 0 {
55+
w.write_bits(0, 3)?;
56+
} else if single_code_length_length {
57+
w.write_bits(1, 3)?;
58+
} else {
59+
w.write_bits(u64::from(code_length_lengths[i]), 3)?;
60+
}
61+
}
62+
63+
match lengths.len() {
64+
256 => {
65+
w.write_bits(1, 1)?; // max_symbol is stored
66+
w.write_bits(3, 3)?; // max_symbol_nbits / 2 - 2
67+
w.write_bits(254, 8)?; // max_symbol - 2
68+
}
69+
280 => w.write_bits(0, 1)?,
70+
_ => unreachable!(),
71+
}
72+
73+
// Write the huffman codes
74+
if !single_code_length_length {
75+
for &len in lengths.iter() {
76+
w.write_bits(
77+
u64::from(code_length_codes[len as usize]),
78+
code_length_lengths[len as usize],
79+
)?;
80+
}
81+
}
82+
83+
Ok(())
84+
}
85+
86+
fn build_huffman_tree(
87+
frequencies: &[u32],
88+
lengths: &mut [u8],
89+
codes: &mut [u16],
90+
length_limit: u8,
91+
) -> bool {
92+
assert_eq!(frequencies.len(), lengths.len());
93+
assert_eq!(frequencies.len(), codes.len());
94+
95+
if frequencies.iter().filter(|&&f| f > 0).count() <= 1 {
96+
lengths.fill(0);
97+
codes.fill(0);
98+
return false;
99+
}
100+
101+
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
102+
struct Item(u32, u16);
103+
impl Ord for Item {
104+
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
105+
other.0.cmp(&self.0)
106+
}
107+
}
108+
impl PartialOrd for Item {
109+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
110+
Some(self.cmp(other))
111+
}
112+
}
113+
114+
// Build a huffman tree
115+
let mut internal_nodes = Vec::new();
116+
let mut nodes = BinaryHeap::from_iter(
117+
frequencies
118+
.iter()
119+
.enumerate()
120+
.filter(|(_, &frequency)| frequency > 0)
121+
.map(|(i, &frequency)| Item(frequency, i as u16)),
122+
);
123+
while nodes.len() > 1 {
124+
let Item(frequency1, index1) = nodes.pop().unwrap();
125+
let mut root = nodes.peek_mut().unwrap();
126+
internal_nodes.push((index1, root.1));
127+
*root = Item(
128+
frequency1 + root.0,
129+
internal_nodes.len() as u16 + frequencies.len() as u16 - 1,
130+
);
131+
}
132+
133+
// Walk the tree to assign code lengths
134+
lengths.fill(0);
135+
let mut stack = Vec::new();
136+
stack.push((nodes.pop().unwrap().1, 0));
137+
while let Some((node, depth)) = stack.pop() {
138+
let node = node as usize;
139+
if node < frequencies.len() {
140+
lengths[node] = depth as u8;
141+
} else {
142+
let (left, right) = internal_nodes[node - frequencies.len()];
143+
stack.push((left, depth + 1));
144+
stack.push((right, depth + 1));
145+
}
146+
}
147+
148+
// Limit the codes to length length_limit
149+
let mut max_length = 0;
150+
for &length in lengths.iter() {
151+
max_length = max_length.max(length);
152+
}
153+
if max_length > length_limit {
154+
let mut counts = [0u32; 16];
155+
for &length in lengths.iter() {
156+
counts[length.min(length_limit) as usize] += 1;
157+
}
158+
159+
let mut total = 0;
160+
for (i, count) in counts
161+
.iter()
162+
.enumerate()
163+
.skip(1)
164+
.take(length_limit as usize)
165+
{
166+
total += count << (length_limit as usize - i);
167+
}
168+
169+
while total > 1u32 << length_limit {
170+
let mut i = length_limit as usize - 1;
171+
while counts[i] == 0 {
172+
i -= 1;
173+
}
174+
counts[i] -= 1;
175+
counts[length_limit as usize] -= 1;
176+
counts[i + 1] += 2;
177+
total -= 1;
178+
}
179+
180+
// assign new lengths
181+
let mut len = length_limit;
182+
let mut indexes = frequencies.iter().copied().enumerate().collect::<Vec<_>>();
183+
indexes.sort_unstable_by_key(|&(_, frequency)| frequency);
184+
for &(i, frequency) in &indexes {
185+
if frequency > 0 {
186+
while counts[len as usize] == 0 {
187+
len -= 1;
188+
}
189+
lengths[i] = len;
190+
counts[len as usize] -= 1;
191+
}
192+
}
193+
}
194+
195+
// Assign codes
196+
codes.fill(0);
197+
let mut code = 0u32;
198+
for len in 1..=length_limit {
199+
for (i, &length) in lengths.iter().enumerate() {
200+
if length == len {
201+
codes[i] = (code as u16).reverse_bits() >> (16 - len);
202+
code += 1;
203+
}
204+
}
205+
code <<= 1;
206+
}
207+
assert_eq!(code, 2 << length_limit);
208+
209+
true
210+
}

0 commit comments

Comments
 (0)