1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 16:57:35 +00:00

LibGfx/JPEG: Support refinement scans

These scans are only present in progressive JPEGs and contains bits to
increase the precision of values acquired in previous scans.
This commit is contained in:
Lucas CHOLLET 2023-03-17 22:14:11 -04:00 committed by Linus Groh
parent 8806e66f66
commit fbad9a70fc

View file

@ -321,6 +321,17 @@ static inline i32* get_component(Macroblock& block, unsigned component)
}
}
static ErrorOr<void> refine_coefficient(Scan& scan, i32& coefficient)
{
// G.1.2.3 - Coding model for subsequent scans of successive approximation
// See the correction bit from rule b.
u8 const bit = TRY(read_huffman_bits(scan.huffman_stream, 1));
if (bit == 1)
coefficient |= 1 << scan.successive_approximation_low;
return {};
}
static ErrorOr<void> add_dc(JPEGLoadingContext& context, Macroblock& macroblock, ScanComponent const& scan_component)
{
auto maybe_table = context.dc_tables.get(scan_component.dc_destination_id);
@ -332,6 +343,14 @@ static ErrorOr<void> add_dc(JPEGLoadingContext& context, Macroblock& macroblock,
auto& dc_table = maybe_table.value();
auto& scan = context.current_scan;
auto* select_component = get_component(macroblock, scan_component.component.index);
auto& coefficient = select_component[0];
if (context.current_scan.successive_approximation_high > 0) {
TRY(refine_coefficient(scan, coefficient));
return {};
}
// For DC coefficients, symbol encodes the length of the coefficient.
auto dc_length = TRY(get_next_symbol(scan.huffman_stream, dc_table));
if (dc_length > 11) {
@ -346,9 +365,9 @@ static ErrorOr<void> add_dc(JPEGLoadingContext& context, Macroblock& macroblock,
if (dc_length != 0 && dc_diff < (1 << (dc_length - 1)))
dc_diff -= (1 << dc_length) - 1;
auto* select_component = get_component(macroblock, scan_component.component.index);
auto& previous_dc = context.previous_dc_values[scan_component.component.index];
select_component[0] = previous_dc += dc_diff;
previous_dc += dc_diff;
coefficient = previous_dc << scan.successive_approximation_low;
return {};
}
@ -375,6 +394,14 @@ static ErrorOr<bool> read_eob(Scan& scan, u32 symbol)
return false;
}
static bool is_progressive(StartOfFrame::FrameType frame_type)
{
return frame_type == StartOfFrame::FrameType::Progressive_DCT
|| frame_type == StartOfFrame::FrameType::Progressive_DCT_Arithmetic
|| frame_type == StartOfFrame::FrameType::Differential_Progressive_DCT
|| frame_type == StartOfFrame::FrameType::Differential_Progressive_DCT_Arithmetic;
}
static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock, ScanComponent const& scan_component)
{
auto maybe_table = context.ac_tables.get(scan_component.ac_destination_id);
@ -395,9 +422,12 @@ static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock,
u32 to_skip = 0;
Optional<u8> saved_symbol;
Optional<u8> saved_bit_for_rule_a;
bool in_zrl = false;
for (int j = first_coefficient; j <= scan.spectral_selection_end; ++j) {
auto& coefficient = select_component[zigzag_map[j]];
// 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.
@ -412,9 +442,20 @@ static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock,
to_skip++;
saved_symbol.clear();
}
if (!in_zrl && is_progressive(context.frame.type) && scan.successive_approximation_high != 0) {
// G.1.2.3 - Coding model for subsequent scans of successive approximation
// Bit sign from rule a
saved_bit_for_rule_a = TRY(read_huffman_bits(scan.huffman_stream, 1));
}
}
}
if (coefficient != 0) {
TRY(refine_coefficient(scan, coefficient));
continue;
}
if (to_skip > 0) {
--to_skip;
if (to_skip == 0)
@ -425,18 +466,31 @@ static ErrorOr<void> add_ac(JPEGLoadingContext& context, Macroblock& macroblock,
if (scan.end_of_bands_run_count > 0)
continue;
u8 coeff_length = *saved_symbol & 0x0F;
if (coeff_length > 10) {
dbgln_if(JPEG_DEBUG, "AC coefficient too long: {}!", coeff_length);
return Error::from_string_literal("AC coefficient too long");
}
if (is_progressive(context.frame.type) && scan.successive_approximation_high != 0) {
// G.1.2.3 - Coding model for subsequent scans of successive approximation
if (auto const low_bits = *saved_symbol & 0x0F; low_bits != 1) {
dbgln_if(JPEG_DEBUG, "AC coefficient low bits isn't equal to 1: {}!", low_bits);
return Error::from_string_literal("AC coefficient low bits isn't equal to 1");
}
if (coeff_length != 0) {
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;
coefficient = (*saved_bit_for_rule_a == 0 ? -1 : 1) << scan.successive_approximation_low;
saved_bit_for_rule_a.clear();
} else {
// F.1.2.2 - Huffman encoding of AC coefficients
u8 const coeff_length = *saved_symbol & 0x0F;
select_component[zigzag_map[j]] = ac_coefficient;
if (coeff_length > 10) {
dbgln_if(JPEG_DEBUG, "AC coefficient too long: {}!", coeff_length);
return Error::from_string_literal("AC coefficient too long");
}
if (coeff_length != 0) {
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;
coefficient = ac_coefficient * (1 << scan.successive_approximation_low);
}
}
saved_symbol.clear();