LibGfx/JBIG2: Implement support for context templates 1, 2, and 3

Template 2 is needed by some symbols in 0000372.pdf page 11 and
0000857.pdf pages 1-4. Implement the others too while here.  (The
mentioned pages in those two PDFs also use the "end of stripe" segment,
so they still don't render yet.

We still don't support EXTTEMPLATE.
This commit is contained in:
Nico Weber 2024-03-21 08:41:37 -04:00 committed by Andreas Kling
parent e2f02f4df7
commit b45a4508c7
Notes: sideshowbarker 2024-07-17 03:18:29 +09:00
2 changed files with 100 additions and 14 deletions

View file

@ -356,6 +356,12 @@ TEST_CASE(test_jbig2_decode)
Array test_inputs = {
TEST_INPUT("jbig2/bitmap.jbig2"sv),
TEST_INPUT("jbig2/bitmap-tpgdon.jbig2"sv),
TEST_INPUT("jbig2/bitmap-template1.jbig2"sv),
TEST_INPUT("jbig2/bitmap-template1-tpgdon.jbig2"sv),
TEST_INPUT("jbig2/bitmap-template2.jbig2"sv),
TEST_INPUT("jbig2/bitmap-template2-tpgdon.jbig2"sv),
TEST_INPUT("jbig2/bitmap-template3.jbig2"sv),
TEST_INPUT("jbig2/bitmap-template3-tpgdon.jbig2"sv),
TEST_INPUT("jbig2/bitmap-symbol.jbig2"sv),
};

View file

@ -292,6 +292,16 @@ Optional<int> ArithmeticIntegerDecoder::decode()
}
static u8 number_of_context_bits_for_template(u8 template_)
{
if (template_ == 0)
return 16;
if (template_ == 1)
return 13;
VERIFY(template_ == 2 || template_ == 3);
return 10;
}
// JBIG2 spec, Annex D, D.4.1 ID string
static constexpr u8 id_string[] = { 0x97, 0x4A, 0x42, 0x32, 0x0D, 0x0A, 0x1A, 0x0A };
@ -879,18 +889,24 @@ static ErrorOr<NonnullOwnPtr<BitBuffer>> generic_region_decoding_procedure(Gener
}
// 6.2.5 Decoding using a template and arithmetic coding
if (inputs.gb_template != 0)
return Error::from_string_literal("JBIG2ImageDecoderPlugin: Cannot decode GBTEMPLATE != 0 yet");
if (inputs.adaptive_template_pixels[0].x != 3 || inputs.adaptive_template_pixels[0].y != -1
|| inputs.adaptive_template_pixels[1].x != -3 || inputs.adaptive_template_pixels[1].y != -1
|| inputs.adaptive_template_pixels[2].x != 2 || inputs.adaptive_template_pixels[2].y != -2
|| inputs.adaptive_template_pixels[3].x != -2 || inputs.adaptive_template_pixels[3].y != -2)
return Error::from_string_literal("JBIG2ImageDecoderPlugin: Cannot handle custom adaptive pixels yet");
if (inputs.is_extended_reference_template_used)
return Error::from_string_literal("JBIG2ImageDecoderPlugin: Cannot decode EXTTEMPLATE yet");
if (inputs.gb_template == 0) {
if (inputs.adaptive_template_pixels[0].x != 3 || inputs.adaptive_template_pixels[0].y != -1
|| inputs.adaptive_template_pixels[1].x != -3 || inputs.adaptive_template_pixels[1].y != -1
|| inputs.adaptive_template_pixels[2].x != 2 || inputs.adaptive_template_pixels[2].y != -2
|| inputs.adaptive_template_pixels[3].x != -2 || inputs.adaptive_template_pixels[3].y != -2)
return Error::from_string_literal("JBIG2ImageDecoderPlugin: Cannot handle custom adaptive pixels yet");
} else if (inputs.gb_template == 1) {
if (inputs.adaptive_template_pixels[0].x != 3 || inputs.adaptive_template_pixels[0].y != -1)
return Error::from_string_literal("JBIG2ImageDecoderPlugin: Cannot handle custom adaptive pixels yet");
} else {
VERIFY(inputs.gb_template == 2 || inputs.gb_template == 3);
if (inputs.adaptive_template_pixels[0].x != 2 || inputs.adaptive_template_pixels[0].y != -1)
return Error::from_string_literal("JBIG2ImageDecoderPlugin: Cannot handle custom adaptive pixels yet");
}
if (inputs.skip_pattern.has_value())
return Error::from_string_literal("JBIG2ImageDecoderPlugin: Cannot decode USESKIP yet");
@ -903,7 +919,7 @@ static ErrorOr<NonnullOwnPtr<BitBuffer>> generic_region_decoding_procedure(Gener
};
// Figure 3(a) Template when GBTEMPLATE = 0 and EXTTEMPLATE = 0,
auto compute_context = [&get_pixel](NonnullOwnPtr<BitBuffer> const& buffer, int x, int y) -> u16 {
auto compute_context_0 = [&get_pixel](NonnullOwnPtr<BitBuffer> const& buffer, int x, int y) -> u16 {
u16 result = 0;
for (int i = 0; i < 5; ++i)
result = (result << 1) | (u16)get_pixel(buffer, x - 2 + i, y - 2);
@ -914,6 +930,51 @@ static ErrorOr<NonnullOwnPtr<BitBuffer>> generic_region_decoding_procedure(Gener
return result;
};
// Figure 4 Template when GBTEMPLATE = 1
auto compute_context_1 = [&get_pixel](NonnullOwnPtr<BitBuffer> const& buffer, int x, int y) -> u16 {
u16 result = 0;
for (int i = 0; i < 4; ++i)
result = (result << 1) | (u16)get_pixel(buffer, x - 1 + i, y - 2);
for (int i = 0; i < 6; ++i)
result = (result << 1) | (u16)get_pixel(buffer, x - 2 + i, y - 1);
for (int i = 0; i < 3; ++i)
result = (result << 1) | (u16)get_pixel(buffer, x - 3 + i, y);
return result;
};
// Figure 5 Template when GBTEMPLATE = 2
auto compute_context_2 = [&get_pixel](NonnullOwnPtr<BitBuffer> const& buffer, int x, int y) -> u16 {
u16 result = 0;
for (int i = 0; i < 3; ++i)
result = (result << 1) | (u16)get_pixel(buffer, x - 1 + i, y - 2);
for (int i = 0; i < 5; ++i)
result = (result << 1) | (u16)get_pixel(buffer, x - 2 + i, y - 1);
for (int i = 0; i < 2; ++i)
result = (result << 1) | (u16)get_pixel(buffer, x - 2 + i, y);
return result;
};
// Figure 6 Template when GBTEMPLATE = 3
auto compute_context_3 = [&get_pixel](NonnullOwnPtr<BitBuffer> const& buffer, int x, int y) -> u16 {
u16 result = 0;
for (int i = 0; i < 6; ++i)
result = (result << 1) | (u16)get_pixel(buffer, x - 3 + i, y - 1);
for (int i = 0; i < 4; ++i)
result = (result << 1) | (u16)get_pixel(buffer, x - 4 + i, y);
return result;
};
auto compute_context = [&inputs, &compute_context_0, &compute_context_1, &compute_context_2, &compute_context_3](NonnullOwnPtr<BitBuffer> const& buffer, int x, int y) -> u16 {
if (inputs.gb_template == 0)
return compute_context_0(buffer, x, y);
if (inputs.gb_template == 1)
return compute_context_1(buffer, x, y);
if (inputs.gb_template == 2)
return compute_context_2(buffer, x, y);
VERIFY(inputs.gb_template == 3);
return compute_context_3(buffer, x, y);
};
// "The values of the pixels in this neighbourhood define a context. Each context has its own adaptive probability estimate
// used by the arithmetic coder (see Annex E)."
// "* Decode the current pixel by invoking the arithmetic entropy decoding procedure, with CX set to the value formed by
@ -925,13 +986,33 @@ static ErrorOr<NonnullOwnPtr<BitBuffer>> generic_region_decoding_procedure(Gener
// Figure 8 Reused context for coding the SLTP value when GBTEMPLATE is 0
constexpr u16 sltp_context_for_template_0 = 0b10011'0110010'0101;
// Figure 9 Reused context for coding the SLTP value when GBTEMPLATE is 1
constexpr u16 sltp_context_for_template_1 = 0b0011'110010'101;
// Figure 10 Reused context for coding the SLTP value when GBTEMPLATE is 2
constexpr u16 sltp_context_for_template_2 = 0b001'11001'01;
// Figure 11 Reused context for coding the SLTP value when GBTEMPLATE is 3
constexpr u16 sltp_context_for_template_3 = 0b011001'0101;
u16 sltp_context = [](u8 gb_template) {
if (gb_template == 0)
return sltp_context_for_template_0;
if (gb_template == 1)
return sltp_context_for_template_1;
if (gb_template == 2)
return sltp_context_for_template_2;
VERIFY(gb_template == 3);
return sltp_context_for_template_3;
}(inputs.gb_template);
// 6.2.5.7 Decoding the bitmap
JBIG2::ArithmeticDecoder& decoder = *inputs.arithmetic_decoder;
bool ltp = false; // "LTP" in spec. "Line (uses) Typical Prediction" maybe?
for (size_t y = 0; y < inputs.region_height; ++y) {
if (inputs.is_typical_prediction_used) {
// "SLTP" in spec. "Swap LTP" or "Switch LTP" maybe?
bool sltp = decoder.get_next_bit(contexts[sltp_context_for_template_0]);
bool sltp = decoder.get_next_bit(contexts[sltp_context]);
ltp = ltp ^ sltp;
if (ltp) {
for (size_t x = 0; x < inputs.region_width; ++x)
@ -1383,8 +1464,7 @@ static ErrorOr<Vector<NonnullRefPtr<Symbol>>> symbol_dictionary_decoding_procedu
auto decoder = TRY(JBIG2::ArithmeticDecoder::initialize(data));
Vector<JBIG2::ArithmeticDecoder::Context> contexts;
u8 template_size = inputs.symbol_template == 0 ? 16 : (inputs.symbol_template == 1 ? 13 : 10);
contexts.resize(1 << template_size);
contexts.resize(1 << number_of_context_bits_for_template(inputs.symbol_template));
// 6.5.6 Height class delta height
// "If SDHUFF is 1, decode a value using the Huffman table specified by SDHUFFDH.
@ -1875,7 +1955,7 @@ static ErrorOr<void> decode_immediate_generic_region(JBIG2LoadingContext& contex
// Done above.
// "2) As described in E.3.7, reset all the arithmetic coding statistics to zero."
Vector<JBIG2::ArithmeticDecoder::Context> contexts;
contexts.resize(1 << 16);
contexts.resize(1 << number_of_context_bits_for_template(arithmetic_coding_template));
// "3) Invoke the generic region decoding procedure described in 6.2, with the parameters to the generic region decoding procedure set as shown in Table 37."
GenericRegionDecodingInputParameters inputs;