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

Jpeg compressed tiff: jpeg decoder should handle the conversion from YCbCr to RGB #2124

Merged
merged 18 commits into from
May 26, 2022

Conversation

brianpopow
Copy link
Collaborator

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

This PR provides a fix for #2123: If the image uses Jpeg Compression, the jpeg decoder should handle the conversion from YCbCr to RGB and not the Tiff Decoder, See discussion in #2123

Sorry, something went wrong.

@JimBobSquarePants
Copy link
Member

@brianpopow You're not gonna like this but since it's such a critical bug we'll have to backport this PR to the release/2.1.x branch.

@brianpopow brianpopow changed the title Jpeg compressed tiff: decoder should handle the conversion from YCbCr to RGB WIP: Jpeg compressed tiff: decoder should handle the conversion from YCbCr to RGB May 21, 2022
@JimBobSquarePants
Copy link
Member

JimBobSquarePants commented May 22, 2022

@brianpopow This looks to be how libjpeg-turbo figures it out.

https://github.com/libjpeg-turbo/libjpeg-turbo/blob/1f55ae7b0fa3acc348a630171617d0e56d922b68/jdapimin.c#L127

@brianpopow
Copy link
Collaborator Author

I think it is working now as it should. I had to change how the jpeg decoder deduces the colorspace. That's why this should be reviewed carefully.

It is close to how libjpeg does it, but not exactly the same. libjpeg checks for a JFIF marker first and then concludes it should be YCbCr. We first check for adobe with a color transform and if the component Id's are ASCII R, G, B. The reason for that is, that there is a test image Tiff\rgb_jpegcompressed_nojpegtable.tiff, which has a JFIF marker, but the component Id's indicate it's in fact RGB.

@br3aker if you have time to review this part, your feedback would be very much appreciated.

@brianpopow brianpopow changed the title WIP: Jpeg compressed tiff: decoder should handle the conversion from YCbCr to RGB Jpeg compressed tiff: decoder should handle the conversion from YCbCr to RGB May 22, 2022
@br3aker
Copy link
Contributor

br3aker commented May 22, 2022

Ups, completely missed this, will review tomorrow!

Copy link
Contributor

@br3aker br3aker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking, oracle docs are not accurate according to jpeg specs. We can have 2 different 'versions' for the jpeg image - jfif and adobe jfif (exif of whatever it's called). So we can have 2 different paths:

  1. Have jfif marker
  2. Have app14 adobe marker

But we can't have both at the same time according to the specs. I'd suggest to write it like this:

if (componentCount == 1)
{
    return grayscale;
}

if (componentCount == 3)
{
    // we prioritize adobe marker over jfif marker
    // if somebody really encoded this image with redundant adobe marker
    // then it's most likely an adobe jfif image IMO
    if (this.adobeMarker is not null)
    {
        if(this.adobeMarker.Transform == ycbcr) return ycbcr;
        else if(this.adobeMarker.Transform == unknown) return rgb;

        // throw if color transform flag has invalid value?
    }

    if (this.jfifMarker is not null)
    {
        return ycbcr;
    }

    // fallback to the id color deduction
    return this.Components[2].Id == 66 && this.Components[1].Id == 71 && this.Components[0].Id == 82 ? rgb : ycbcr;
}

if (componentCount == 4)
{
    // jfif images don't not support 4 component images
    // so we only check adobe
    if (this.adobeMarker is not null)
    {
        if(this.adobeMarker.Transform == ycck) return ycck;
        else if(this.adobeMarker.Transform == unknown) return cmyk;

        // throw if color transform flag has invalid value?
    }

    // fallback to cmyk as neither of cmyk nor ycck have 'special' component ids
    return cmyk;
}

JpegThrowHelper.ThrowNotSupportedComponentCount(componentCount);
return default;

P.S.
I personally don't like component id color deduction as ids are used to determine components in scans, they have nothing to do with encoding color. AFAIR 'RGB' ASCII ids were proposed by Adobe before jpeg extension via APP14 marker was published.

@brianpopow brianpopow changed the title Jpeg compressed tiff: decoder should handle the conversion from YCbCr to RGB Jpeg compressed tiff: jpeg decoder should handle the conversion from YCbCr to RGB May 23, 2022
@brianpopow
Copy link
Collaborator Author

@br3aker thanks for your feedback, I have changed the color deduction accordingly.

For Tiff\rgb_jpegcompressed_nojpegtable.tiff: I am pretty sure this was wrongly encoded (by myself probably). The jpeg data was not following the spec. This is changed now.

@brianpopow brianpopow requested a review from a team May 23, 2022 13:21
@JimBobSquarePants
Copy link
Member

If @br3aker is happy with this then I am 😄

@brianpopow I'm gonna create a PR using these files against the release/2.1.x branch and once it's built will merge both.

@brianpopow
Copy link
Collaborator Author

If @br3aker is happy with this then I am 😄

@brianpopow I'm gonna create a PR using these files against the release/2.1.x branch and once it's built will merge both.

Thanks. I was a bit unsure how to do it, never made a release.

JimBobSquarePants added a commit that referenced this pull request May 25, 2022
@antonfirsov
Copy link
Member

I was a bit unsure how to do it, never made a release.

After merging this PR, we should rebase all the commits in this PR on release/2.1.x and do a separate "backport" PR. We may also consider squashing them into a single commit, if that makes things simpler.

@JimBobSquarePants I'm not sure what happens after merging the backport PR, is it enough to just tag the merge commit with 2.1.2 or are there any manual steps you have to perform? How does MinVer figure out that our next patch=2 in the package version?

@JimBobSquarePants JimBobSquarePants mentioned this pull request May 25, 2022
4 tasks
@antonfirsov
Copy link
Member

@JimBobSquarePants is faster doing than me talking 😆

@JimBobSquarePants
Copy link
Member

Haha 😄 Yeah, See #2126

It should be enough to PR against the release branch then create a tagged release via the UI here ensuring we are pointing at the correct branch to create the release from.

@JimBobSquarePants JimBobSquarePants merged commit 0eb411c into main May 26, 2022
@JimBobSquarePants JimBobSquarePants deleted the bp/Issue2123 branch May 26, 2022 06:00
@brianpopow brianpopow mentioned this pull request Jul 14, 2022
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants