Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jasper does not write cdef box for output with opactiy #392

Open
nico opened this issue Feb 3, 2025 · 2 comments
Open

Jasper does not write cdef box for output with opactiy #392

nico opened this issue Feb 3, 2025 · 2 comments

Comments

@nico
Copy link

nico commented Feb 3, 2025

The attached file is a jp2 with rgb and a channels. (I made it in Photoshop and saved it using its "Save a Copy…" feature, which under the hood seems to use kakadu.) Its cdef says that component 0 is color 1 (red), 1 is 2 (blue), 2 is 3 (green), and 3 is alpha associated with the entire image.

kakadu.zip

('jp2h')
  ('ihdr')
  - height = 101
  - width = 119
  - num_components = 4
  - are_components_signed = false
  - bits_per_component = 8
  - compression_type = 7
  - is_colorspace_unknown = 1
  - contains_intellectual_property_rights = 0
  ('colr')
  - method = 1
  - precedence = 0
  - approximation = 1
  - enumerated_color_space = 16
  ('cdef')
  - channel_index = 0
  - channel_type = 0 (color)
  - channel_association = 1
  - channel_index = 1
  - channel_type = 0 (color)
  - channel_association = 2
  - channel_index = 2
  - channel_type = 0 (color)
  - channel_association = 3
  - channel_index = 3
  - channel_type = 1 (opacity)
  - channel_association = 0

If I re-encode this image using jasper, e.g. with build/src/app/jasper --input Tests/LibGfx/test-inputs/jpeg2000/kakadu-lossless-rgba-u8-prog1-layers1-res6-mct.jp2 --output jasper-tile3x2-res5.jp2 -O tilewidth=39 -O tileheight=53 -O numrlvls=5, it doesn't write a cdef box.

I believe this is incorrect.

T.800, I.5.3.6 Channel Definition box: "If the JP2 Header box does not contain a Component Mapping box, then a reader shall map component i to channel i, for all components in the codestream."

T.800 Table 1.18 maps 1 to R, 2 to G, 3 to B, and 4 to nothing. (The spec text probably means "map component i to channel i + 1".) That is, per spec, the last component, alpha, should be ignored if there's no cdef box.

And indeed, Preview.app at least displays jasper's output as if it didn't contain an alpha channel.

jasper should write a cdef box for rgba output.

@nico
Copy link
Author

nico commented Feb 3, 2025

Here's a hack; a real fix probably would want to check if the fourth component is opacity.

diff --git a/src/libjasper/jp2/jp2_enc.c b/src/libjasper/jp2/jp2_enc.c
index 30f8a82..fb2586e 100644
--- a/src/libjasper/jp2/jp2_enc.c
+++ b/src/libjasper/jp2/jp2_enc.c
@@ -297,7 +297,7 @@ int jp2_encode(jas_image_t *image, jas_stream_t *out, const char *optstr)
        needcdef = 1;
        switch (jas_clrspc_fam(jas_image_clrspc(image))) {
        case JAS_CLRSPC_FAM_RGB:
-               if (jas_image_numcmpts(image) >= 3 &&
+               if (jas_image_numcmpts(image) == 3 &&
                  jas_image_cmpttype(image, 0) ==
                  JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R) &&
                  jas_image_cmpttype(image, 1) ==

With this, jasper produces a jp2 with a cdef box that looks right, and its output does show up with transparency in Preview.app.

nico added a commit to nico/serenity that referenced this issue Feb 3, 2025
The test image added in SerenityOS#25678 had wonky borders, due to
uclouvain/openjpeg#1575. Recreate it using jasper instead of
openjpeg, which doesn't have this problem.

jasper does have the problem that it doesn't write a cdef box,
which I think is invalid, so I built jasper with this local hack
to work around that:
jasper-software/jasper#392 (comment)

Image created using

    build/src/app/jasper --input \
        Tests/LibGfx/test-inputs/jpeg2000/kakadu-lossless-*.jp2 \
        --output jasper-tile3x2-res5.jp2 \
        -O tilewidth=39 -O tileheight=53 -O numrlvls=5

I verified that the new image still triggers the crash without the
code change in SerenityOS#25678.
@nico
Copy link
Author

nico commented Feb 3, 2025

(OpenJPEG's opj_encode does write this cdef box with a similar invocation, fwiw.)

nico added a commit to SerenityOS/serenity that referenced this issue Feb 4, 2025
The test image added in #25678 had wonky borders, due to
uclouvain/openjpeg#1575. Recreate it using jasper instead of
openjpeg, which doesn't have this problem.

jasper does have the problem that it doesn't write a cdef box,
which I think is invalid, so I built jasper with this local hack
to work around that:
jasper-software/jasper#392 (comment)

Image created using

    build/src/app/jasper --input \
        Tests/LibGfx/test-inputs/jpeg2000/kakadu-lossless-*.jp2 \
        --output jasper-tile3x2-res5.jp2 \
        -O tilewidth=39 -O tileheight=53 -O numrlvls=5

I verified that the new image still triggers the crash without the
code change in #25678.
nico added a commit to nico/serenity that referenced this issue Feb 8, 2025
Test files created by:

    for opt in lazy termall segsym vcausal pterm resetprob; do

    ../jasper-build/src/app/jasper --input \
        Tests/LibGfx/*-inputs/*2000/kakadu-lossless-rgba-u8-prog1*.jp2 \
        --output jasper-rgba-u8-cbstyle-$opt.jp2 \
        -O $opt

     done

Where jasper has this downstream patch applied:
jasper-software/jasper#392 (comment)

I then manually renamed the "lazy" one to "bypass" to match spec
language, and for all files manually inserted the code block style
bitmask in the name.

To test interactions between these, I also made a file that sets
all these options at once, by passing `-O lazy -O termal ...`.

We can't decode any of these yet.
nico added a commit to nico/serenity that referenced this issue Feb 9, 2025
Test files created by:

    for opt in lazy termall segsym vcausal pterm resetprob; do

    ../jasper-build/src/app/jasper --input \
        Tests/LibGfx/*-inputs/*2000/kakadu-lossless-rgba-u8-prog1*.jp2 \
        --output jasper-rgba-u8-cbstyle-$opt.jp2 \
        -O $opt

     done

Where jasper has this downstream patch applied:
jasper-software/jasper#392 (comment)

I then manually renamed the "lazy" one to "bypass" to match spec
language, and for all files manually inserted the code block style
bitmask in the name.

To test interactions between these, I also made a file that sets
all these options at once, by passing `-O lazy -O termall ...`.

We can't decode any of these yet.
nico added a commit to SerenityOS/serenity that referenced this issue Feb 9, 2025
Test files created by:

    for opt in lazy termall segsym vcausal pterm resetprob; do

    ../jasper-build/src/app/jasper --input \
        Tests/LibGfx/*-inputs/*2000/kakadu-lossless-rgba-u8-prog1*.jp2 \
        --output jasper-rgba-u8-cbstyle-$opt.jp2 \
        -O $opt

     done

Where jasper has this downstream patch applied:
jasper-software/jasper#392 (comment)

I then manually renamed the "lazy" one to "bypass" to match spec
language, and for all files manually inserted the code block style
bitmask in the name.

To test interactions between these, I also made a file that sets
all these options at once, by passing `-O lazy -O termall ...`.

We can't decode any of these yet.
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

No branches or pull requests

1 participant