LibGfx/JPEG2000: Add LRCP and RLCP progression iterators

This commit is contained in:
Nico Weber 2025-01-05 15:57:10 -05:00
parent e99fe94cfb
commit 801d7e2ced
5 changed files with 254 additions and 0 deletions

View file

@ -80,6 +80,7 @@ shared_library("LibGfx") {
"ImageFormats/ImageDecoder.cpp",
"ImageFormats/JBIG2Loader.cpp",
"ImageFormats/JPEG2000Loader.cpp",
"ImageFormats/JPEG2000ProgressionIterators.cpp",
"ImageFormats/JPEGLoader.cpp",
"ImageFormats/JPEGWriter.cpp",
"ImageFormats/JPEGXLEntropyDecoder.cpp",

View file

@ -15,6 +15,7 @@
#include <LibGfx/ImageFormats/ImageDecoder.h>
#include <LibGfx/ImageFormats/JBIG2Loader.h>
#include <LibGfx/ImageFormats/JPEG2000Loader.h>
#include <LibGfx/ImageFormats/JPEG2000ProgressionIterators.h>
#include <LibGfx/ImageFormats/JPEGLoader.h>
#include <LibGfx/ImageFormats/JPEGXLLoader.h>
#include <LibGfx/ImageFormats/PAMLoader.h>
@ -632,6 +633,77 @@ TEST_CASE(test_jpeg2000_gray)
EXPECT_EQ(icc_bytes->size(), 912u);
}
TEST_CASE(test_jpeg2000_progression_iterators)
{
{
int const layer_count = 2;
int const max_number_of_decomposition_levels = 2;
int const component_count = 2;
auto precinct_count = [](int, int) { return 1; };
Gfx::JPEG2000::LayerResolutionLevelComponentPositionProgressionIterator iterator { layer_count, max_number_of_decomposition_levels, component_count, move(precinct_count) };
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 0, .resolution_level = 0, .component = 0, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 0, .resolution_level = 0, .component = 1, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 0, .resolution_level = 1, .component = 0, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 0, .resolution_level = 1, .component = 1, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 0, .resolution_level = 2, .component = 0, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 0, .resolution_level = 2, .component = 1, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 1, .resolution_level = 0, .component = 0, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 1, .resolution_level = 0, .component = 1, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 1, .resolution_level = 1, .component = 0, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 1, .resolution_level = 1, .component = 1, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 1, .resolution_level = 2, .component = 0, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 1, .resolution_level = 2, .component = 1, .precinct = 0 }));
EXPECT(!iterator.has_next());
}
{
int const layer_count = 2;
int const max_number_of_decomposition_levels = 2;
int const component_count = 2;
auto precinct_count = [](int, int) { return 1; };
Gfx::JPEG2000::ResolutionLevelLayerComponentPositionProgressionIterator iterator { layer_count, max_number_of_decomposition_levels, component_count, move(precinct_count) };
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 0, .resolution_level = 0, .component = 0, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 0, .resolution_level = 0, .component = 1, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 1, .resolution_level = 0, .component = 0, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 1, .resolution_level = 0, .component = 1, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 0, .resolution_level = 1, .component = 0, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 0, .resolution_level = 1, .component = 1, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 1, .resolution_level = 1, .component = 0, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 1, .resolution_level = 1, .component = 1, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 0, .resolution_level = 2, .component = 0, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 0, .resolution_level = 2, .component = 1, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 1, .resolution_level = 2, .component = 0, .precinct = 0 }));
EXPECT(iterator.has_next());
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = 1, .resolution_level = 2, .component = 1, .precinct = 0 }));
EXPECT(!iterator.has_next());
}
}
TEST_CASE(test_jpeg2000_tag_tree)
{
{

View file

@ -54,6 +54,7 @@ set(SOURCES
ImageFormats/ISOBMFF/Reader.cpp
ImageFormats/JBIG2Loader.cpp
ImageFormats/JPEG2000Loader.cpp
ImageFormats/JPEG2000ProgressionIterators.cpp
ImageFormats/JPEGLoader.cpp
ImageFormats/JPEGXLEntropyDecoder.cpp
ImageFormats/JPEGXLICC.cpp

View file

@ -0,0 +1,121 @@
/*
* Copyright (c) 2025, Nico Weber <thakis@chromium.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/ImageFormats/JPEG2000ProgressionIterators.h>
namespace Gfx::JPEG2000 {
LayerResolutionLevelComponentPositionProgressionIterator::LayerResolutionLevelComponentPositionProgressionIterator(int layer_count, int max_number_of_decomposition_levels, int component_count, Function<int(int resolution_level, int component)> precinct_count)
: m_precinct_count(move(precinct_count))
{
m_end.layer = layer_count;
m_end.resolution_level = max_number_of_decomposition_levels + 1;
m_end.component = component_count;
m_end.precinct = m_precinct_count(m_next.resolution_level, m_next.component);
}
bool LayerResolutionLevelComponentPositionProgressionIterator::has_next() const
{
return m_next != ProgressionData { m_end.layer, 0, 0, 0 };
}
ProgressionData LayerResolutionLevelComponentPositionProgressionIterator::next()
{
ProgressionData current_data = m_next;
// B.12.1.1 Layer-resolution level-component-position progression
// "for each l = 0,..., L 1
// for each r = 0,..., Nmax
// for each i = 0,..., Csiz 1
// for each k = 0,..., numprecincts 1
// packet for component i, resolution level r, layer l, and precinct k.
// Here, L is the number of layers and Nmax is the maximum number of decomposition levels, N_L, used in any component of the tile."
// FIXME: This always iterates up to Nmax, instead of just N_l of each component. That means several of the iteration results will be invalid and skipped.
// (This is a performance issue, not a correctness issue.)
++m_next.precinct;
if (m_next.precinct < m_end.precinct)
return current_data;
m_next.precinct = 0;
++m_next.component;
if (m_next.component < m_end.component) {
m_end.precinct = m_precinct_count(m_next.resolution_level, m_next.component);
return current_data;
}
m_next.component = 0;
++m_next.resolution_level;
if (m_next.resolution_level < m_end.resolution_level) {
m_end.precinct = m_precinct_count(m_next.resolution_level, m_next.component);
return current_data;
}
m_next.resolution_level = 0;
m_end.precinct = m_precinct_count(m_next.resolution_level, m_next.component);
++m_next.layer;
VERIFY(m_next.layer < m_end.layer || !has_next());
return current_data;
}
ResolutionLevelLayerComponentPositionProgressionIterator::ResolutionLevelLayerComponentPositionProgressionIterator(int layer_count, int max_number_of_decomposition_levels, int component_count, Function<int(int resolution_level, int component)> precinct_count)
: m_precinct_count(move(precinct_count))
{
m_end.layer = layer_count;
m_end.resolution_level = max_number_of_decomposition_levels + 1;
m_end.component = component_count;
m_end.precinct = m_precinct_count(m_next.resolution_level, m_next.component);
}
bool ResolutionLevelLayerComponentPositionProgressionIterator::has_next() const
{
return m_next != ProgressionData { 0, m_end.resolution_level, 0, 0 };
}
ProgressionData ResolutionLevelLayerComponentPositionProgressionIterator::next()
{
ProgressionData current_data = m_next;
// B.12.1.2 Resolution level-layer-component-position progression
// "for each r = 0,..., Nmax
// for each l = 0,..., L 1
// for each i = 0,..., Csiz 1
// for each k = 0,..., numprecincts 1
// packet for component i, resolution level r, layer l, and precinct k."
// FIXME: This always iterates up to Nmax, instead of just N_l of each component. That means several of the iteration results will be invalid and skipped.
// (This is a performance issue, not a correctness issue.)
++m_next.precinct;
if (m_next.precinct < m_end.precinct)
return current_data;
m_next.precinct = 0;
++m_next.component;
if (m_next.component < m_end.component) {
m_end.precinct = m_precinct_count(m_next.resolution_level, m_next.component);
return current_data;
}
m_next.component = 0;
++m_next.layer;
if (m_next.layer < m_end.layer) {
m_end.precinct = m_precinct_count(m_next.resolution_level, m_next.component);
return current_data;
}
m_next.layer = 0;
++m_next.resolution_level;
if (has_next())
m_end.precinct = m_precinct_count(m_next.resolution_level, m_next.component);
VERIFY(m_next.resolution_level < m_end.resolution_level || !has_next());
return current_data;
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2025, Nico Weber <thakis@chromium.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Function.h>
namespace Gfx::JPEG2000 {
// B.12 Progression order
struct ProgressionData {
int layer { 0 };
int resolution_level { 0 };
int component { 0 };
int precinct { 0 };
bool operator==(ProgressionData const&) const = default;
};
class ProgressionIterator {
public:
virtual ~ProgressionIterator() = default;
virtual bool has_next() const = 0;
virtual ProgressionData next() = 0;
};
// B.12.1.1 Layer-resolution level-component-position progression
class LayerResolutionLevelComponentPositionProgressionIterator : public ProgressionIterator {
public:
// FIXME: Supporting POC packets will probably require changes to this.
LayerResolutionLevelComponentPositionProgressionIterator(int layer_count, int max_number_of_decomposition_levels, int component_count, Function<int(int resolution_level, int component)> precinct_count);
virtual bool has_next() const override;
virtual ProgressionData next() override;
private:
Function<int(int resolution_level, int component)> m_precinct_count;
ProgressionData m_next {};
ProgressionData m_end {};
};
// B.12.1.2 Resolution level-layer-component-position progression
class ResolutionLevelLayerComponentPositionProgressionIterator : public ProgressionIterator {
public:
// FIXME: Supporting POC packets will probably require changes to this.
ResolutionLevelLayerComponentPositionProgressionIterator(int layer_count, int max_number_of_decomposition_levels, int component_count, Function<int(int resolution_level, int component)> precinct_count);
virtual bool has_next() const override;
virtual ProgressionData next() override;
private:
Function<int(int resolution_level, int component)> m_precinct_count;
ProgressionData m_next {};
ProgressionData m_end {};
};
}