Coverage Report

Created: 2026-02-14 08:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibGfx/ImageFormats/JBIG2Shared.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2025, Nico Weber <thakis@chromium.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <AK/Endian.h>
10
#include <AK/Optional.h>
11
#include <AK/Types.h>
12
#include <AK/Vector.h>
13
#include <LibGfx/ImageFormats/BilevelImage.h>
14
#include <LibGfx/ImageFormats/MQArithmeticCoder.h>
15
#include <LibGfx/Rect.h>
16
17
namespace Gfx::JBIG2 {
18
19
struct HalftoneGeometry {
20
    u32 region_width { 0 };         // "HBW" in spec.
21
    u32 region_height { 0 };        // "HBH" in spec.
22
    u32 grayscale_width { 0 };      // "HGW" in spec.
23
    u32 grayscale_height { 0 };     // "HGH" in spec.
24
    i32 grid_origin_x_offset { 0 }; // "HGX" in spec.
25
    i32 grid_origin_y_offset { 0 }; // "HGY" in spec.
26
    u16 grid_vector_x { 0 };        // "HRY" in spec.
27
    u16 grid_vector_y { 0 };        // "HRX" in spec.
28
    u8 pattern_width { 0 };         // "HPW" in spec.
29
    u8 pattern_height { 0 };        // "HPH" in spec.
30
};
31
ErrorOr<NonnullRefPtr<BilevelImage>> halftone_skip_pattern(HalftoneGeometry const&);
32
33
class HuffmanTable;
34
35
struct TextRegionHuffmanTables {
36
    JBIG2::HuffmanTable const* first_s_table { nullptr };                 // "SBHUFFFS" in spec.
37
    JBIG2::HuffmanTable const* subsequent_s_table { nullptr };            // "SBHUFFDS" in spec.
38
    JBIG2::HuffmanTable const* delta_t_table { nullptr };                 // "SBHUFFDT" in spec.
39
    JBIG2::HuffmanTable const* refinement_delta_width_table { nullptr };  // "SBHUFFRDW" in spec.
40
    JBIG2::HuffmanTable const* refinement_delta_height_table { nullptr }; // "SBHUFFRDH" in spec.
41
    JBIG2::HuffmanTable const* refinement_x_offset_table { nullptr };     // "SBHUFFRDX" in spec.
42
    JBIG2::HuffmanTable const* refinement_y_offset_table { nullptr };     // "SBHUFFRDY" in spec.
43
    JBIG2::HuffmanTable const* refinement_size_table { nullptr };         // "SBHUFFRSIZE" in spec.
44
};
45
ErrorOr<TextRegionHuffmanTables> text_region_huffman_tables_from_flags(u16 huffman_flags, Vector<JBIG2::HuffmanTable const*> custom_tables);
46
47
struct SymbolDictionaryHuffmanTables {
48
    JBIG2::HuffmanTable const* delta_height_table { nullptr };               // "SDHUFFDH" in spec.
49
    JBIG2::HuffmanTable const* delta_width_table { nullptr };                // "SDHUFFDW" in spec.
50
    JBIG2::HuffmanTable const* bitmap_size_table { nullptr };                // "SDHUFFBMSIZE" in spec.
51
    JBIG2::HuffmanTable const* number_of_symbol_instances_table { nullptr }; // "SDHUFFAGGINST" in spec.
52
};
53
ErrorOr<SymbolDictionaryHuffmanTables> symbol_dictionary_huffman_tables_from_flags(u16 flags, Vector<JBIG2::HuffmanTable const*> custom_tables);
54
55
// JBIG2 spec, Annex D, D.4.1 ID string
56
inline constexpr u8 id_string[] = { 0x97, 0x4A, 0x42, 0x32, 0x0D, 0x0A, 0x1A, 0x0A };
57
58
// 7.3 Segment types
59
enum SegmentType {
60
    SymbolDictionary = 0,
61
    IntermediateTextRegion = 4,
62
    ImmediateTextRegion = 6,
63
    ImmediateLosslessTextRegion = 7,
64
    PatternDictionary = 16,
65
    IntermediateHalftoneRegion = 20,
66
    ImmediateHalftoneRegion = 22,
67
    ImmediateLosslessHalftoneRegion = 23,
68
    IntermediateGenericRegion = 36,
69
    ImmediateGenericRegion = 38,
70
    ImmediateLosslessGenericRegion = 39,
71
    IntermediateGenericRefinementRegion = 40,
72
    ImmediateGenericRefinementRegion = 42,
73
    ImmediateLosslessGenericRefinementRegion = 43,
74
    PageInformation = 48,
75
    EndOfPage = 49,
76
    EndOfStripe = 50,
77
    EndOfFile = 51,
78
    Profiles = 52,
79
    Tables = 53,
80
    ColorPalette = 54,
81
    Extension = 62,
82
};
83
84
// Annex D
85
enum class Organization {
86
    // D.1 Sequential organization
87
    Sequential,
88
89
    // D.2 Random-access organization
90
    RandomAccess,
91
92
    // D.3 Embedded organization
93
    Embedded,
94
};
95
96
struct SegmentHeader {
97
    u32 segment_number { 0 };
98
    SegmentType type { SegmentType::Extension };
99
    bool retention_flag { false };
100
101
    // These two have the same size.
102
    Vector<u32> referred_to_segment_numbers;
103
    Vector<bool> referred_to_segment_retention_flags;
104
105
    // 7.2.6 Segment page association
106
    // "The first page must be numbered "1". This field may contain a value of zero; this value indicates that this segment is not associated with any page."
107
    u32 page_association { 0 };
108
109
    Optional<u32> data_length;
110
};
111
112
// 7.4.3.1.1 Text region segment flags
113
enum class ReferenceCorner {
114
    BottomLeft = 0,
115
    TopLeft = 1,
116
    BottomRight = 2,
117
    TopRight = 3,
118
};
119
120
// 7.4.8.5 Page segment flags
121
enum class CombinationOperator {
122
    Or = 0,
123
    And = 1,
124
    Xor = 2,
125
    XNor = 3,
126
    Replace = 4,
127
};
128
129
// 7.4.1 Region segment information field
130
struct [[gnu::packed]] RegionSegmentInformationField {
131
    BigEndian<u32> width;
132
    BigEndian<u32> height;
133
    BigEndian<u32> x_location;
134
    BigEndian<u32> y_location;
135
    u8 flags { 0 };
136
137
    IntRect rect() const
138
0
    {
139
0
        return { x_location, y_location, width, height };
140
0
    }
141
142
    CombinationOperator external_combination_operator() const
143
10
    {
144
10
        VERIFY((flags & 0x7) <= 4);
145
10
        return static_cast<CombinationOperator>(flags & 0x7);
146
10
    }
147
148
    bool is_color_bitmap() const
149
14
    {
150
14
        return (flags & 0x8) != 0;
151
14
    }
152
};
153
static_assert(AssertSize<RegionSegmentInformationField, 17>());
154
155
struct AdaptiveTemplatePixel {
156
    i8 x { 0 };
157
    i8 y { 0 };
158
159
    bool operator<=>(AdaptiveTemplatePixel const&) const = default;
160
};
161
162
// Figure 7 – Field to which AT pixel locations are restricted
163
inline ErrorOr<void> check_valid_adaptive_template_pixel(AdaptiveTemplatePixel const& adaptive_template_pixel)
164
4
{
165
    // Don't have to check < -127 or > 127: The offsets are stored in an i8, so they can't be out of those bounds.
166
4
    if (adaptive_template_pixel.y > 0)
167
0
        return Error::from_string_literal("JBIG2ImageDecoderPlugin: Adaptive pixel y too big");
168
4
    if (adaptive_template_pixel.y == 0 && adaptive_template_pixel.x > -1)
169
0
        return Error::from_string_literal("JBIG2ImageDecoderPlugin: Adaptive pixel x too big");
170
4
    return {};
171
4
}
172
173
struct GenericContexts {
174
    GenericContexts(u8 template_)
175
2
    {
176
2
        contexts.resize(1 << number_of_context_bits_for_template(template_));
177
2
    }
178
179
    Vector<MQArithmeticCoderContext> contexts; // "GB" (+ binary suffix) in spec.
180
181
private:
182
    static u8 number_of_context_bits_for_template(u8 template_)
183
2
    {
184
2
        if (template_ == 0)
185
1
            return 16;
186
1
        if (template_ == 1)
187
0
            return 13;
188
1
        VERIFY(template_ == 2 || template_ == 3);
189
1
        return 10;
190
1
    }
191
};
192
193
struct RefinementContexts {
194
    explicit RefinementContexts(u8 refinement_template)
195
3
    {
196
3
        contexts.resize(1 << (refinement_template == 0 ? 13 : 10));
197
3
    }
198
199
    Vector<MQArithmeticCoderContext> contexts; // "GR" (+ binary suffix) in spec.
200
};
201
202
// 7.4.8 Page information segment syntax
203
struct [[gnu::packed]] PageInformationSegment {
204
    BigEndian<u32> bitmap_width;
205
    BigEndian<u32> bitmap_height;
206
    BigEndian<u32> page_x_resolution; // In pixels/meter.
207
    BigEndian<u32> page_y_resolution; // In pixels/meter.
208
    u8 flags { 0 };
209
    BigEndian<u16> striping_information;
210
211
0
    bool is_eventually_lossless() const { return flags & 1; }
212
0
    bool might_contain_refinements() const { return (flags >> 1) & 1; }
213
16
    u8 default_color() const { return (flags >> 2) & 1; }
214
215
    CombinationOperator default_combination_operator() const
216
16
    {
217
16
        return static_cast<CombinationOperator>((flags >> 3) & 3);
218
16
    }
219
220
0
    bool requires_auxiliary_buffers() const { return (flags >> 5) & 1; }
221
222
    bool direct_region_segments_override_default_combination_operator() const
223
16
    {
224
16
        return (flags >> 6) & 1;
225
16
    }
226
227
0
    bool might_contain_coloured_segments() const { return (flags >> 7) & 1; }
228
229
17
    bool page_is_striped() const { return (striping_information & 0x8000) != 0; }
230
17
    u16 maximum_stripe_size() const { return striping_information & 0x7FFF; }
231
};
232
static_assert(AssertSize<PageInformationSegment, 19>());
233
234
// 7.4.10 End of stripe segment syntax
235
struct [[gnu::packed]] EndOfStripeSegment {
236
    // "The segment data of an end of stripe segment consists of one four-byte value, specifying the Y coordinate of the end row."
237
    BigEndian<u32> y_coordinate;
238
};
239
static_assert(AssertSize<EndOfStripeSegment, 4>());
240
241
enum class ExtensionType {
242
    SingleByteCodedComment = 0x20000000,
243
    MultiByteCodedComment = 0x20000002,
244
};
245
246
struct Code {
247
    u16 prefix_length {};         // "PREFLEN" in spec. High bit set for lower range table line.
248
    u8 range_length {};           // "RANGELEN" in spec.
249
    Optional<i32> first_value {}; // First number in "VAL" in spec.
250
    u32 code {};                  // "Encoding" in spec.
251
252
    constexpr static int LowerRangeBit = 0x8000;
253
};
254
255
ErrorOr<Vector<u32>> assign_huffman_codes(ReadonlyBytes code_lengths);
256
ErrorOr<Vector<JBIG2::Code>> uniform_huffman_codes(u32 number_of_symbols, u32 code_length);
257
258
class HuffmanTable {
259
public:
260
    enum class StandardTable {
261
        B_1,  // Standard Huffman table A
262
        B_2,  // Standard Huffman table B
263
        B_3,  // Standard Huffman table C
264
        B_4,  // Standard Huffman table D
265
        B_5,  // Standard Huffman table E
266
        B_6,  // Standard Huffman table F
267
        B_7,  // Standard Huffman table G
268
        B_8,  // Standard Huffman table H
269
        B_9,  // Standard Huffman table I
270
        B_10, // Standard Huffman table J
271
        B_11, // Standard Huffman table K
272
        B_12, // Standard Huffman table L
273
        B_13, // Standard Huffman table M
274
        B_14, // Standard Huffman table N
275
        B_15, // Standard Huffman table O
276
    };
277
    static ErrorOr<HuffmanTable*> standard_huffman_table(StandardTable);
278
279
24
    bool has_oob_symbol() const { return m_has_oob_symbol; }
280
281
    // Returns OptionalNone for OOB.
282
    ErrorOr<Optional<i32>> read_symbol(BigEndianInputBitStream&) const;
283
284
    // Will never return OOB.
285
    ErrorOr<i32> read_symbol_non_oob(BigEndianInputBitStream&) const;
286
287
    // Takes OptionalNone for OOB.
288
    ErrorOr<void> write_symbol(BigEndianOutputBitStream&, Optional<i32>) const;
289
    ErrorOr<void> write_symbol_non_oob(BigEndianOutputBitStream&, i32) const;
290
291
    HuffmanTable(ReadonlySpan<Code> codes, bool has_oob_symbol = false);
292
293
private:
294
    ErrorOr<Optional<i32>> read_symbol_internal(BigEndianInputBitStream&) const;
295
    ErrorOr<void> write_symbol_internal(BigEndianOutputBitStream&, Optional<i32>) const;
296
297
    ReadonlySpan<Code> m_codes;
298
    bool m_has_oob_symbol { false };
299
};
300
301
}