Coverage Report

Created: 2025-12-18 07:52

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