/src/serenity/Userland/Libraries/LibGfx/ImageFormats/JBIG2Shared.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2025, Nico Weber <thakis@chromium.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/BitStream.h> |
8 | | #include <AK/Enumerate.h> |
9 | | #include <LibGfx/ImageFormats/JBIG2Shared.h> |
10 | | |
11 | | namespace Gfx::JBIG2 { |
12 | | |
13 | | ErrorOr<TextRegionHuffmanTables> text_region_huffman_tables_from_flags(u16 huffman_flags, Vector<JBIG2::HuffmanTable const*> custom_tables) |
14 | 0 | { |
15 | 0 | TextRegionHuffmanTables tables; |
16 | |
|
17 | 0 | u8 custom_table_index = 0; |
18 | 0 | auto custom_table = [&custom_tables, &custom_table_index]() -> ErrorOr<JBIG2::HuffmanTable const*> { |
19 | 0 | if (custom_table_index >= custom_tables.size()) |
20 | 0 | return Error::from_string_literal("JBIG2: Custom Huffman table index out of range"); |
21 | 0 | return custom_tables[custom_table_index++]; |
22 | 0 | }; |
23 | |
|
24 | 0 | auto first_s_selection = (huffman_flags >> 0) & 0b11; // "SBHUFFFS" in spec. |
25 | 0 | if (first_s_selection == 0) |
26 | 0 | tables.first_s_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_6)); |
27 | 0 | else if (first_s_selection == 1) |
28 | 0 | tables.first_s_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_7)); |
29 | 0 | else if (first_s_selection == 2) |
30 | 0 | return Error::from_string_literal("JBIG2: Invalid first_s_table"); |
31 | 0 | else if (first_s_selection == 3) |
32 | 0 | tables.first_s_table = TRY(custom_table()); |
33 | | |
34 | 0 | auto subsequent_s_selection = (huffman_flags >> 2) & 0b11; // "SBHUFFDS" in spec. |
35 | 0 | if (subsequent_s_selection == 0) |
36 | 0 | tables.subsequent_s_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_8)); |
37 | 0 | else if (subsequent_s_selection == 1) |
38 | 0 | tables.subsequent_s_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_9)); |
39 | 0 | else if (subsequent_s_selection == 2) |
40 | 0 | tables.subsequent_s_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_10)); |
41 | 0 | else if (subsequent_s_selection == 3) |
42 | 0 | tables.subsequent_s_table = TRY(custom_table()); |
43 | |
|
44 | 0 | auto delta_t_selection = (huffman_flags >> 4) & 0b11; // "SBHUFFDT" in spec. |
45 | 0 | if (delta_t_selection == 0) |
46 | 0 | tables.delta_t_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_11)); |
47 | 0 | else if (delta_t_selection == 1) |
48 | 0 | tables.delta_t_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_12)); |
49 | 0 | else if (delta_t_selection == 2) |
50 | 0 | tables.delta_t_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_13)); |
51 | 0 | else if (delta_t_selection == 3) |
52 | 0 | tables.delta_t_table = TRY(custom_table()); |
53 | |
|
54 | 0 | auto refinement_delta_width_selection = (huffman_flags >> 6) & 0b11; // "SBHUFFRDW" in spec. |
55 | 0 | if (refinement_delta_width_selection == 0) |
56 | 0 | tables.refinement_delta_width_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_14)); |
57 | 0 | else if (refinement_delta_width_selection == 1) |
58 | 0 | tables.refinement_delta_width_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_15)); |
59 | 0 | else if (refinement_delta_width_selection == 2) |
60 | 0 | return Error::from_string_literal("JBIG2: Invalid refinement_delta_width_table"); |
61 | 0 | else if (refinement_delta_width_selection == 3) |
62 | 0 | tables.refinement_delta_width_table = TRY(custom_table()); |
63 | | |
64 | 0 | auto refinement_delta_height_selection = (huffman_flags >> 8) & 0b11; // "SBHUFFRDH" in spec. |
65 | 0 | if (refinement_delta_height_selection == 0) |
66 | 0 | tables.refinement_delta_height_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_14)); |
67 | 0 | else if (refinement_delta_height_selection == 1) |
68 | 0 | tables.refinement_delta_height_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_15)); |
69 | 0 | else if (refinement_delta_height_selection == 2) |
70 | 0 | return Error::from_string_literal("JBIG2: Invalid refinement_delta_height_table"); |
71 | 0 | else if (refinement_delta_height_selection == 3) |
72 | 0 | tables.refinement_delta_height_table = TRY(custom_table()); |
73 | | |
74 | 0 | auto refinement_x_offset_selection = (huffman_flags >> 10) & 0b11; // "SBHUFFRDX" in spec. |
75 | 0 | if (refinement_x_offset_selection == 0) |
76 | 0 | tables.refinement_x_offset_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_14)); |
77 | 0 | else if (refinement_x_offset_selection == 1) |
78 | 0 | tables.refinement_x_offset_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_15)); |
79 | 0 | else if (refinement_x_offset_selection == 2) |
80 | 0 | return Error::from_string_literal("JBIG2: Invalid refinement_x_offset_table"); |
81 | 0 | else if (refinement_x_offset_selection == 3) |
82 | 0 | tables.refinement_x_offset_table = TRY(custom_table()); |
83 | | |
84 | 0 | auto refinement_y_offset_selection = (huffman_flags >> 12) & 0b11; // "SBHUFFRDY" in spec. |
85 | 0 | if (refinement_y_offset_selection == 0) |
86 | 0 | tables.refinement_y_offset_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_14)); |
87 | 0 | else if (refinement_y_offset_selection == 1) |
88 | 0 | tables.refinement_y_offset_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_15)); |
89 | 0 | else if (refinement_y_offset_selection == 2) |
90 | 0 | return Error::from_string_literal("JBIG2: Invalid refinement_y_offset_table"); |
91 | 0 | else if (refinement_y_offset_selection == 3) |
92 | 0 | tables.refinement_y_offset_table = TRY(custom_table()); |
93 | | |
94 | 0 | auto refinement_size_selection = (huffman_flags >> 14) & 0b1; // "SBHUFFRSIZE" in spec. |
95 | 0 | if (refinement_size_selection == 0) |
96 | 0 | tables.refinement_size_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_1)); |
97 | 0 | else if (refinement_size_selection == 1) |
98 | 0 | tables.refinement_size_table = TRY(custom_table()); |
99 | |
|
100 | 0 | if (custom_table_index != custom_tables.size()) |
101 | 0 | return Error::from_string_literal("JBIG2: Not all referred text region custom tables used"); |
102 | | |
103 | 0 | if (!tables.subsequent_s_table->has_oob_symbol()) |
104 | 0 | return Error::from_string_literal("JBIG2: Custom SBHUFFDS table must have OOB symbol"); |
105 | | |
106 | 0 | if (tables.first_s_table->has_oob_symbol() |
107 | 0 | || tables.delta_t_table->has_oob_symbol() |
108 | 0 | || tables.refinement_delta_width_table->has_oob_symbol() |
109 | 0 | || tables.refinement_delta_height_table->has_oob_symbol() |
110 | 0 | || tables.refinement_x_offset_table->has_oob_symbol() |
111 | 0 | || tables.refinement_y_offset_table->has_oob_symbol() |
112 | 0 | || tables.refinement_size_table->has_oob_symbol()) { |
113 | 0 | return Error::from_string_literal("JBIG2: Custom text region Huffman tables must not have OOB symbol"); |
114 | 0 | } |
115 | | |
116 | 0 | if (huffman_flags & 0x8000) |
117 | 0 | return Error::from_string_literal("JBIG2: Invalid text region segment Huffman flags"); |
118 | | |
119 | 0 | return tables; |
120 | 0 | } |
121 | | |
122 | | ErrorOr<SymbolDictionaryHuffmanTables> symbol_dictionary_huffman_tables_from_flags(u16 flags, Vector<JBIG2::HuffmanTable const*> custom_tables) |
123 | 1 | { |
124 | | // 7.4.2.1.1 Symbol dictionary flags |
125 | 1 | SymbolDictionaryHuffmanTables tables; |
126 | | |
127 | 1 | bool uses_huffman_encoding = (flags & 1) != 0; // "SDHUFF" in spec. |
128 | | |
129 | 1 | u8 custom_table_index = 0; |
130 | 1 | auto custom_table = [&custom_tables, &custom_table_index]() -> ErrorOr<JBIG2::HuffmanTable const*> { |
131 | 0 | if (custom_table_index >= custom_tables.size()) |
132 | 0 | return Error::from_string_literal("JBIG2: Custom Huffman table index out of range"); |
133 | 0 | return custom_tables[custom_table_index++]; |
134 | 0 | }; |
135 | | |
136 | 1 | u8 huffman_table_selection_for_height_differences = (flags >> 2) & 0b11; // "SDHUFFDH" in spec. |
137 | 1 | if (!uses_huffman_encoding && huffman_table_selection_for_height_differences != 0) |
138 | 0 | return Error::from_string_literal("JBIG2: Invalid huffman_table_selection_for_height_differences"); |
139 | | |
140 | 1 | if (uses_huffman_encoding) { |
141 | 1 | if (huffman_table_selection_for_height_differences == 0) |
142 | 1 | tables.delta_height_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_4)); |
143 | 0 | else if (huffman_table_selection_for_height_differences == 1) |
144 | 0 | tables.delta_height_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_5)); |
145 | 0 | else if (huffman_table_selection_for_height_differences == 2) |
146 | 0 | return Error::from_string_literal("JBIG2: Invalid huffman_table_selection_for_height_differences"); |
147 | 0 | else if (huffman_table_selection_for_height_differences == 3) |
148 | 0 | tables.delta_height_table = TRY(custom_table()); |
149 | 1 | } |
150 | | |
151 | 1 | u8 huffman_table_selection_for_width_differences = (flags >> 4) & 0b11; // "SDHUFFDW" in spec. |
152 | 1 | if (!uses_huffman_encoding && huffman_table_selection_for_width_differences != 0) |
153 | 0 | return Error::from_string_literal("JBIG2: Invalid huffman_table_selection_for_width_differences"); |
154 | | |
155 | 1 | if (uses_huffman_encoding) { |
156 | 1 | if (huffman_table_selection_for_width_differences == 0) |
157 | 1 | tables.delta_width_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_2)); |
158 | 0 | else if (huffman_table_selection_for_width_differences == 1) |
159 | 0 | tables.delta_width_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_3)); |
160 | 0 | else if (huffman_table_selection_for_width_differences == 2) |
161 | 0 | return Error::from_string_literal("JBIG2: Invalid huffman_table_selection_for_width_differences"); |
162 | 0 | else if (huffman_table_selection_for_width_differences == 3) |
163 | 0 | tables.delta_width_table = TRY(custom_table()); |
164 | 1 | } |
165 | | |
166 | 1 | bool uses_user_supplied_size_table = (flags >> 6) & 1; // "SDHUFFBMSIZE" in spec. |
167 | 1 | if (!uses_huffman_encoding && uses_user_supplied_size_table) |
168 | 0 | return Error::from_string_literal("JBIG2: Invalid uses_user_supplied_size_table"); |
169 | | |
170 | 1 | if (uses_huffman_encoding) { |
171 | 1 | if (!uses_user_supplied_size_table) |
172 | 1 | tables.bitmap_size_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_1)); |
173 | 0 | else |
174 | 0 | tables.bitmap_size_table = TRY(custom_table()); |
175 | 1 | } |
176 | | |
177 | 1 | bool uses_user_supplied_aggregate_table = (flags >> 7) & 1; // "SDHUFFAGGINST" in spec. |
178 | 1 | if (!uses_huffman_encoding && uses_user_supplied_aggregate_table) |
179 | 0 | return Error::from_string_literal("JBIG2: Invalid uses_user_supplied_aggregate_table"); |
180 | | |
181 | 1 | if (uses_huffman_encoding) { |
182 | 1 | if (!uses_user_supplied_aggregate_table) |
183 | 1 | tables.number_of_symbol_instances_table = TRY(JBIG2::HuffmanTable::standard_huffman_table(JBIG2::HuffmanTable::StandardTable::B_1)); |
184 | 0 | else |
185 | 0 | tables.number_of_symbol_instances_table = TRY(custom_table()); |
186 | 1 | } |
187 | | |
188 | 1 | if (custom_table_index != custom_tables.size()) |
189 | 0 | return Error::from_string_literal("JBIG2: Not all referred symbol dictionary custom tables used"); |
190 | | |
191 | 1 | if (uses_huffman_encoding) { |
192 | 1 | if (!tables.delta_width_table->has_oob_symbol()) |
193 | 0 | return Error::from_string_literal("JBIG2: Custom SDHUFFDW table must have OOB symbol"); |
194 | | |
195 | 1 | if (tables.delta_height_table->has_oob_symbol() |
196 | 1 | || tables.bitmap_size_table->has_oob_symbol() |
197 | 1 | || tables.number_of_symbol_instances_table->has_oob_symbol()) { |
198 | 0 | return Error::from_string_literal("JBIG2: Custom symbol dictionary Huffman tables must not have OOB symbol"); |
199 | 0 | } |
200 | 1 | } |
201 | | |
202 | 1 | return tables; |
203 | 1 | } |
204 | | |
205 | | ErrorOr<Vector<u32>> assign_huffman_codes(ReadonlyBytes code_lengths) |
206 | 1 | { |
207 | | // FIXME: Use shared huffman code, instead of using this algorithm from the spec. |
208 | | |
209 | | // B.3 Assigning the prefix codes |
210 | | // code_lengths is "PREFLEN" in spec, code_lengths.size is "NTEMP". |
211 | 1 | Vector<u32> codes; // "CODES" in spec. |
212 | 1 | TRY(codes.try_resize(code_lengths.size())); |
213 | | |
214 | | // "1) Build a histogram in the array LENCOUNT counting the number of times each prefix length value |
215 | | // occurs in PREFLEN: LENCOUNT[I] is the number of times that the value I occurs in the array |
216 | | // PREFLEN." |
217 | 1 | Array<u32, 32> length_counts {}; // "LENCOUNT" in spec. |
218 | 37 | for (auto length : code_lengths) { |
219 | 37 | VERIFY(length < 32); |
220 | 37 | length_counts[length]++; |
221 | 37 | } |
222 | | |
223 | | // "2) Let LENMAX be the largest value for which LENCOUNT[LENMAX] > 0. Set: |
224 | | // CURLEN = 1 |
225 | | // FIRSTCODE[0] = 0 |
226 | | // LENCOUNT[0] = 0" |
227 | 1 | size_t highest_length_index = 0; // "LENMAX" in spec. |
228 | 32 | for (auto const& [i, count] : enumerate(length_counts)) { |
229 | 32 | if (count > 0) |
230 | 11 | highest_length_index = i; |
231 | 32 | } |
232 | 1 | size_t current_length = 1; // "CURLEN" in spec. |
233 | 1 | Array<u32, 32> first_code_at_length; // "FIRSTCODE" in spec. |
234 | 1 | first_code_at_length[0] = 0; |
235 | 1 | length_counts[0] = 0; |
236 | | |
237 | | // "3) While CURLEN ≤ LENMAX, perform the following operations:" |
238 | 16 | while (current_length <= highest_length_index) { |
239 | | // "a) Set: |
240 | | // FIRSTCODE[CURLEN] = (FIRSTCODE[CURLEN – 1] + LENCOUNT[CURLEN – 1]) × 2 |
241 | | // CURCODE = FIRSTCODE[CURLEN] |
242 | | // CURTEMP = 0" |
243 | 15 | first_code_at_length[current_length] = (first_code_at_length[current_length - 1] + length_counts[current_length - 1]) * 2; |
244 | 15 | u32 current_code = first_code_at_length[current_length]; // "CURCODE" in spec. |
245 | 15 | size_t i = 0; // "CURTEMP" in spec. |
246 | | |
247 | | // "b) While CURTEMP < NTEMP, perform the following operations:" |
248 | 570 | while (i < code_lengths.size()) { |
249 | | // "i) If PREFLEN[CURTEMP] = CURLEN, then set: |
250 | | // CODES[CURTEMP] = CURCODE |
251 | | // CURCODE = CURCODE + 1" |
252 | 555 | if (code_lengths[i] == current_length) { |
253 | 20 | codes[i] = current_code; |
254 | 20 | current_code++; |
255 | 20 | } |
256 | | |
257 | | // "ii) Set CURTEMP = CURTEMP + 1" |
258 | 555 | i++; |
259 | 555 | } |
260 | | |
261 | | // "c) Set: |
262 | | // CURLEN = CURLEN + 1" |
263 | 15 | current_length++; |
264 | 15 | } |
265 | | |
266 | 1 | return codes; |
267 | 1 | } |
268 | | |
269 | | ErrorOr<Vector<JBIG2::Code>> uniform_huffman_codes(u32 number_of_symbols, u32 code_length) |
270 | 0 | { |
271 | 0 | Vector<u8> code_lengths; |
272 | 0 | for (u32 i = 0; i < number_of_symbols; ++i) |
273 | 0 | code_lengths.append(code_length); |
274 | 0 | auto codes = TRY(JBIG2::assign_huffman_codes(code_lengths)); |
275 | |
|
276 | 0 | Vector<JBIG2::Code> uniform_codes; |
277 | 0 | for (auto const& [i, length] : enumerate(code_lengths)) { |
278 | 0 | if (length == 0) |
279 | 0 | continue; |
280 | 0 | JBIG2::Code code { .prefix_length = length, .range_length = 0, .first_value = i, .code = codes[i] }; |
281 | 0 | uniform_codes.append(code); |
282 | 0 | } |
283 | 0 | return uniform_codes; |
284 | 0 | } |
285 | | |
286 | | // Table B.1 – Standard Huffman table A |
287 | | constexpr Array standard_huffman_table_A = { |
288 | | Code { 1, 4, 0, 0b0 }, |
289 | | Code { 2, 8, 16, 0b10 }, |
290 | | Code { 3, 16, 272, 0b110 }, |
291 | | Code { 3, 32, 65808, 0b111 }, |
292 | | }; |
293 | | |
294 | | // Table B.2 – Standard Huffman table B |
295 | | constexpr Array standard_huffman_table_B = { |
296 | | Code { 1, 0, 0, 0b0 }, |
297 | | Code { 2, 0, 1, 0b10 }, |
298 | | Code { 3, 0, 2, 0b110 }, |
299 | | Code { 4, 3, 3, 0b1110 }, |
300 | | Code { 5, 6, 11, 0b11110 }, |
301 | | Code { 6, 32, 75, 0b111110 }, |
302 | | Code { 6, 0, OptionalNone {}, 0b111111 }, |
303 | | }; |
304 | | |
305 | | // Table B.3 – Standard Huffman table C |
306 | | constexpr Array standard_huffman_table_C = { |
307 | | Code { 8, 8, -256, 0b11111110 }, |
308 | | Code { 1, 0, 0, 0b0 }, |
309 | | Code { 2, 0, 1, 0b10 }, |
310 | | Code { 3, 0, 2, 0b110 }, |
311 | | Code { 4, 3, 3, 0b1110 }, |
312 | | Code { 5, 6, 11, 0b11110 }, |
313 | | Code { 8 | Code::LowerRangeBit, 32, -257, 0b11111111 }, |
314 | | Code { 7, 32, 75, 0b1111110 }, |
315 | | Code { 6, 0, OptionalNone {}, 0b111110 }, |
316 | | }; |
317 | | |
318 | | // Table B.4 – Standard Huffman table D |
319 | | constexpr Array standard_huffman_table_D = { |
320 | | Code { 1, 0, 1, 0b0 }, |
321 | | Code { 2, 0, 2, 0b10 }, |
322 | | Code { 3, 0, 3, 0b110 }, |
323 | | Code { 4, 3, 4, 0b1110 }, |
324 | | Code { 5, 6, 12, 0b11110 }, |
325 | | Code { 5, 32, 76, 0b11111 }, |
326 | | }; |
327 | | |
328 | | // Table B.5 – Standard Huffman table E |
329 | | constexpr Array standard_huffman_table_E = { |
330 | | Code { 7, 8, -255, 0b1111110 }, |
331 | | Code { 1, 0, 1, 0b0 }, |
332 | | Code { 2, 0, 2, 0b10 }, |
333 | | Code { 3, 0, 3, 0b110 }, |
334 | | Code { 4, 3, 4, 0b1110 }, |
335 | | Code { 5, 6, 12, 0b11110 }, |
336 | | Code { 7 | Code::LowerRangeBit, 32, -256, 0b1111111 }, |
337 | | Code { 6, 32, 76, 0b111110 }, |
338 | | }; |
339 | | |
340 | | // Table B.6 – Standard Huffman table F |
341 | | constexpr Array standard_huffman_table_F = { |
342 | | Code { 5, 10, -2048, 0b11100 }, |
343 | | Code { 4, 9, -1024, 0b1000 }, |
344 | | Code { 4, 8, -512, 0b1001 }, |
345 | | Code { 4, 7, -256, 0b1010 }, |
346 | | Code { 5, 6, -128, 0b11101 }, |
347 | | Code { 5, 5, -64, 0b11110 }, |
348 | | Code { 4, 5, -32, 0b1011 }, |
349 | | Code { 2, 7, 0, 0b00 }, |
350 | | Code { 3, 7, 128, 0b010 }, |
351 | | Code { 3, 8, 256, 0b011 }, |
352 | | Code { 4, 9, 512, 0b1100 }, |
353 | | Code { 4, 10, 1024, 0b1101 }, |
354 | | Code { 6 | Code::LowerRangeBit, 32, -2049, 0b111110 }, |
355 | | Code { 6, 32, 2048, 0b111111 }, |
356 | | }; |
357 | | |
358 | | // Table B.7 – Standard Huffman table G |
359 | | constexpr Array standard_huffman_table_G = { |
360 | | Code { 4, 9, -1024, 0b1000 }, |
361 | | Code { 3, 8, -512, 0b000 }, |
362 | | Code { 4, 7, -256, 0b1001 }, |
363 | | Code { 5, 6, -128, 0b11010 }, |
364 | | Code { 5, 5, -64, 0b11011 }, |
365 | | Code { 4, 5, -32, 0b1010 }, |
366 | | Code { 4, 5, 0, 0b1011 }, |
367 | | Code { 5, 5, 32, 0b11100 }, |
368 | | Code { 5, 6, 64, 0b11101 }, |
369 | | Code { 4, 7, 128, 0b1100 }, |
370 | | Code { 3, 8, 256, 0b001 }, |
371 | | Code { 3, 9, 512, 0b010 }, |
372 | | Code { 3, 10, 1024, 0b011 }, |
373 | | Code { 5 | Code::LowerRangeBit, 32, -1025, 0b11110 }, |
374 | | Code { 5, 32, 2048, 0b11111 }, |
375 | | }; |
376 | | |
377 | | // Table B.8 – Standard Huffman table H |
378 | | constexpr Array standard_huffman_table_H = { |
379 | | Code { 8, 3, -15, 0b11111100 }, |
380 | | Code { 9, 1, -7, 0b111111100 }, |
381 | | Code { 8, 1, -5, 0b11111101 }, |
382 | | Code { 9, 0, -3, 0b111111101 }, |
383 | | Code { 7, 0, -2, 0b1111100 }, |
384 | | Code { 4, 0, -1, 0b1010 }, |
385 | | Code { 2, 1, 0, 0b00 }, |
386 | | Code { 5, 0, 2, 0b11010 }, |
387 | | Code { 6, 0, 3, 0b111010 }, |
388 | | Code { 3, 4, 4, 0b100 }, |
389 | | Code { 6, 1, 20, 0b111011 }, |
390 | | Code { 4, 4, 22, 0b1011 }, |
391 | | Code { 4, 5, 38, 0b1100 }, |
392 | | Code { 5, 6, 70, 0b11011 }, |
393 | | Code { 5, 7, 134, 0b11100 }, |
394 | | Code { 6, 7, 262, 0b111100 }, |
395 | | Code { 7, 8, 390, 0b1111101 }, |
396 | | Code { 6, 10, 646, 0b111101 }, |
397 | | Code { 9 | Code::LowerRangeBit, 32, -16, 0b111111110 }, |
398 | | Code { 9, 32, 1670, 0b111111111 }, |
399 | | Code { 2, 0, OptionalNone {}, 0b01 }, |
400 | | }; |
401 | | |
402 | | // Table B.9 – Standard Huffman table I |
403 | | constexpr Array standard_huffman_table_I = { |
404 | | Code { 8, 4, -31, 0b11111100 }, |
405 | | Code { 9, 2, -15, 0b111111100 }, |
406 | | Code { 8, 2, -11, 0b11111101 }, |
407 | | Code { 9, 1, -7, 0b111111101 }, |
408 | | Code { 7, 1, -5, 0b1111100 }, |
409 | | Code { 4, 1, -3, 0b1010 }, |
410 | | Code { 3, 1, -1, 0b010 }, |
411 | | Code { 3, 1, 1, 0b011 }, |
412 | | Code { 5, 1, 3, 0b11010 }, |
413 | | Code { 6, 1, 5, 0b111010 }, |
414 | | Code { 3, 5, 7, 0b100 }, |
415 | | Code { 6, 2, 39, 0b111011 }, |
416 | | Code { 4, 5, 43, 0b1011 }, |
417 | | Code { 4, 6, 75, 0b1100 }, |
418 | | Code { 5, 7, 139, 0b11011 }, |
419 | | Code { 5, 8, 267, 0b11100 }, |
420 | | Code { 6, 8, 523, 0b111100 }, |
421 | | Code { 7, 9, 779, 0b1111101 }, |
422 | | Code { 6, 11, 1291, 0b111101 }, |
423 | | Code { 9 | Code::LowerRangeBit, 32, -32, 0b111111110 }, |
424 | | Code { 9, 32, 3339, 0b111111111 }, |
425 | | Code { 2, 0, OptionalNone {}, 0b00 }, |
426 | | }; |
427 | | |
428 | | // Table B.10 – Standard Huffman table J |
429 | | constexpr Array standard_huffman_table_J = { |
430 | | Code { 7, 4, -21, 0b1111010 }, |
431 | | Code { 8, 0, -5, 0b11111100 }, |
432 | | Code { 7, 0, -4, 0b1111011 }, |
433 | | Code { 5, 0, -3, 0b11000 }, |
434 | | Code { 2, 2, -2, 0b00 }, |
435 | | Code { 5, 0, 2, 0b11001 }, |
436 | | Code { 6, 0, 3, 0b110110 }, |
437 | | Code { 7, 0, 4, 0b1111100 }, |
438 | | Code { 8, 0, 5, 0b11111101 }, |
439 | | Code { 2, 6, 6, 0b01 }, |
440 | | Code { 5, 5, 70, 0b11010 }, |
441 | | Code { 6, 5, 102, 0b110111 }, |
442 | | Code { 6, 6, 134, 0b111000 }, |
443 | | Code { 6, 7, 198, 0b111001 }, |
444 | | Code { 6, 8, 326, 0b111010 }, |
445 | | Code { 6, 9, 582, 0b111011 }, |
446 | | Code { 6, 10, 1094, 0b111100 }, |
447 | | Code { 7, 11, 2118, 0b1111101 }, |
448 | | Code { 8 | Code::LowerRangeBit, 32, -22, 0b11111110 }, |
449 | | Code { 8, 32, 4166, 0b11111111 }, |
450 | | Code { 2, 0, OptionalNone {}, 0b10 }, |
451 | | }; |
452 | | |
453 | | // Table B.11 – Standard Huffman table K |
454 | | constexpr Array standard_huffman_table_K = { |
455 | | Code { 1, 0, 1, 0b0 }, |
456 | | Code { 2, 1, 2, 0b10 }, |
457 | | Code { 4, 0, 4, 0b1100 }, |
458 | | Code { 4, 1, 5, 0b1101 }, |
459 | | Code { 5, 1, 7, 0b11100 }, |
460 | | Code { 5, 2, 9, 0b11101 }, |
461 | | Code { 6, 2, 13, 0b111100 }, |
462 | | Code { 7, 2, 17, 0b1111010 }, |
463 | | Code { 7, 3, 21, 0b1111011 }, |
464 | | Code { 7, 4, 29, 0b1111100 }, |
465 | | Code { 7, 5, 45, 0b1111101 }, |
466 | | Code { 7, 6, 77, 0b1111110 }, |
467 | | Code { 7, 32, 141, 0b1111111 }, |
468 | | }; |
469 | | |
470 | | // Table B.12 – Standard Huffman table L |
471 | | constexpr Array standard_huffman_table_L = { |
472 | | Code { 1, 0, 1, 0b0 }, |
473 | | Code { 2, 0, 2, 0b10 }, |
474 | | Code { 3, 1, 3, 0b110 }, |
475 | | Code { 5, 0, 5, 0b11100 }, |
476 | | Code { 5, 1, 6, 0b11101 }, |
477 | | Code { 6, 1, 8, 0b111100 }, |
478 | | Code { 7, 0, 10, 0b1111010 }, |
479 | | Code { 7, 1, 11, 0b1111011 }, |
480 | | Code { 7, 2, 13, 0b1111100 }, |
481 | | Code { 7, 3, 17, 0b1111101 }, |
482 | | Code { 7, 4, 25, 0b1111110 }, |
483 | | Code { 8, 5, 41, 0b11111110 }, |
484 | | Code { 8, 32, 73, 0b11111111 }, |
485 | | }; |
486 | | |
487 | | // Table B.13 – Standard Huffman table M |
488 | | constexpr Array standard_huffman_table_M = { |
489 | | Code { 1, 0, 1, 0b0 }, |
490 | | Code { 3, 0, 2, 0b100 }, |
491 | | Code { 4, 0, 3, 0b1100 }, |
492 | | Code { 5, 0, 4, 0b11100 }, |
493 | | Code { 4, 1, 5, 0b1101 }, |
494 | | Code { 3, 3, 7, 0b101 }, |
495 | | Code { 6, 1, 15, 0b111010 }, |
496 | | Code { 6, 2, 17, 0b111011 }, |
497 | | Code { 6, 3, 21, 0b111100 }, |
498 | | Code { 6, 4, 29, 0b111101 }, |
499 | | Code { 6, 5, 45, 0b111110 }, |
500 | | Code { 7, 6, 77, 0b1111110 }, |
501 | | Code { 7, 32, 141, 0b1111111 }, |
502 | | }; |
503 | | |
504 | | // Table B.14 – Standard Huffman table N |
505 | | constexpr Array standard_huffman_table_N = { |
506 | | Code { 3, 0, -2, 0b100 }, |
507 | | Code { 3, 0, -1, 0b101 }, |
508 | | Code { 1, 0, 0, 0b0 }, |
509 | | Code { 3, 0, 1, 0b110 }, |
510 | | Code { 3, 0, 2, 0b111 }, |
511 | | }; |
512 | | |
513 | | // Table B.15 – Standard Huffman table O |
514 | | constexpr Array standard_huffman_table_O = { |
515 | | Code { 7, 4, -24, 0b1111100 }, |
516 | | Code { 6, 2, -8, 0b111100 }, |
517 | | Code { 5, 1, -4, 0b11100 }, |
518 | | Code { 4, 0, -2, 0b1100 }, |
519 | | Code { 3, 0, -1, 0b100 }, |
520 | | Code { 1, 0, 0, 0b0 }, |
521 | | Code { 3, 0, 1, 0b101 }, |
522 | | Code { 4, 0, 2, 0b1101 }, |
523 | | Code { 5, 1, 3, 0b11101 }, |
524 | | Code { 6, 2, 5, 0b111101 }, |
525 | | Code { 7, 4, 9, 0b1111101 }, |
526 | | Code { 7 | Code::LowerRangeBit, 32, -25, 0b1111110 }, |
527 | | Code { 7, 32, 25, 0b1111111 }, |
528 | | }; |
529 | | |
530 | | ErrorOr<HuffmanTable*> HuffmanTable::standard_huffman_table(StandardTable kind) |
531 | 4 | { |
532 | 4 | switch (kind) { |
533 | 2 | case StandardTable::B_1: { |
534 | 2 | static HuffmanTable standard_table_A(standard_huffman_table_A); |
535 | 2 | return &standard_table_A; |
536 | 0 | } |
537 | 1 | case StandardTable::B_2: { |
538 | 1 | static HuffmanTable standard_table_B(standard_huffman_table_B, true); |
539 | 1 | return &standard_table_B; |
540 | 0 | } |
541 | 0 | case StandardTable::B_3: { |
542 | 0 | static HuffmanTable standard_table_C(standard_huffman_table_C, true); |
543 | 0 | return &standard_table_C; |
544 | 0 | } |
545 | 1 | case StandardTable::B_4: { |
546 | 1 | static HuffmanTable standard_table_D(standard_huffman_table_D); |
547 | 1 | return &standard_table_D; |
548 | 0 | } |
549 | 0 | case StandardTable::B_5: { |
550 | 0 | static HuffmanTable standard_table_E(standard_huffman_table_E); |
551 | 0 | return &standard_table_E; |
552 | 0 | } |
553 | 0 | case StandardTable::B_6: { |
554 | 0 | static HuffmanTable standard_table_F(standard_huffman_table_F); |
555 | 0 | return &standard_table_F; |
556 | 0 | } |
557 | 0 | case StandardTable::B_7: { |
558 | 0 | static HuffmanTable standard_table_G(standard_huffman_table_G); |
559 | 0 | return &standard_table_G; |
560 | 0 | } |
561 | 0 | case StandardTable::B_8: { |
562 | 0 | static HuffmanTable standard_table_H(standard_huffman_table_H, true); |
563 | 0 | return &standard_table_H; |
564 | 0 | } |
565 | 0 | case StandardTable::B_9: { |
566 | 0 | static HuffmanTable standard_table_I(standard_huffman_table_I, true); |
567 | 0 | return &standard_table_I; |
568 | 0 | } |
569 | 0 | case StandardTable::B_10: { |
570 | 0 | static HuffmanTable standard_table_J(standard_huffman_table_J, true); |
571 | 0 | return &standard_table_J; |
572 | 0 | } |
573 | 0 | case StandardTable::B_11: { |
574 | 0 | static HuffmanTable standard_table_K(standard_huffman_table_K); |
575 | 0 | return &standard_table_K; |
576 | 0 | } |
577 | 0 | case StandardTable::B_12: { |
578 | 0 | static HuffmanTable standard_table_L(standard_huffman_table_L); |
579 | 0 | return &standard_table_L; |
580 | 0 | } |
581 | 0 | case StandardTable::B_13: { |
582 | 0 | static HuffmanTable standard_table_M(standard_huffman_table_M); |
583 | 0 | return &standard_table_M; |
584 | 0 | } |
585 | 0 | case StandardTable::B_14: { |
586 | 0 | static HuffmanTable standard_table_N(standard_huffman_table_N); |
587 | 0 | return &standard_table_N; |
588 | 0 | } |
589 | 0 | case StandardTable::B_15: { |
590 | 0 | static HuffmanTable standard_table_O(standard_huffman_table_O); |
591 | 0 | return &standard_table_O; |
592 | 0 | } |
593 | 4 | } |
594 | 0 | VERIFY_NOT_REACHED(); |
595 | 0 | } |
596 | | |
597 | | ErrorOr<Optional<i32>> HuffmanTable::read_symbol_internal(BigEndianInputBitStream& stream) const |
598 | 212 | { |
599 | | // FIXME: Use an approach that doesn't require a full scan for every bit. See Compress::CanonicalCodes. |
600 | 212 | u32 code_word = 0; |
601 | 212 | u8 code_size = 0; |
602 | 329 | while (true) { |
603 | 329 | code_word = (code_word << 1) | TRY(stream.read_bit()); |
604 | 329 | code_size++; |
605 | 1.14k | for (auto const& code : m_codes) { |
606 | 1.14k | if ((code.prefix_length & ~Code::LowerRangeBit) == code_size && code.code == code_word) { |
607 | 212 | if (!code.first_value.has_value()) |
608 | 1 | return code.first_value; // OOB |
609 | | |
610 | 211 | i32 value = 0; // "HTOFFSET" in spec. |
611 | 262 | for (u8 i = 0; i < code.range_length; ++i) |
612 | 51 | value = (value << 1) | TRY(stream.read_bit()); |
613 | | |
614 | 211 | if (code.prefix_length & Code::LowerRangeBit) |
615 | 0 | return code.first_value.value() - value; |
616 | 211 | return value + code.first_value.value(); |
617 | 211 | } |
618 | 1.14k | } |
619 | 329 | } |
620 | 0 | VERIFY_NOT_REACHED(); |
621 | 0 | } |
622 | | |
623 | | ErrorOr<Optional<i32>> HuffmanTable::read_symbol(BigEndianInputBitStream& stream) const |
624 | 210 | { |
625 | 210 | VERIFY(m_has_oob_symbol); |
626 | 210 | return read_symbol_internal(stream); |
627 | 210 | } |
628 | | |
629 | | ErrorOr<i32> HuffmanTable::read_symbol_non_oob(BigEndianInputBitStream& stream) const |
630 | 2 | { |
631 | 2 | VERIFY(!m_has_oob_symbol); |
632 | 2 | auto result = TRY(read_symbol_internal(stream)); |
633 | 2 | return result.value(); |
634 | 2 | } |
635 | | |
636 | | ErrorOr<void> HuffmanTable::write_symbol_internal(BigEndianOutputBitStream& stream, Optional<i32> value_or_oob) const |
637 | 0 | { |
638 | | // FIXME: Use an approach that doesn't require a full scan for every value, |
639 | | // for example by handling OOB, lower range, and upper range first, |
640 | | // and then binary searching the rest. |
641 | 0 | for (auto const& code : m_codes) { |
642 | 0 | if (value_or_oob.has_value() != code.first_value.has_value()) |
643 | 0 | continue; |
644 | | |
645 | 0 | if (!value_or_oob.has_value()) { |
646 | 0 | VERIFY(code.range_length == 0); |
647 | 0 | return stream.write_bits(code.code, code.prefix_length); |
648 | 0 | } |
649 | | |
650 | 0 | auto first_value = code.first_value.value(); |
651 | 0 | auto value = value_or_oob.value(); |
652 | |
|
653 | 0 | if (code.prefix_length & Code::LowerRangeBit) { |
654 | 0 | VERIFY(code.range_length == 32); |
655 | 0 | if (value > first_value) |
656 | 0 | continue; |
657 | 0 | TRY(stream.write_bits(code.code, code.prefix_length & ~Code::LowerRangeBit)); |
658 | 0 | return stream.write_bits(static_cast<u32>(first_value - value), code.range_length); |
659 | 0 | } |
660 | | |
661 | 0 | if (value < first_value || (code.range_length != 32 && value >= (first_value + (1 << code.range_length)))) |
662 | 0 | continue; |
663 | 0 | TRY(stream.write_bits(code.code, code.prefix_length)); |
664 | 0 | return stream.write_bits(static_cast<u32>(value - first_value), code.range_length); |
665 | 0 | } |
666 | 0 | return Error::from_string_literal("JBIG2Writer: value not representable in this huffman table"); |
667 | 0 | } |
668 | | |
669 | | ErrorOr<void> HuffmanTable::write_symbol(BigEndianOutputBitStream& stream, Optional<i32> value) const |
670 | 0 | { |
671 | 0 | VERIFY(m_has_oob_symbol); |
672 | 0 | return write_symbol_internal(stream, value); |
673 | 0 | } |
674 | | |
675 | | ErrorOr<void> HuffmanTable::write_symbol_non_oob(BigEndianOutputBitStream& stream, i32 value) const |
676 | 0 | { |
677 | 0 | VERIFY(!m_has_oob_symbol); |
678 | 0 | return write_symbol_internal(stream, value); |
679 | 0 | } |
680 | | |
681 | | HuffmanTable::HuffmanTable(ReadonlySpan<Code> codes, bool has_oob_symbol) |
682 | 4 | : m_codes(codes) |
683 | 4 | , m_has_oob_symbol(has_oob_symbol) |
684 | 4 | { |
685 | 4 | } |
686 | | |
687 | | } |