diff --git a/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp b/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp index 73fea189cea..43ba2340bcd 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp @@ -334,6 +334,36 @@ static ErrorOr read_coding_style_default(ReadonlyBytes data) return cod; } +// A.6.2 Coding style component (COC) +struct CodingStyleComponent { + u16 component_index { 0 }; // "Ccoc" in spec. + + // Table A.23 – Coding style parameter values for the Scoc parameter + bool has_explicit_precinct_size { false }; // "Scoc" in spec. + + CodingStyleParameters parameters; +}; + +static ErrorOr read_coding_style_component(ReadonlyBytes data, size_t number_of_components) +{ + FixedMemoryStream stream { data }; + + // Table A.22 – Coding style component parameter values + CodingStyleComponent coc; + if (number_of_components < 257) + coc.component_index = TRY(stream.read_value()); + else + coc.component_index = TRY(stream.read_value>()); + + u8 Scoc = TRY(stream.read_value()); + coc.has_explicit_precinct_size = Scoc & 1; + + dbgln_if(JPEG2000_DEBUG, "JPEG2000ImageDecoderPlugin: COC marker segment: component_index={}", coc.component_index); + coc.parameters = TRY(read_coding_style_parameters(data.slice(TRY(stream.tell())), "COC"sv, coc.has_explicit_precinct_size)); + + return coc; +} + // A.6.4 Quantization default (QCD) struct QuantizationDefault { enum QuantizationStyle { @@ -479,6 +509,7 @@ struct TilePartData { struct TileData { Optional cod; + Vector cocs; Optional qcd; Vector qccs; Vector tile_parts; @@ -502,6 +533,7 @@ struct JPEG2000LoadingContext { // Data from marker segments: ImageAndTileSize siz; CodingStyleDefault cod; + Vector cocs; QuantizationDefault qcd; Vector qccs; Vector coms; @@ -587,6 +619,8 @@ static ErrorOr parse_codestream_main_header(JPEG2000LoadingContext& contex return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple COD markers in main header"); context.cod = TRY(read_coding_style_default(marker.data.value())); saw_COD_marker = true; + } else if (marker.marker == J2K_COC) { + context.cocs.append(TRY(read_coding_style_component(marker.data.value(), context.siz.components.size()))); } else if (marker.marker == J2K_QCD) { if (saw_QCD_marker) return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple QCD markers in main header"); @@ -617,7 +651,8 @@ static ErrorOr parse_codestream_main_header(JPEG2000LoadingContext& contex [](Empty) -> size_t { VERIFY_NOT_REACHED(); }, [](Vector const& step_sizes) { return step_sizes.size(); }, [](Vector const& step_sizes) { return step_sizes.size(); }); - if (context.qcd.quantization_style != QuantizationDefault::ScalarDerived && step_sizes_count < context.cod.number_of_decomposition_levels * 3u + 1u) + // FIXME: What if number_of_decomposition_levels is in context.cocs and varies by component? + if (context.qcd.quantization_style != QuantizationDefault::ScalarDerived && step_sizes_count < context.cod.parameters.number_of_decomposition_levels * 3u + 1u) return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Not enough step sizes for number of decomposition levels"); return {}; @@ -676,6 +711,8 @@ static ErrorOr parse_codestream_tile_header(JPEG2000LoadingContext& contex if (tile.cod.has_value()) return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple COD markers in tile header"); tile.cod = TRY(read_coding_style_default(marker.data.value())); + } else if (marker.marker == J2K_COC) { + tile.cocs.append(TRY(read_coding_style_component(marker.data.value(), context.siz.components.size()))); } else if (marker.marker == J2K_QCD) { if (tile.qcd.has_value()) return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Multiple QCD markers in tile header");