1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 08:57:34 +00:00

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.
This commit is contained in:
Lucas CHOLLET 2023-02-22 20:16:15 -05:00 committed by Andreas Kling
parent 508ae37c6e
commit 9fa375b844

View file

@ -124,13 +124,23 @@ struct MacroblockMeta {
u32 vpadded_count { 0 }; u32 vpadded_count { 0 };
}; };
struct ComponentSpec { // In the JPEG format, components are defined first at the frame level, then
u8 id { 0 }; // referenced in each scan and aggregated with scan-specific information. The
u8 hsample_factor { 1 }; // Horizontal sampling factor. // two following structs mimic this hierarchy.
u8 vsample_factor { 1 }; // Vertical sampling factor.
u8 ac_destination_id { 0 }; struct Component {
u8 dc_destination_id { 0 }; // B.2.2 - Frame header syntax
u8 qtable_id { 0 }; // Quantization table id. 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 { struct StartOfFrame {
@ -179,6 +189,7 @@ struct ICCMultiChunkState {
struct Scan { struct Scan {
// B.2.3 - Scan header syntax // B.2.3 - Scan header syntax
Vector<ScanComponent, 3> components;
u8 spectral_selection_start {}; u8 spectral_selection_start {};
u8 spectral_selection_end {}; u8 spectral_selection_end {};
@ -205,7 +216,7 @@ struct JPEGLoadingContext {
Scan current_scan; Scan current_scan;
Vector<ComponentSpec, 3> components; Vector<Component, 3> components;
RefPtr<Gfx::Bitmap> bitmap; RefPtr<Gfx::Bitmap> bitmap;
u16 dc_restart_interval { 0 }; u16 dc_restart_interval { 0 };
HashMap<u8, HuffmanTableSpec> dc_tables; HashMap<u8, HuffmanTableSpec> dc_tables;
@ -283,9 +294,9 @@ static inline i32* get_component(Macroblock& block, unsigned component)
} }
} }
static ErrorOr<void> add_dc(JPEGLoadingContext& context, Macroblock& macroblock, ComponentSpec const& component, unsigned component_index) static ErrorOr<void> 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. // 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(context.huffman_stream, dc_table));
@ -308,9 +319,9 @@ static ErrorOr<void> add_dc(JPEGLoadingContext& context, Macroblock& macroblock,
return {}; return {};
} }
static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock, ComponentSpec const& component, unsigned component_index) static ErrorOr<void> 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); auto* select_component = get_component(macroblock, component_index);
// Compute the AC coefficients. // Compute the AC coefficients.
@ -370,22 +381,22 @@ static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock,
*/ */
static ErrorOr<void> build_macroblocks(JPEGLoadingContext& context, Vector<Macroblock>& macroblocks, u32 hcursor, u32 vcursor) static ErrorOr<void> build_macroblocks(JPEGLoadingContext& context, Vector<Macroblock>& macroblocks, u32 hcursor, u32 vcursor)
{ {
for (unsigned component_i = 0; component_i < context.components.size(); component_i++) { for (unsigned component_i = 0; component_i < context.current_scan.components.size(); component_i++) {
auto& component = context.components[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"); 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"); 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 vfactor_i = 0; vfactor_i < scan_component.component.vsample_factor; vfactor_i++) {
for (u8 hfactor_i = 0; hfactor_i < component.hsample_factor; hfactor_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); u32 mb_index = (vcursor + vfactor_i) * context.mblock_meta.hpadded_count + (hfactor_i + hcursor);
Macroblock& block = macroblocks[mb_index]; Macroblock& block = macroblocks[mb_index];
if (context.current_scan.spectral_selection_start == 0) if (context.current_scan.spectral_selection_start == 0)
TRY(add_dc(context, block, component, component_i)); TRY(add_dc(context, block, scan_component, component_i));
TRY(add_ac(context, block, component, component_i)); TRY(add_ac(context, block, scan_component, component_i));
} }
} }
} }
@ -552,6 +563,8 @@ static ErrorOr<void> read_start_of_scan(AK::SeekableStream& stream, JPEGLoadingC
return Error::from_string_literal("Unsupported number of components"); return Error::from_string_literal("Unsupported number of components");
} }
Scan current_scan;
for (auto& component : context.components) { for (auto& component : context.components) {
u8 component_id = TRY(stream.read_value<u8>()); u8 component_id = TRY(stream.read_value<u8>());
@ -562,25 +575,25 @@ static ErrorOr<void> read_start_of_scan(AK::SeekableStream& stream, JPEGLoadingC
u8 table_ids = TRY(stream.read_value<u8>()); u8 table_ids = TRY(stream.read_value<u8>());
component.dc_destination_id = table_ids >> 4; ScanComponent scan_component { component, static_cast<u8>(table_ids >> 4), static_cast<u8>(table_ids & 0x0F) };
component.ac_destination_id = table_ids & 0x0F;
if (context.dc_tables.size() != context.ac_tables.size()) { if (context.dc_tables.size() != context.ac_tables.size()) {
dbgln_if(JPEG_DEBUG, "{}: DC & AC table count mismatch!", TRY(stream.tell())); dbgln_if(JPEG_DEBUG, "{}: DC & AC table count mismatch!", TRY(stream.tell()));
return Error::from_string_literal("DC & AC table count mismatch"); return Error::from_string_literal("DC & AC table count mismatch");
} }
if (!context.dc_tables.contains(component.dc_destination_id)) { if (!context.dc_tables.contains(scan_component.dc_destination_id)) {
dbgln_if(JPEG_DEBUG, "DC table (id: {}) does not exist!", 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"); return Error::from_string_literal("DC table does not exist");
} }
if (!context.ac_tables.contains(component.ac_destination_id)) { if (!context.ac_tables.contains(scan_component.ac_destination_id)) {
dbgln_if(JPEG_DEBUG, "AC table (id: {}) does not exist!", 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"); 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<u8>()); current_scan.spectral_selection_start = TRY(stream.read_value<u8>());
current_scan.spectral_selection_end = TRY(stream.read_value<u8>()); current_scan.spectral_selection_end = TRY(stream.read_value<u8>());
@ -596,7 +609,7 @@ static ErrorOr<void> 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"); 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 {}; return {};
} }
@ -754,7 +767,7 @@ static ErrorOr<void> read_app_marker(SeekableStream& stream, JPEGLoadingContext&
return stream.discard(bytes_to_read); 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)) { 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; context.mblock_meta.hpadded_count += luma.hsample_factor == 1 ? 0 : context.mblock_meta.hcount % 2;
@ -822,7 +835,7 @@ static ErrorOr<void> read_start_of_frame(AK::SeekableStream& stream, JPEGLoading
} }
for (u8 i = 0; i < component_count; i++) { for (u8 i = 0; i < component_count; i++) {
ComponentSpec component; Component component;
component.id = TRY(stream.read_value<u8>()); component.id = TRY(stream.read_value<u8>());
u8 subsample_factors = TRY(stream.read_value<u8>()); u8 subsample_factors = TRY(stream.read_value<u8>());