Skip to content

Commit c513123

Browse files
committed
LibGfx/JPEG2000: Support "vertically casual contexts" code block style
The additional checks take `Build/lagom/bin/image --no-output ~/Downloads/balloon.jpf` from Time (mean ± σ): 742.3 ms ± 4.7 ms to Time (mean ± σ): 777.3 ms ± 2.2 ms but we're not yet at a point where we worry about perf too much.
1 parent 761e1f5 commit c513123

File tree

3 files changed

+39
-29
lines changed

3 files changed

+39
-29
lines changed

Tests/LibGfx/TestImageDecoder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,7 @@ TEST_CASE(test_jpeg2000_decode)
696696
TEST_INPUT("jpeg2000/kakadu-lossless-rgba-u8-prog1-layers1-res6-mct.jp2"sv),
697697
TEST_INPUT("jpeg2000/openjpeg-lossless-rgba-u8-prog0-tile3x2-cblk4x16-tp3-layers3-res2-mct.jp2"sv),
698698
TEST_INPUT("jpeg2000/jasper-rgba-u8-cbstyle-02-resetprob.jp2"sv),
699+
TEST_INPUT("jpeg2000/jasper-rgba-u8-cbstyle-08-vcausal.jp2"sv),
699700
TEST_INPUT("jpeg2000/jasper-tile3x2-res5.jp2"sv),
700701
TEST_INPUT("jpeg2000/openjpeg-lossless-rgba-u8-prog0-SOP.jp2"sv),
701702
TEST_INPUT("jpeg2000/openjpeg-lossless-rgba-u8-prog0-EPH.jp2"sv),
@@ -787,7 +788,6 @@ TEST_CASE(test_jpeg2000_decode_unsupported)
787788
Array test_inputs = {
788789
TEST_INPUT("jpeg2000/jasper-rgba-u8-cbstyle-01-bypass.jp2"sv),
789790
TEST_INPUT("jpeg2000/jasper-rgba-u8-cbstyle-04-termall.jp2"sv),
790-
TEST_INPUT("jpeg2000/jasper-rgba-u8-cbstyle-08-vcausal.jp2"sv),
791791
TEST_INPUT("jpeg2000/jasper-rgba-u8-cbstyle-16-pterm.jp2"sv),
792792
TEST_INPUT("jpeg2000/jasper-rgba-u8-cbstyle-32-segsym.jp2"sv),
793793
TEST_INPUT("jpeg2000/jasper-rgba-u8-cbstyle-63-all.jp2"sv),

Userland/Libraries/LibGfx/ImageFormats/JPEG2000BitplaneDecoding.h

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace Gfx::JPEG2000 {
1515

1616
struct BitplaneDecodingOptions {
1717
bool reset_context_probabilities_each_pass { false };
18+
bool uses_vertically_causal_context { false };
1819
};
1920

2021
inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int number_of_coding_passes, ReadonlyBytes data, int M_b, int p, BitplaneDecodingOptions options = {})
@@ -87,6 +88,14 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
8788
auto strip_value = significance_and_sign[strip_offset + x];
8889
return (strip_value & (1 << strip_y)) != 0;
8990
};
91+
auto is_significant_with_y_horizon = [&](int x, int y, int y_horizon) {
92+
if (options.uses_vertically_causal_context && y >= y_horizon) {
93+
// D.7 Vertically causal context formation
94+
// "any coefficient from the next code-block scan is considered to be insignificant"
95+
return false;
96+
}
97+
return is_significant(x, y);
98+
};
9099
auto sign_is_negative = [&](int x, int y) {
91100
auto strip_index = y / 4;
92101
auto strip_y = y % 4;
@@ -120,11 +129,11 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
120129

121130
// Helper functions, mostly for computing arithmetic decoder contexts in various situations.
122131

123-
auto compute_context_ll_lh = [&](int x, int y) -> unsigned {
132+
auto compute_context_ll_lh = [&](int x, int y, int y_horizon) -> unsigned {
124133
// Table D.1 – Contexts for the significance propagation and cleanup coding passes
125134
u8 sum_h = is_significant(x - 1, y) + is_significant(x + 1, y);
126-
u8 sum_v = is_significant(x, y - 1) + is_significant(x, y + 1);
127-
u8 sum_d = is_significant(x - 1, y - 1) + is_significant(x - 1, y + 1) + is_significant(x + 1, y - 1) + is_significant(x + 1, y + 1);
135+
u8 sum_v = is_significant(x, y - 1) + is_significant_with_y_horizon(x, y + 1, y_horizon);
136+
u8 sum_d = is_significant(x - 1, y - 1) + is_significant_with_y_horizon(x - 1, y + 1, y_horizon) + is_significant(x + 1, y - 1) + is_significant_with_y_horizon(x + 1, y + 1, y_horizon);
128137

129138
if (sum_h == 2)
130139
return 8;
@@ -150,11 +159,11 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
150159
};
151160

152161
// Like compute_context_ll_lh but with sum_h and sum_v swapped
153-
auto compute_context_hl = [&](int x, int y) -> unsigned {
162+
auto compute_context_hl = [&](int x, int y, int y_horizon) -> unsigned {
154163
// Table D.1 – Contexts for the significance propagation and cleanup coding passes
155164
u8 sum_h = is_significant(x - 1, y) + is_significant(x + 1, y);
156-
u8 sum_v = is_significant(x, y - 1) + is_significant(x, y + 1);
157-
u8 sum_d = is_significant(x - 1, y - 1) + is_significant(x - 1, y + 1) + is_significant(x + 1, y - 1) + is_significant(x + 1, y + 1);
165+
u8 sum_v = is_significant(x, y - 1) + is_significant_with_y_horizon(x, y + 1, y_horizon);
166+
u8 sum_d = is_significant(x - 1, y - 1) + is_significant_with_y_horizon(x - 1, y + 1, y_horizon) + is_significant(x + 1, y - 1) + is_significant_with_y_horizon(x + 1, y + 1, y_horizon);
158167

159168
if (sum_v == 2)
160169
return 8;
@@ -179,12 +188,12 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
179188
return 0;
180189
};
181190

182-
auto compute_context_hh = [&](int x, int y) -> unsigned {
191+
auto compute_context_hh = [&](int x, int y, int y_horizon) -> unsigned {
183192
// Table D.1 – Contexts for the significance propagation and cleanup coding passes
184193
u8 sum_h = is_significant(x - 1, y) + is_significant(x + 1, y);
185-
u8 sum_v = is_significant(x, y - 1) + is_significant(x, y + 1);
194+
u8 sum_v = is_significant(x, y - 1) + is_significant_with_y_horizon(x, y + 1, y_horizon);
186195
u8 sum_h_v = sum_h + sum_v;
187-
u8 sum_d = is_significant(x - 1, y - 1) + is_significant(x - 1, y + 1) + is_significant(x + 1, y - 1) + is_significant(x + 1, y + 1);
196+
u8 sum_d = is_significant(x - 1, y - 1) + is_significant_with_y_horizon(x - 1, y + 1, y_horizon) + is_significant(x + 1, y - 1) + is_significant_with_y_horizon(x + 1, y + 1, y_horizon);
188197

189198
if (sum_d >= 3)
190199
return 8;
@@ -211,24 +220,24 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
211220
return 0;
212221
};
213222

214-
auto compute_context = [&](int x, int y) -> unsigned {
223+
auto compute_context = [&](int x, int y, int y_horizon) -> unsigned {
215224
switch (sub_band) {
216225
case SubBand::HorizontalLowpassVerticalLowpass:
217226
case SubBand::HorizontalLowpassVerticalHighpass:
218-
return compute_context_ll_lh(x, y);
227+
return compute_context_ll_lh(x, y, y_horizon);
219228
case SubBand::HorizontalHighpassVerticalLowpass:
220-
return compute_context_hl(x, y);
229+
return compute_context_hl(x, y, y_horizon);
221230
case SubBand::HorizontalHighpassVerticalHighpass:
222-
return compute_context_hh(x, y);
231+
return compute_context_hh(x, y, y_horizon);
223232
}
224233
VERIFY_NOT_REACHED();
225234
};
226235

227-
auto v_or_h_contribution = [&](IntPoint p, IntPoint d0, IntPoint d1) -> i8 {
236+
auto v_or_h_contribution = [&](IntPoint p, IntPoint d0, IntPoint d1, int y_horizon) -> i8 {
228237
auto p0 = p + d0;
229238
auto p1 = p + d1;
230239
// Table D.2 – Contributions of the vertical (and the horizontal) neighbours to the sign context
231-
if (is_significant(p1.x(), p1.y())) {
240+
if (is_significant_with_y_horizon(p1.x(), p1.y(), y_horizon)) {
232241
if (!sign_is_negative(p1.x(), p1.y())) {
233242
if (is_significant(p0.x(), p0.y()))
234243
return !sign_is_negative(p0.x(), p0.y()) ? 1 : 0;
@@ -243,13 +252,13 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
243252
return 0;
244253
};
245254

246-
auto read_sign_bit = [&](int x, int y) {
255+
auto read_sign_bit = [&](int x, int y, int y_horizon) {
247256
// C2, Decode sign bit of current coefficient
248257
// Sign bit
249258
// D.3.2 Sign bit decoding
250259
// Table D.2 – Contributions of the vertical (and the horizontal) neighbours to the sign context
251-
i8 v_contribution = v_or_h_contribution({ x, y }, { 0, -1 }, { 0, 1 });
252-
i8 h_contribution = v_or_h_contribution({ x, y }, { -1, 0 }, { 1, 0 });
260+
i8 v_contribution = v_or_h_contribution({ x, y }, { 0, -1 }, { 0, 1 }, y_horizon);
261+
i8 h_contribution = v_or_h_contribution({ x, y }, { -1, 0 }, { 1, 0 }, y_horizon);
253262
// Table D.3 – Sign contexts from the vertical and horizontal contributions
254263
u8 context_label = 9;
255264
if (h_contribution == 0)
@@ -282,7 +291,7 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
282291
// D1, Is the current coefficient significant?
283292
if (!is_significant(x, y + coefficient_index)) {
284293
// D2, Is the context bin zero? (see Table D.1)
285-
u8 context = compute_context(x, y + coefficient_index);
294+
u8 context = compute_context(x, y + coefficient_index, y + 4);
286295
if (context != 0) {
287296
// C1, Decode significance bit of current coefficient (See D.3.1)
288297
bool is_newly_significant = arithmetic_decoder.get_next_bit(all_other_contexts[context]);
@@ -295,7 +304,7 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
295304

296305
// D3, Did the current coefficient just become significant?
297306
if (is_newly_significant) {
298-
bool sign_bit = read_sign_bit(x, y + coefficient_index);
307+
bool sign_bit = read_sign_bit(x, y + coefficient_index, y + 4);
299308
set_sign(x, y + coefficient_index, sign_bit);
300309
}
301310
}
@@ -327,8 +336,8 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
327336
u8 context;
328337
if (became_significant_at_bitplane[(y + coefficient_index) * w + x] == current_bitplane - 1) {
329338
u8 sum_h = is_significant(x - 1, y + coefficient_index) + is_significant(x + 1, y + coefficient_index);
330-
u8 sum_v = is_significant(x, y + coefficient_index - 1) + is_significant(x, y + coefficient_index + 1);
331-
u8 sum_d = is_significant(x - 1, y + coefficient_index - 1) + is_significant(x - 1, y + coefficient_index + 1) + is_significant(x + 1, y + coefficient_index - 1) + is_significant(x + 1, y + coefficient_index + 1);
339+
u8 sum_v = is_significant(x, y + coefficient_index - 1) + is_significant_with_y_horizon(x, y + coefficient_index + 1, y + 4);
340+
u8 sum_d = is_significant(x - 1, y + coefficient_index - 1) + is_significant_with_y_horizon(x - 1, y + coefficient_index + 1, y + 4) + is_significant(x + 1, y + coefficient_index - 1) + is_significant_with_y_horizon(x + 1, y + coefficient_index + 1, y + 4);
332341
context = (sum_h + sum_v + sum_d) >= 1 ? 15 : 14;
333342
} else {
334343
context = 16;
@@ -355,7 +364,7 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
355364
Array<u8, 4> contexts {};
356365
int num_undecoded = 0;
357366
for (int i = 0; i < 4; ++i) {
358-
contexts[i] = compute_context(x, y + i);
367+
contexts[i] = compute_context(x, y + i, y + 4);
359368
if (!is_significant(x, y + i))
360369
++num_undecoded; // FIXME: This is probably redundant since this would imply a context being non-0.
361370
}
@@ -386,7 +395,7 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
386395
++coefficient_index;
387396

388397
// C1, Decode significance bit of current coefficient (See D.3.1)
389-
u8 context = compute_context(x, y + coefficient_index); // PERF: could use `contexts` cache (needs invalidation then).
398+
u8 context = compute_context(x, y + coefficient_index, y + 4); // PERF: could use `contexts` cache (needs invalidation then).
390399
bool is_newly_significant = arithmetic_decoder.get_next_bit(all_other_contexts[context]);
391400
is_current_coefficient_significant = is_newly_significant;
392401
set_significant(x, y + coefficient_index, is_newly_significant);
@@ -399,7 +408,7 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
399408

400409
// D3, Did the current coefficient just become significant?
401410
if (is_current_coefficient_significant) {
402-
bool sign_bit = read_sign_bit(x, y + coefficient_index);
411+
bool sign_bit = read_sign_bit(x, y + coefficient_index, y + 4);
403412
set_sign(x, y + coefficient_index, sign_bit);
404413
}
405414

@@ -422,7 +431,7 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
422431
bool has_already_been_coded = pass > 0 && was_coded_in_pass[(y + coefficient_index) * w + x] == pass - 2;
423432
if (!is_significant_or_coded && !has_already_been_coded) {
424433
// C1, Decode significance bit of current coefficient
425-
u8 context = compute_context(x, y + coefficient_index); // PERF: could use `contexts` cache (needs invalidation then).
434+
u8 context = compute_context(x, y + coefficient_index, y + 4); // PERF: could use `contexts` cache (needs invalidation then).
426435
bool is_newly_significant = arithmetic_decoder.get_next_bit(all_other_contexts[context]);
427436
set_significant(x, y + coefficient_index, is_newly_significant);
428437
if (is_newly_significant) {
@@ -432,7 +441,7 @@ inline ErrorOr<void> decode_code_block(Span2D<i16> result, SubBand sub_band, int
432441

433442
// D3, Did the current coefficient just become significant?
434443
if (is_newly_significant) {
435-
bool sign_bit = read_sign_bit(x, y + coefficient_index);
444+
bool sign_bit = read_sign_bit(x, y + coefficient_index, y + 4);
436445
set_sign(x, y + coefficient_index, sign_bit);
437446
}
438447
}

Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1474,7 +1474,7 @@ static ErrorOr<u32> read_one_packet_header(JPEG2000LoadingContext& context, Tile
14741474
u32 const current_layer_index = progression_data.layer;
14751475

14761476
// FIXME: Relax. Will need implementing D.5, D.6, D.7, and probably more.
1477-
if ((coding_parameters.code_block_style & ~2) != 0)
1477+
if ((coding_parameters.code_block_style & ~(8 | 2)) != 0)
14781478
return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Code-block style not yet implemented");
14791479

14801480
// B.10 Packet header information coding
@@ -1821,6 +1821,7 @@ static ErrorOr<void> decode_bitplanes_to_coefficients(JPEG2000LoadingContext& co
18211821
auto const& coding_style = context.coding_style_parameters_for_component(tile, component_index);
18221822
JPEG2000::BitplaneDecodingOptions bitplane_decoding_options;
18231823
bitplane_decoding_options.reset_context_probabilities_each_pass = coding_style.reset_context_probabilities();
1824+
bitplane_decoding_options.uses_vertically_causal_context = coding_style.uses_vertically_causal_context();
18241825

18251826
int M_b = compute_M_b(context, tile, component_index, sub_band_type, r, N_L);
18261827

0 commit comments

Comments
 (0)