/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 | | } |