Skip to content

Fix data loss / assert when encoding tiles with empty resolution levels - #1657

Open
nico wants to merge 1 commit into
uclouvain:masterfrom
nico:fix-1575
Open

Fix data loss / assert when encoding tiles with empty resolution levels#1657
nico wants to merge 1 commit into
uclouvain:masterfrom
nico:fix-1575

Conversation

@nico

@nico nico commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

When a tile is narrow but the number of decomposition levels is large, some of its lower resolution levels can become empty. Neither 5/3 not 9/7 DWT handled zero-length inputs correctly:

  • opj_dwt_encode_and_deinterleave_h_one_row (5/3, lossless): The if (even) branch correctly checks if (width > 1), but the else branch for the odd case special-cased width == 1 and then just had an else branch on that. This caused width == 0 to be handled incorrectly. This is similar to the if (even) branch just above, and also similar to inverse opj_idwt53_h with its if (len > 1). Fixes opj_compress sometimes loses data when encoding in lossless mode #1575.

  • opj_dwt_encode_and_deinterleave_h_one_row_real (9/7, lossy): only returned early for width == 1, so for width == 0 it called opj_dwt_encode_1_real() with dn == sn == 0. This tripped the assert(dn + sn > 1) therein in debug builds. Return early for width <= 1, to handle empty tiles too.

Before this commit, reproduced with a 119x53 grayscale image: width 119 with -t 117 leaves a 2-pixel-wide right edge tile, and -n 5 makes that tile's three lowest resolution levels empty:

# lossless: cols 117-118 corrupted
opj_compress -i in.pgm -o out.jp2 -n 5 -t 117,53

# lossy: assert abort in debug builds
opj_compress -i in.pgm -o out.jp2 -n 5 -t 117,53 -I

When a tile is narrow but the number of decomposition levels is large,
some of its lower resolution levels can become empty.
Neither 5/3 not 9/7 DWT handled zero-length inputs correctly:

- opj_dwt_encode_and_deinterleave_h_one_row (5/3, lossless):
  The `if (even)` branch correctly checks `if (width > 1)`, but the else
  branch for the odd case special-cased `width == 1` and then just had an
  `else` branch on that. This caused width == 0 to be handled incorrectly.
  This is similar to the `if (even)` branch just above, and also similar
  to inverse opj_idwt53_h with its `if (len > 1)`. Fixes uclouvain#1575.

- opj_dwt_encode_and_deinterleave_h_one_row_real (9/7, lossy):
  only returned early for width == 1, so for width == 0 it called
  opj_dwt_encode_1_real() with dn == sn == 0.
  This tripped the `assert(dn + sn > 1)` therein in debug builds.
  Return early for `width <= 1`, to handle empty tiles too.

Before this commit, reproduced with a 119x53 grayscale image:
width 119 with `-t 117` leaves a 2-pixel-wide right edge tile,
and `-n 5` makes that tile's three lowest resolution levels empty:

    # lossless: cols 117-118 corrupted
    opj_compress -i in.pgm -o out.jp2 -n 5 -t 117,53

    # lossy: assert abort in debug builds
    opj_compress -i in.pgm -o out.jp2 -n 5 -t 117,53 -I
@nico

nico commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

I have a roundtrip test for this too, but I'll put it in a separate PR in case the fix is easier to merge than the test for some reason.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

opj_compress sometimes loses data when encoding in lossless mode

1 participant