diff options
Diffstat (limited to 'Userland/Libraries/LibGfx/JPEGLoader.cpp')
-rw-r--r-- | Userland/Libraries/LibGfx/JPEGLoader.cpp | 49 |
1 files changed, 28 insertions, 21 deletions
diff --git a/Userland/Libraries/LibGfx/JPEGLoader.cpp b/Userland/Libraries/LibGfx/JPEGLoader.cpp index 23b184f8b2..5145ac6b0f 100644 --- a/Userland/Libraries/LibGfx/JPEGLoader.cpp +++ b/Userland/Libraries/LibGfx/JPEGLoader.cpp @@ -200,6 +200,8 @@ struct Scan { u8 spectral_selection_start {}; u8 spectral_selection_end {}; u8 successive_approximation {}; + + HuffmanStreamState huffman_stream; }; struct JPEGLoadingContext { @@ -227,7 +229,6 @@ struct JPEGLoadingContext { u16 dc_restart_interval { 0 }; HashMap<u8, HuffmanTableSpec> dc_tables; HashMap<u8, HuffmanTableSpec> ac_tables; - HuffmanStreamState huffman_stream; i32 previous_dc_values[3] = { 0 }; MacroblockMeta mblock_meta; OwnPtr<FixedMemoryStream> stream; @@ -303,16 +304,17 @@ static inline i32* get_component(Macroblock& block, unsigned component) static ErrorOr<void> add_dc(JPEGLoadingContext& context, Macroblock& macroblock, ScanComponent const& scan_component) { auto& dc_table = context.dc_tables.find(scan_component.dc_destination_id)->value; + auto& scan = context.current_scan; // For DC coefficients, symbol encodes the length of the coefficient. - auto dc_length = TRY(get_next_symbol(context.huffman_stream, dc_table)); + auto dc_length = TRY(get_next_symbol(scan.huffman_stream, dc_table)); if (dc_length > 11) { dbgln_if(JPEG_DEBUG, "DC coefficient too long: {}!", dc_length); return Error::from_string_literal("DC coefficient too long"); } // DC coefficients are encoded as the difference between previous and current DC values. - i32 dc_diff = TRY(read_huffman_bits(context.huffman_stream, dc_length)); + i32 dc_diff = TRY(read_huffman_bits(scan.huffman_stream, dc_length)); // If MSB in diff is 0, the difference is -ve. Otherwise +ve. if (dc_length != 0 && dc_diff < (1 << (dc_length - 1))) @@ -330,16 +332,18 @@ static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock, auto& ac_table = context.ac_tables.find(scan_component.ac_destination_id)->value; auto* select_component = get_component(macroblock, scan_component.component.index); + auto& scan = context.current_scan; + // Compute the AC coefficients. // 0th coefficient is the dc, which is already handled - auto first_coefficient = max(1, context.current_scan.spectral_selection_start); + auto first_coefficient = max(1, scan.spectral_selection_start); - for (int j = first_coefficient; j <= context.current_scan.spectral_selection_end;) { + for (int j = first_coefficient; j <= scan.spectral_selection_end;) { // AC symbols encode 2 pieces of information, the high 4 bits represent // number of zeroes to be stuffed before reading the coefficient. Low 4 // bits represent the magnitude of the coefficient. - auto ac_symbol = TRY(get_next_symbol(context.huffman_stream, ac_table)); + auto ac_symbol = TRY(get_next_symbol(scan.huffman_stream, ac_table)); if (ac_symbol == 0) break; @@ -347,7 +351,7 @@ static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock, u8 run_length = ac_symbol == 0xF0 ? 16 : ac_symbol >> 4; j += run_length; - if (j > context.current_scan.spectral_selection_end) { + if (j > scan.spectral_selection_end) { dbgln_if(JPEG_DEBUG, "Run-length exceeded boundaries. Cursor: {}, Skipping: {}!", j, run_length); return Error::from_string_literal("Run-length exceeded boundaries"); } @@ -359,7 +363,7 @@ static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock, } if (coeff_length != 0) { - i32 ac_coefficient = TRY(read_huffman_bits(context.huffman_stream, coeff_length)); + i32 ac_coefficient = TRY(read_huffman_bits(scan.huffman_stream, coeff_length)); if (ac_coefficient < (1 << (coeff_length - 1))) ac_coefficient -= (1 << coeff_length) - 1; @@ -445,20 +449,23 @@ static ErrorOr<void> decode_huffman_stream(JPEGLoadingContext& context, Vector<M for (u32 vcursor = 0; vcursor < context.mblock_meta.vcount; vcursor += context.vsample_factor) { for (u32 hcursor = 0; hcursor < context.mblock_meta.hcount; hcursor += context.hsample_factor) { u32 i = vcursor * context.mblock_meta.hpadded_count + hcursor; + + auto& huffman_stream = context.current_scan.huffman_stream; + if (context.dc_restart_interval > 0) { if (i != 0 && i % (context.dc_restart_interval * context.vsample_factor * context.hsample_factor) == 0) { reset_decoder(context); // Restart markers are stored in byte boundaries. Advance the huffman stream cursor to // the 0th bit of the next byte. - if (context.huffman_stream.byte_offset < context.huffman_stream.stream.size()) { - if (context.huffman_stream.bit_offset > 0) { - context.huffman_stream.bit_offset = 0; - context.huffman_stream.byte_offset++; + if (huffman_stream.byte_offset < huffman_stream.stream.size()) { + if (huffman_stream.bit_offset > 0) { + huffman_stream.bit_offset = 0; + huffman_stream.byte_offset++; } // Skip the restart marker (RSTn). - context.huffman_stream.byte_offset++; + huffman_stream.byte_offset++; } } } @@ -466,8 +473,8 @@ static ErrorOr<void> decode_huffman_stream(JPEGLoadingContext& context, Vector<M if (auto result = build_macroblocks(context, macroblocks, hcursor, vcursor); result.is_error()) { if constexpr (JPEG_DEBUG) { dbgln("Failed to build Macroblock {}: {}", i, result.error()); - dbgln("Huffman stream byte offset {}", context.huffman_stream.byte_offset); - dbgln("Huffman stream bit offset {}", context.huffman_stream.bit_offset); + dbgln("Huffman stream byte offset {}", huffman_stream.byte_offset); + dbgln("Huffman stream bit offset {}", huffman_stream.bit_offset); } return result.release_error(); } @@ -559,6 +566,7 @@ static ErrorOr<void> read_start_of_scan(AK::SeekableStream& stream, JPEGLoadingC u8 const component_count = TRY(stream.read_value<u8>()); Scan current_scan; + current_scan.huffman_stream.stream.ensure_capacity(50 * KiB); Optional<u8> last_read; u8 component_read = 0; @@ -1255,7 +1263,7 @@ static ErrorOr<void> parse_header(AK::SeekableStream& stream, JPEGLoadingContext VERIFY_NOT_REACHED(); } -static ErrorOr<void> scan_huffman_stream(AK::SeekableStream& stream, JPEGLoadingContext& context) +static ErrorOr<void> scan_huffman_stream(AK::SeekableStream& stream, HuffmanStreamState& huffman_stream) { u8 last_byte; u8 current_byte = TRY(stream.read_value<u8>()); @@ -1269,12 +1277,12 @@ static ErrorOr<void> scan_huffman_stream(AK::SeekableStream& stream, JPEGLoading continue; if (current_byte == 0x00) { current_byte = TRY(stream.read_value<u8>()); - context.huffman_stream.stream.append(last_byte); + huffman_stream.stream.append(last_byte); continue; } Marker marker = 0xFF00 | current_byte; if (marker >= JPEG_RST0 && marker <= JPEG_RST7) { - context.huffman_stream.stream.append(marker); + huffman_stream.stream.append(marker); current_byte = TRY(stream.read_value<u8>()); continue; } @@ -1283,7 +1291,7 @@ static ErrorOr<void> scan_huffman_stream(AK::SeekableStream& stream, JPEGLoading TRY(stream.seek(-2, AK::SeekMode::FromCurrentPosition)); return {}; } else { - context.huffman_stream.stream.append(last_byte); + huffman_stream.stream.append(last_byte); } } @@ -1328,7 +1336,7 @@ static ErrorOr<Vector<Macroblock>> construct_macroblocks(JPEGLoadingContext& con TRY(handle_miscellaneous_or_table(*context.stream, context, marker)); } else if (marker == JPEG_SOS) { TRY(read_start_of_scan(*context.stream, context)); - TRY(scan_huffman_stream(*context.stream, context)); + TRY(scan_huffman_stream(*context.stream, context.current_scan.huffman_stream)); TRY(decode_huffman_stream(context, macroblocks)); return macroblocks; } else { @@ -1357,7 +1365,6 @@ JPEGImageDecoderPlugin::JPEGImageDecoderPlugin(u8 const* data, size_t size) m_context = make<JPEGLoadingContext>(); m_context->data = data; m_context->data_size = size; - m_context->huffman_stream.stream.ensure_capacity(50 * KiB); } JPEGImageDecoderPlugin::~JPEGImageDecoderPlugin() = default; |