mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-22 09:21:57 -05:00
LibGfx/JPEG2000: Add LRCP and RLCP progression iterators
This commit is contained in:
parent
e99fe94cfb
commit
801d7e2ced
5 changed files with 254 additions and 0 deletions
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {};
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue