From 9fa375b844465918626624d5bf9e4e33510cfd8c Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Wed, 22 Feb 2023 20:16:15 -0500 Subject: LibGfx: Differentiate scan-level and frame-level data for components This patch brings us closer to the spec point of view. And while it makes no functional changes, it reduces the number of places where you can misuse scan-specific data and improve support for multiple scans. --- Userland/Libraries/LibGfx/JPEGLoader.cpp | 73 +++++++++++++++++++------------- 1 file changed, 43 insertions(+), 30 deletions(-) (limited to 'Userland') diff --git a/Userland/Libraries/LibGfx/JPEGLoader.cpp b/Userland/Libraries/LibGfx/JPEGLoader.cpp index 7b62a80311..ee3e67f897 100644 --- a/Userland/Libraries/LibGfx/JPEGLoader.cpp +++ b/Userland/Libraries/LibGfx/JPEGLoader.cpp @@ -124,13 +124,23 @@ struct MacroblockMeta { u32 vpadded_count { 0 }; }; -struct ComponentSpec { - u8 id { 0 }; - u8 hsample_factor { 1 }; // Horizontal sampling factor. - u8 vsample_factor { 1 }; // Vertical sampling factor. - u8 ac_destination_id { 0 }; - u8 dc_destination_id { 0 }; - u8 qtable_id { 0 }; // Quantization table id. +// In the JPEG format, components are defined first at the frame level, then +// referenced in each scan and aggregated with scan-specific information. The +// two following structs mimic this hierarchy. + +struct Component { + // B.2.2 - Frame header syntax + u8 id { 0 }; // Ci, Component identifier + u8 hsample_factor { 1 }; // Hi, Horizontal sampling factor + u8 vsample_factor { 1 }; // Vi, Vertical sampling factor + u8 qtable_id { 0 }; // Tqi, Quantization table destination selector +}; + +struct ScanComponent { + // B.2.3 - Scan header syntax + Component& component; + u8 dc_destination_id { 0 }; // Tdj, DC entropy coding table destination selector + u8 ac_destination_id { 0 }; // Taj, AC entropy coding table destination selector }; struct StartOfFrame { @@ -179,6 +189,7 @@ struct ICCMultiChunkState { struct Scan { // B.2.3 - Scan header syntax + Vector components; u8 spectral_selection_start {}; u8 spectral_selection_end {}; @@ -205,7 +216,7 @@ struct JPEGLoadingContext { Scan current_scan; - Vector components; + Vector components; RefPtr bitmap; u16 dc_restart_interval { 0 }; HashMap dc_tables; @@ -283,9 +294,9 @@ static inline i32* get_component(Macroblock& block, unsigned component) } } -static ErrorOr add_dc(JPEGLoadingContext& context, Macroblock& macroblock, ComponentSpec const& component, unsigned component_index) +static ErrorOr add_dc(JPEGLoadingContext& context, Macroblock& macroblock, ScanComponent const& scan_component, unsigned component_index) { - auto& dc_table = context.dc_tables.find(component.dc_destination_id)->value; + auto& dc_table = context.dc_tables.find(scan_component.dc_destination_id)->value; // For DC coefficients, symbol encodes the length of the coefficient. auto dc_length = TRY(get_next_symbol(context.huffman_stream, dc_table)); @@ -308,9 +319,9 @@ static ErrorOr add_dc(JPEGLoadingContext& context, Macroblock& macroblock, return {}; } -static ErrorOr add_ac(JPEGLoadingContext& context, Macroblock& macroblock, ComponentSpec const& component, unsigned component_index) +static ErrorOr add_ac(JPEGLoadingContext& context, Macroblock& macroblock, ScanComponent const& scan_component, unsigned component_index) { - auto& ac_table = context.ac_tables.find(component.ac_destination_id)->value; + auto& ac_table = context.ac_tables.find(scan_component.ac_destination_id)->value; auto* select_component = get_component(macroblock, component_index); // Compute the AC coefficients. @@ -370,22 +381,22 @@ static ErrorOr add_ac(JPEGLoadingContext& context, Macroblock& macroblock, */ static ErrorOr build_macroblocks(JPEGLoadingContext& context, Vector& macroblocks, u32 hcursor, u32 vcursor) { - for (unsigned component_i = 0; component_i < context.components.size(); component_i++) { - auto& component = context.components[component_i]; + for (unsigned component_i = 0; component_i < context.current_scan.components.size(); component_i++) { + auto& scan_component = context.current_scan.components[component_i]; - if (component.dc_destination_id >= context.dc_tables.size()) + if (scan_component.dc_destination_id >= context.dc_tables.size()) return Error::from_string_literal("DC destination ID is greater than number of DC tables"); - if (component.ac_destination_id >= context.ac_tables.size()) + if (scan_component.ac_destination_id >= context.ac_tables.size()) return Error::from_string_literal("AC destination ID is greater than number of AC tables"); - for (u8 vfactor_i = 0; vfactor_i < component.vsample_factor; vfactor_i++) { - for (u8 hfactor_i = 0; hfactor_i < component.hsample_factor; hfactor_i++) { + for (u8 vfactor_i = 0; vfactor_i < scan_component.component.vsample_factor; vfactor_i++) { + for (u8 hfactor_i = 0; hfactor_i < scan_component.component.hsample_factor; hfactor_i++) { u32 mb_index = (vcursor + vfactor_i) * context.mblock_meta.hpadded_count + (hfactor_i + hcursor); Macroblock& block = macroblocks[mb_index]; if (context.current_scan.spectral_selection_start == 0) - TRY(add_dc(context, block, component, component_i)); - TRY(add_ac(context, block, component, component_i)); + TRY(add_dc(context, block, scan_component, component_i)); + TRY(add_ac(context, block, scan_component, component_i)); } } } @@ -552,6 +563,8 @@ static ErrorOr read_start_of_scan(AK::SeekableStream& stream, JPEGLoadingC return Error::from_string_literal("Unsupported number of components"); } + Scan current_scan; + for (auto& component : context.components) { u8 component_id = TRY(stream.read_value()); @@ -562,25 +575,25 @@ static ErrorOr read_start_of_scan(AK::SeekableStream& stream, JPEGLoadingC u8 table_ids = TRY(stream.read_value()); - component.dc_destination_id = table_ids >> 4; - component.ac_destination_id = table_ids & 0x0F; + ScanComponent scan_component { component, static_cast(table_ids >> 4), static_cast(table_ids & 0x0F) }; if (context.dc_tables.size() != context.ac_tables.size()) { dbgln_if(JPEG_DEBUG, "{}: DC & AC table count mismatch!", TRY(stream.tell())); return Error::from_string_literal("DC & AC table count mismatch"); } - if (!context.dc_tables.contains(component.dc_destination_id)) { - dbgln_if(JPEG_DEBUG, "DC table (id: {}) does not exist!", component.dc_destination_id); + if (!context.dc_tables.contains(scan_component.dc_destination_id)) { + dbgln_if(JPEG_DEBUG, "DC table (id: {}) does not exist!", scan_component.dc_destination_id); return Error::from_string_literal("DC table does not exist"); } - if (!context.ac_tables.contains(component.ac_destination_id)) { - dbgln_if(JPEG_DEBUG, "AC table (id: {}) does not exist!", component.ac_destination_id); + if (!context.ac_tables.contains(scan_component.ac_destination_id)) { + dbgln_if(JPEG_DEBUG, "AC table (id: {}) does not exist!", scan_component.ac_destination_id); return Error::from_string_literal("AC table does not exist"); } + + current_scan.components.append(scan_component); } - Scan current_scan; current_scan.spectral_selection_start = TRY(stream.read_value()); current_scan.spectral_selection_end = TRY(stream.read_value()); @@ -596,7 +609,7 @@ static ErrorOr read_start_of_scan(AK::SeekableStream& stream, JPEGLoadingC return Error::from_string_literal("Spectral selection is not [0,63] or successive approximation is not null"); } - context.current_scan = current_scan; + context.current_scan = move(current_scan); return {}; } @@ -754,7 +767,7 @@ static ErrorOr read_app_marker(SeekableStream& stream, JPEGLoadingContext& return stream.discard(bytes_to_read); } -static inline bool validate_luma_and_modify_context(ComponentSpec const& luma, JPEGLoadingContext& context) +static inline bool validate_luma_and_modify_context(Component const& luma, JPEGLoadingContext& context) { if ((luma.hsample_factor == 1 || luma.hsample_factor == 2) && (luma.vsample_factor == 1 || luma.vsample_factor == 2)) { context.mblock_meta.hpadded_count += luma.hsample_factor == 1 ? 0 : context.mblock_meta.hcount % 2; @@ -822,7 +835,7 @@ static ErrorOr read_start_of_frame(AK::SeekableStream& stream, JPEGLoading } for (u8 i = 0; i < component_count; i++) { - ComponentSpec component; + Component component; component.id = TRY(stream.read_value()); u8 subsample_factors = TRY(stream.read_value()); -- cgit v1.2.3