diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index a81b227feac..c9eae20a317 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -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), }; diff --git a/Userland/Libraries/LibGfx/ImageFormats/JBIG2Loader.cpp b/Userland/Libraries/LibGfx/ImageFormats/JBIG2Loader.cpp index 2d6741cd8a7..f02c86438b3 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/JBIG2Loader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/JBIG2Loader.cpp @@ -292,6 +292,16 @@ Optional 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> 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> generic_region_decoding_procedure(Gener }; // Figure 3(a) – Template when GBTEMPLATE = 0 and EXTTEMPLATE = 0, - auto compute_context = [&get_pixel](NonnullOwnPtr const& buffer, int x, int y) -> u16 { + auto compute_context_0 = [&get_pixel](NonnullOwnPtr 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> generic_region_decoding_procedure(Gener return result; }; + // Figure 4 – Template when GBTEMPLATE = 1 + auto compute_context_1 = [&get_pixel](NonnullOwnPtr 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 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 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 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> 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>> symbol_dictionary_decoding_procedu auto decoder = TRY(JBIG2::ArithmeticDecoder::initialize(data)); Vector 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 decode_immediate_generic_region(JBIG2LoadingContext& contex // Done above. // "2) As described in E.3.7, reset all the arithmetic coding statistics to zero." Vector 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;