Skip to content

Commit 286e016

Browse files
committedMar 19, 2025
LibGfx/JPEG2000: Implement support for progression order 4
Very similar to the implementation of progression orders 2 and 3. With this, we support all progression orders :^)
1 parent 5df0e95 commit 286e016

File tree

4 files changed

+158
-5
lines changed

4 files changed

+158
-5
lines changed
 

‎Tests/LibGfx/TestImageDecoder.cpp

+34-1
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,7 @@ TEST_CASE(test_jpeg2000_decode)
708708
TEST_INPUT("jpeg2000/openjpeg-lossless-rgba-u8-prog1-tile4x2-cblk4x16-tp3-layers3-res2.jp2"sv),
709709
TEST_INPUT("jpeg2000/openjpeg-lossless-rgba-u8-prog2-tile4x2-cblk4x16-tp3-layers3-res2.jp2"sv),
710710
TEST_INPUT("jpeg2000/openjpeg-lossless-rgba-u8-prog3-tile4x2-cblk4x16-tp3-layers3-res2.jp2"sv),
711+
TEST_INPUT("jpeg2000/openjpeg-lossless-rgba-u8-prog4-tile4x2-cblk4x16-tp3-layers3-res2.jp2"sv),
711712
TEST_INPUT("jpeg2000/jasper-rgba-u8-cbstyle-01-bypass.jp2"sv),
712713
TEST_INPUT("jpeg2000/jasper-rgba-u8-cbstyle-01-bypass-layers.jp2"sv),
713714
TEST_INPUT("jpeg2000/jasper-rgba-u8-cbstyle-01-bypass-finer-layers.jp2"sv),
@@ -947,7 +948,6 @@ TEST_CASE(test_jpeg2000_decode_unsupported)
947948
TEST_INPUT("jpeg2000/openjpeg-lossless-RGN.jp2"sv),
948949
TEST_INPUT("jpeg2000/openjpeg-lossless-bgra-u8.jp2"sv),
949950
TEST_INPUT("jpeg2000/openjpeg-lossless-rgba-u8-prog0-tile-part-index-overflow.jp2"sv),
950-
TEST_INPUT("jpeg2000/openjpeg-lossless-rgba-u8-prog4-tile4x2-cblk4x16-tp3-layers3-res2.jp2"sv),
951951

952952
// FIXME: See FIXME in JPEG2000ColorSpecificationBox::read_from_stream() for lab.
953953
// TEST_INPUT("jpeg2000/kakadu-lossless-lab-u8-prog1-layers1-res6.jp2"sv),
@@ -1114,6 +1114,39 @@ TEST_CASE(test_jpeg2000_progression_iterators)
11141114
}
11151115
EXPECT(!iterator.has_next());
11161116
}
1117+
1118+
{
1119+
int const layer_count = 2;
1120+
int const max_number_of_decomposition_levels = 2;
1121+
int const component_count = 4;
1122+
int const precinct_count_number = 5;
1123+
auto precinct_count = [](int, int) { return precinct_count_number; };
1124+
1125+
Gfx::IntRect tile_rect { 0, 0, 5 * 32, 32 };
1126+
auto XRsiz = [&](size_t) { return 1; };
1127+
auto YRsiz = [&](size_t) { return 1; };
1128+
1129+
auto PPx = [&](int r, int) { return 5 - (max_number_of_decomposition_levels - r); };
1130+
auto PPy = [&](int r, int) { return 5 - (max_number_of_decomposition_levels - r); };
1131+
auto N_L = [&](int) { return max_number_of_decomposition_levels; };
1132+
auto num_precincts_wide = [&](int, int) { return precinct_count_number; };
1133+
auto ll_rect = [&](int r, int) {
1134+
return tile_rect / (1 << (max_number_of_decomposition_levels - r));
1135+
};
1136+
Gfx::JPEG2000::ComponentPositionResolutionLevelLayerProgressionIterator iterator {
1137+
layer_count, component_count, move(precinct_count),
1138+
move(XRsiz), move(YRsiz), move(PPx), move(PPy), move(N_L), move(num_precincts_wide), tile_rect, move(ll_rect)
1139+
};
1140+
1141+
for (int component = 0; component < component_count; ++component)
1142+
for (int precinct = 0; precinct < precinct_count_number; ++precinct)
1143+
for (int resolution_level = 0; resolution_level <= max_number_of_decomposition_levels; ++resolution_level)
1144+
for (int layer = 0; layer < layer_count; ++layer) {
1145+
EXPECT(iterator.has_next());
1146+
EXPECT_EQ(iterator.next(), (Gfx::JPEG2000::ProgressionData { .layer = layer, .resolution_level = resolution_level, .component = component, .precinct = precinct }));
1147+
}
1148+
EXPECT(!iterator.has_next());
1149+
}
11171150
}
11181151

11191152
TEST_CASE(test_jpeg2000_tag_tree)

‎Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -1625,7 +1625,8 @@ static ErrorOr<void> compute_decoding_metadata(JPEG2000LoadingContext& context)
16251625
case CodingStyleDefault::ResolutionLayerComponentPosition:
16261626
return make<JPEG2000::ResolutionLevelLayerComponentPositionProgressionIterator>(number_of_layers, max_number_of_decomposition_levels, context.siz.components.size(), move(number_of_precincts_from_resolution_level_and_component));
16271627
case CodingStyleDefault::ResolutionPositionComponentLayer:
1628-
case CodingStyleDefault::PositionComponentResolutionLayer: {
1628+
case CodingStyleDefault::PositionComponentResolutionLayer:
1629+
case CodingStyleDefault::ComponentPositionResolutionLayer: {
16291630
auto XRsiz = [&](size_t i) { return context.siz.components[i].horizontal_separation; };
16301631
auto YRsiz = [&](size_t i) { return context.siz.components[i].vertical_separation; };
16311632

@@ -1658,12 +1659,14 @@ static ErrorOr<void> compute_decoding_metadata(JPEG2000LoadingContext& context)
16581659
return make<JPEG2000::ResolutionLevelPositionComponentLayerProgressionIterator>(
16591660
number_of_layers, max_number_of_decomposition_levels, context.siz.components.size(), move(number_of_precincts_from_resolution_level_and_component),
16601661
move(XRsiz), move(YRsiz), move(PPx), move(PPy), move(N_L), move(num_precincts_wide), tile.rect, move(ll_rect));
1661-
return make<JPEG2000::PositionComponentResolutionLevelLayerProgressionIterator>(
1662+
if (tile.cod.value_or(context.cod).progression_order == CodingStyleDefault::PositionComponentResolutionLayer)
1663+
return make<JPEG2000::PositionComponentResolutionLevelLayerProgressionIterator>(
1664+
number_of_layers, context.siz.components.size(), move(number_of_precincts_from_resolution_level_and_component),
1665+
move(XRsiz), move(YRsiz), move(PPx), move(PPy), move(N_L), move(num_precincts_wide), tile.rect, move(ll_rect));
1666+
return make<JPEG2000::ComponentPositionResolutionLevelLayerProgressionIterator>(
16621667
number_of_layers, context.siz.components.size(), move(number_of_precincts_from_resolution_level_and_component),
16631668
move(XRsiz), move(YRsiz), move(PPx), move(PPy), move(N_L), move(num_precincts_wide), tile.rect, move(ll_rect));
16641669
}
1665-
case CodingStyleDefault::ComponentPositionResolutionLayer:
1666-
return Error::from_string_literal("JPEG2000Loader: ComponentPositionResolutionLayer progression order not yet supported");
16671670
}
16681671
VERIFY_NOT_REACHED();
16691672
};

‎Userland/Libraries/LibGfx/ImageFormats/JPEG2000ProgressionIterators.cpp

+85
Original file line numberDiff line numberDiff line change
@@ -261,4 +261,89 @@ SyncGenerator<ProgressionData> PositionComponentResolutionLevelLayerProgressionI
261261
}
262262
}
263263

264+
ComponentPositionResolutionLevelLayerProgressionIterator::ComponentPositionResolutionLevelLayerProgressionIterator(
265+
int layer_count, int component_count, Function<int(int resolution_level, int component)> precinct_count,
266+
Function<int(int component)> XRsiz, Function<int(int component)> YRsiz,
267+
Function<int(int resolution_level, int component)> PPx, Function<int(int resolution_level, int component)> PPy,
268+
Function<int(int component)> N_L,
269+
Function<int(int resolution_level, int component)> num_precincts_wide,
270+
Gfx::IntRect tile_rect,
271+
Function<IntRect(int resolution_level, int component)> ll_rect)
272+
: m_layer_count(layer_count)
273+
, m_component_count(component_count)
274+
, m_precinct_count(move(precinct_count))
275+
, m_XRsiz(move(XRsiz))
276+
, m_YRsiz(move(YRsiz))
277+
, m_PPx(move(PPx))
278+
, m_PPy(move(PPy))
279+
, m_N_L(move(N_L))
280+
, m_num_precincts_wide(move(num_precincts_wide))
281+
, m_tile_rect(tile_rect)
282+
, m_ll_rect(move(ll_rect))
283+
, m_generator(generator())
284+
{
285+
m_next = m_generator.next();
286+
}
287+
288+
bool ComponentPositionResolutionLevelLayerProgressionIterator::has_next() const
289+
{
290+
return m_next.has_value();
291+
}
292+
293+
ProgressionData ComponentPositionResolutionLevelLayerProgressionIterator::next()
294+
{
295+
auto result = m_next;
296+
m_next = m_generator.next();
297+
return result.value();
298+
}
299+
300+
SyncGenerator<ProgressionData> ComponentPositionResolutionLevelLayerProgressionIterator::generator()
301+
{
302+
auto compute_precinct = [&](int x, int y, int r, int i) {
303+
// (B-20)
304+
auto const trx0 = m_ll_rect(r, i).left();
305+
auto const try0 = m_ll_rect(r, i).top();
306+
auto const x_offset = floor_div(ceil_div(x, m_XRsiz(i) * (1 << (m_N_L(i) - r))), 1 << m_PPx(r, i)) - floor_div(trx0, 1 << m_PPx(r, i));
307+
auto const y_offset = floor_div(ceil_div(y, m_YRsiz(i) * (1 << (m_N_L(i) - r))), 1 << m_PPy(r, i)) - floor_div(try0, 1 << m_PPy(r, i));
308+
return x_offset + m_num_precincts_wide(r, i) * y_offset;
309+
};
310+
// B.12.1.5 Component-position-resolution level-layer progression
311+
// "for each i = 0,..., Csiz – 1
312+
// for each y = ty0,..., ty1 – 1,
313+
// for each x = tx0,..., tx1 – 1,
314+
// for each r = 0,..., NL where NL is the number of decomposition levels for component i,
315+
// if ((y divisible by YRsiz(i) * 2 ** (PPy(r, i) + N_L(i) - r) OR
316+
// ((y == ty0) AND (try0 * 2 ** (N_L(i) - r) NOT divisible by 2 ** (PPy(r, i) + N_L(i) - r))))
317+
// if ((x divisible by XRsiz(i) * 2 ** (PPx(r, i) + N_L(i) - r) OR
318+
// ((x == tx0) AND (trx0 * 2 ** (N_L(i) - r) NOT divisible by 2 ** (PPx(r, i) + N_L(i) - r))))
319+
// for the next precinct, k, if one exists, in the sequence shown in Figure B.8
320+
// for each l = 0,..., L – 1
321+
// packet for component i, resolution level r, layer l, and precinct k."
322+
// The motivation for this loop is to walk corresponding precincts in different resolution levels at the same time,
323+
// even if the resolution levels have different precinct counts.
324+
auto const tx0 = m_tile_rect.left();
325+
auto const ty0 = m_tile_rect.top();
326+
for (int i = 0; i < m_component_count; ++i) {
327+
for (int y = ty0; y < m_tile_rect.bottom(); ++y) {
328+
for (int x = tx0; x < m_tile_rect.right(); ++x) {
329+
for (int r = 0; r <= m_N_L(i); ++r) {
330+
auto const trx0 = m_ll_rect(r, i).left();
331+
auto const try0 = m_ll_rect(r, i).top();
332+
if ((y % (m_YRsiz(i) * (1 << (m_PPy(r, i) + m_N_L(i) - r))) == 0)
333+
|| ((y == ty0) && (try0 * (1 << (m_N_L(i) - r)) % (1 << (m_PPy(r, i) + m_N_L(i) - r)) != 0))) {
334+
if ((x % (m_XRsiz(i) * (1 << (m_PPx(r, i) + m_N_L(i) - r))) == 0)
335+
|| ((x == tx0) && (trx0 * (1 << (m_N_L(i) - r)) % (1 << (m_PPx(r, i) + m_N_L(i) - r)) != 0))) {
336+
if (int k = compute_precinct(x, y, r, i); k < m_precinct_count(r, i)) {
337+
for (int l = 0; l < m_layer_count; ++l) {
338+
co_yield ProgressionData { l, r, i, k };
339+
}
340+
}
341+
}
342+
}
343+
}
344+
}
345+
}
346+
}
347+
}
348+
264349
}

‎Userland/Libraries/LibGfx/ImageFormats/JPEG2000ProgressionIterators.h

+32
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,38 @@ class PositionComponentResolutionLevelLayerProgressionIterator : public Progress
134134
SyncGenerator<ProgressionData> m_generator;
135135
};
136136

137+
// B.12.1.5 Component-position-resolution level-layer progression
138+
class ComponentPositionResolutionLevelLayerProgressionIterator : public ProgressionIterator {
139+
public:
140+
// FIXME: Supporting POC packets will probably require changes to this.
141+
ComponentPositionResolutionLevelLayerProgressionIterator(int layer_count, int component_count, Function<int(int resolution_level, int component)> precinct_count,
142+
Function<int(int component)> XRsiz, Function<int(int component)> YRsiz,
143+
Function<int(int resolution_level, int component)> PPx, Function<int(int resolution_level, int component)> PPy,
144+
Function<int(int component)> N_L,
145+
Function<int(int resolution_level, int component)> num_precincts_wide,
146+
Gfx::IntRect tile_rect,
147+
Function<IntRect(int resolution_level, int component)> ll_rect);
148+
virtual bool has_next() const override;
149+
virtual ProgressionData next() override;
150+
151+
private:
152+
SyncGenerator<ProgressionData> generator();
153+
154+
Optional<ProgressionData> m_next;
155+
int m_layer_count { 0 };
156+
int m_component_count { 0 };
157+
Function<int(int resolution_level, int component)> m_precinct_count;
158+
Function<int(int component)> m_XRsiz;
159+
Function<int(int component)> m_YRsiz;
160+
Function<int(int resolution_level, int component)> m_PPx;
161+
Function<int(int resolution_level, int component)> m_PPy;
162+
Function<int(int component)> m_N_L;
163+
Function<int(int resolution_level, int component)> m_num_precincts_wide;
164+
Gfx::IntRect m_tile_rect;
165+
Function<IntRect(int resolution_level, int component)> m_ll_rect;
166+
SyncGenerator<ProgressionData> m_generator;
167+
};
168+
137169
}
138170

139171
namespace AK {

0 commit comments

Comments
 (0)