/src/libjxl/lib/jxl/enc_huffman.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) the JPEG XL Project Authors. All rights reserved. |
2 | | // |
3 | | // Use of this source code is governed by a BSD-style |
4 | | // license that can be found in the LICENSE file. |
5 | | |
6 | | #include "lib/jxl/enc_huffman.h" |
7 | | |
8 | | #include <algorithm> |
9 | | #include <memory> |
10 | | |
11 | | #include "lib/jxl/huffman_tree.h" |
12 | | |
13 | | namespace jxl { |
14 | | |
15 | | namespace { |
16 | | |
17 | | constexpr int kCodeLengthCodes = 18; |
18 | | |
19 | | void StoreHuffmanTreeOfHuffmanTreeToBitMask(const int num_codes, |
20 | | const uint8_t* code_length_bitdepth, |
21 | 368 | BitWriter* writer) { |
22 | 368 | static const uint8_t kStorageOrder[kCodeLengthCodes] = { |
23 | 368 | 1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15}; |
24 | | // The bit lengths of the Huffman code over the code length alphabet |
25 | | // are compressed with the following static Huffman code: |
26 | | // Symbol Code |
27 | | // ------ ---- |
28 | | // 0 00 |
29 | | // 1 1110 |
30 | | // 2 110 |
31 | | // 3 01 |
32 | | // 4 10 |
33 | | // 5 1111 |
34 | 368 | static const uint8_t kHuffmanBitLengthHuffmanCodeSymbols[6] = {0, 7, 3, |
35 | 368 | 2, 1, 15}; |
36 | 368 | static const uint8_t kHuffmanBitLengthHuffmanCodeBitLengths[6] = {2, 4, 3, |
37 | 368 | 2, 2, 4}; |
38 | | |
39 | | // Throw away trailing zeros: |
40 | 368 | size_t codes_to_store = kCodeLengthCodes; |
41 | 368 | if (num_codes > 1) { |
42 | 4.87k | for (; codes_to_store > 0; --codes_to_store) { |
43 | 4.87k | if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) { |
44 | 368 | break; |
45 | 368 | } |
46 | 4.87k | } |
47 | 368 | } |
48 | 368 | size_t skip_some = 0; // skips none. |
49 | 368 | if (code_length_bitdepth[kStorageOrder[0]] == 0 && |
50 | 368 | code_length_bitdepth[kStorageOrder[1]] == 0) { |
51 | 0 | skip_some = 2; // skips two. |
52 | 0 | if (code_length_bitdepth[kStorageOrder[2]] == 0) { |
53 | 0 | skip_some = 3; // skips three. |
54 | 0 | } |
55 | 0 | } |
56 | 368 | writer->Write(2, skip_some); |
57 | 2.48k | for (size_t i = skip_some; i < codes_to_store; ++i) { |
58 | 2.11k | size_t l = code_length_bitdepth[kStorageOrder[i]]; |
59 | 2.11k | writer->Write(kHuffmanBitLengthHuffmanCodeBitLengths[l], |
60 | 2.11k | kHuffmanBitLengthHuffmanCodeSymbols[l]); |
61 | 2.11k | } |
62 | 368 | } |
63 | | |
64 | | void StoreHuffmanTreeToBitMask(const size_t huffman_tree_size, |
65 | | const uint8_t* huffman_tree, |
66 | | const uint8_t* huffman_tree_extra_bits, |
67 | | const uint8_t* code_length_bitdepth, |
68 | | const uint16_t* code_length_bitdepth_symbols, |
69 | 368 | BitWriter* writer) { |
70 | 3.22k | for (size_t i = 0; i < huffman_tree_size; ++i) { |
71 | 2.85k | size_t ix = huffman_tree[i]; |
72 | 2.85k | writer->Write(code_length_bitdepth[ix], code_length_bitdepth_symbols[ix]); |
73 | | // Extra bits |
74 | 2.85k | switch (ix) { |
75 | 0 | case 16: |
76 | 0 | writer->Write(2, huffman_tree_extra_bits[i]); |
77 | 0 | break; |
78 | 0 | case 17: |
79 | 0 | writer->Write(3, huffman_tree_extra_bits[i]); |
80 | 0 | break; |
81 | 2.85k | } |
82 | 2.85k | } |
83 | 368 | } |
84 | | |
85 | | void StoreSimpleHuffmanTree(const uint8_t* depths, size_t symbols[4], |
86 | | size_t num_symbols, size_t max_bits, |
87 | 92 | BitWriter* writer) { |
88 | | // value of 1 indicates a simple Huffman code |
89 | 92 | writer->Write(2, 1); |
90 | 92 | writer->Write(2, num_symbols - 1); // NSYM - 1 |
91 | | |
92 | | // Sort |
93 | 460 | for (size_t i = 0; i < num_symbols; i++) { |
94 | 920 | for (size_t j = i + 1; j < num_symbols; j++) { |
95 | 552 | if (depths[symbols[j]] < depths[symbols[i]]) { |
96 | 0 | std::swap(symbols[j], symbols[i]); |
97 | 0 | } |
98 | 552 | } |
99 | 368 | } |
100 | | |
101 | 92 | if (num_symbols == 2) { |
102 | 0 | writer->Write(max_bits, symbols[0]); |
103 | 0 | writer->Write(max_bits, symbols[1]); |
104 | 92 | } else if (num_symbols == 3) { |
105 | 0 | writer->Write(max_bits, symbols[0]); |
106 | 0 | writer->Write(max_bits, symbols[1]); |
107 | 0 | writer->Write(max_bits, symbols[2]); |
108 | 92 | } else { |
109 | 92 | writer->Write(max_bits, symbols[0]); |
110 | 92 | writer->Write(max_bits, symbols[1]); |
111 | 92 | writer->Write(max_bits, symbols[2]); |
112 | 92 | writer->Write(max_bits, symbols[3]); |
113 | | // tree-select |
114 | 92 | writer->Write(1, depths[symbols[0]] == 1 ? 1 : 0); |
115 | 92 | } |
116 | 92 | } |
117 | | |
118 | | // num = alphabet size |
119 | | // depths = symbol depths |
120 | 368 | void StoreHuffmanTree(const uint8_t* depths, size_t num, BitWriter* writer) { |
121 | | // Write the Huffman tree into the compact representation. |
122 | 368 | std::unique_ptr<uint8_t[]> arena(new uint8_t[2 * num]); |
123 | 368 | uint8_t* huffman_tree = arena.get(); |
124 | 368 | uint8_t* huffman_tree_extra_bits = arena.get() + num; |
125 | 368 | size_t huffman_tree_size = 0; |
126 | 368 | WriteHuffmanTree(depths, num, &huffman_tree_size, huffman_tree, |
127 | 368 | huffman_tree_extra_bits); |
128 | | |
129 | | // Calculate the statistics of the Huffman tree in the compact representation. |
130 | 368 | uint32_t huffman_tree_histogram[kCodeLengthCodes] = {0}; |
131 | 3.22k | for (size_t i = 0; i < huffman_tree_size; ++i) { |
132 | 2.85k | ++huffman_tree_histogram[huffman_tree[i]]; |
133 | 2.85k | } |
134 | | |
135 | 368 | int num_codes = 0; |
136 | 368 | int code = 0; |
137 | 1.10k | for (int i = 0; i < kCodeLengthCodes; ++i) { |
138 | 1.10k | if (huffman_tree_histogram[i]) { |
139 | 736 | if (num_codes == 0) { |
140 | 368 | code = i; |
141 | 368 | num_codes = 1; |
142 | 368 | } else if (num_codes == 1) { |
143 | 368 | num_codes = 2; |
144 | 368 | break; |
145 | 368 | } |
146 | 736 | } |
147 | 1.10k | } |
148 | | |
149 | | // Calculate another Huffman tree to use for compressing both the |
150 | | // earlier Huffman tree with. |
151 | 368 | uint8_t code_length_bitdepth[kCodeLengthCodes] = {0}; |
152 | 368 | uint16_t code_length_bitdepth_symbols[kCodeLengthCodes] = {0}; |
153 | 368 | CreateHuffmanTree(&huffman_tree_histogram[0], kCodeLengthCodes, 5, |
154 | 368 | &code_length_bitdepth[0]); |
155 | 368 | ConvertBitDepthsToSymbols(code_length_bitdepth, kCodeLengthCodes, |
156 | 368 | &code_length_bitdepth_symbols[0]); |
157 | | |
158 | | // Now, we have all the data, let's start storing it |
159 | 368 | StoreHuffmanTreeOfHuffmanTreeToBitMask(num_codes, code_length_bitdepth, |
160 | 368 | writer); |
161 | | |
162 | 368 | if (num_codes == 1) { |
163 | 0 | code_length_bitdepth[code] = 0; |
164 | 0 | } |
165 | | |
166 | | // Store the real huffman tree now. |
167 | 368 | StoreHuffmanTreeToBitMask(huffman_tree_size, huffman_tree, |
168 | 368 | huffman_tree_extra_bits, &code_length_bitdepth[0], |
169 | 368 | code_length_bitdepth_symbols, writer); |
170 | 368 | } |
171 | | |
172 | | } // namespace |
173 | | |
174 | | void BuildAndStoreHuffmanTree(const uint32_t* histogram, const size_t length, |
175 | | uint8_t* depth, uint16_t* bits, |
176 | 460 | BitWriter* writer) { |
177 | 460 | size_t count = 0; |
178 | 460 | size_t s4[4] = {0}; |
179 | 4.23k | for (size_t i = 0; i < length; i++) { |
180 | 4.04k | if (histogram[i]) { |
181 | 2.48k | if (count < 4) { |
182 | 1.84k | s4[count] = i; |
183 | 1.84k | } else if (count > 4) { |
184 | 276 | break; |
185 | 276 | } |
186 | 2.20k | count++; |
187 | 2.20k | } |
188 | 4.04k | } |
189 | | |
190 | 460 | size_t max_bits_counter = length - 1; |
191 | 460 | size_t max_bits = 0; |
192 | 2.02k | while (max_bits_counter) { |
193 | 1.56k | max_bits_counter >>= 1; |
194 | 1.56k | ++max_bits; |
195 | 1.56k | } |
196 | | |
197 | 460 | if (count <= 1) { |
198 | | // Output symbol bits and depths are initialized with 0, nothing to do. |
199 | 0 | writer->Write(4, 1); |
200 | 0 | writer->Write(max_bits, s4[0]); |
201 | 0 | return; |
202 | 0 | } |
203 | | |
204 | 460 | CreateHuffmanTree(histogram, length, 15, depth); |
205 | 460 | ConvertBitDepthsToSymbols(depth, length, bits); |
206 | | |
207 | 460 | if (count <= 4) { |
208 | 92 | StoreSimpleHuffmanTree(depth, s4, count, max_bits, writer); |
209 | 368 | } else { |
210 | 368 | StoreHuffmanTree(depth, length, writer); |
211 | 368 | } |
212 | 460 | } |
213 | | |
214 | | } // namespace jxl |