Menu

#90 Image corruption with invalid transparency

v1.0 (example)
open
None
1
2024-03-06
2024-01-25
Andrew
No

Tested with current v0.7.8 Win32 binary. Using: optipng.exe -q -i0 -zc1 -zm8 -zs3 -f0 -force "%~1" (but issue will occur with most conversions / typical parameters).
When getting transparency chunk via libpng, validity is not tested before converting color type. Here, the RGB transparency index does not appear in the palette.
This can lead to image corruption. See attached.

2 Attachments

Discussion

  • Andrew

    Andrew - 2024-01-25

    I have highlighted the defective pixel (that becomes full transparent), to red for clarity - attached.

     
  • Andrew

    Andrew - 2024-01-26

    I started to debug this before, but lost my notes...

    I observe the issue with non-paletted images (RGB/A), that have a tRNS chunk that specifies an RGB value not present in an image. May affect grayscale images too (G/A) ?
    When the image is optimised as indexed, the transparency is wrongly applied to the palette index.
    I think the issue is in the use of libpng png_get_tRNS…?

    “…For non-paletted images, the function retrieves the single color value which is treated as fully transparent. If the transparency information is valid, i.e. PNG_INFO_tRNS bit is set for info_ptr >valid: * trans shall be set to the transparency values for a paletted image. Values for the data shall be in range [0,255], ranging from fully transparent to fully opaque, respectively. * num_trans shall be set to the number of transparency values * trans_values shall be set to the single color value specified for non-paletted images. …”

     

    Last edit: Andrew 2024-01-26
  • Cosmin Truta

    Cosmin Truta - 2024-02-29

    Andrew, I tried to reproduce the corruption that you're seeing, but I couldn't. I tried it both on Windows and Linux, and both with the embedded libpng and with the system libpng. If you could please take a look at my command line and let me know what I am missing.

    optipng.exe -i0 -zc1 -zm8 -zs3 -f0 -force actions.png -out out.png
    

    The resulting file out.png is identical to actions.png.

    And then I tried the same but with actions-broken.png:

    optipng.exe -i0 -zc1 -zm8 -zs3 -f0 -force actions-broken.png -out out.png
    

    The resulting file out.png is not identical to actions-broken.png, but the image content is the same. I can even see the red pixel, which is the difference in transparency that you mentioned, but that is NOT the output of optipng, from what I'm seeing:

    <html>
    <body bgcolor="red">
    <img src="actions.png">
    &nbsp;
    <img src="actions-broken.png">
    &nbsp;
    <img src="out.png">
    </body>
    </html>
    

    My conclusion, from my own experiments, is that actions-broken.png must have come from some other place, because I could not break actions.png by using optipng in the way that you reported.

     
  • Cosmin Truta

    Cosmin Truta - 2024-03-01
    • assigned_to: Cosmin Truta
    • Priority: 2 --> 1
     
  • Cosmin Truta

    Cosmin Truta - 2024-03-01

    Please ignore my previous message. I must have done something in which I did not manipulate the test images correctly, before running my tests; but after a full reset of my test environment, now I confirm that you bug report is 100% legit.

     
    👍
    1
  • Andrew

    Andrew - 2024-03-02

    Example 2

    To aid debugging, this test case is perhaps more obvious?
    See attached 'toolbar.png'. The image is RGB with a tRNS chunk defining RGB(0,0,1) as transparent. Critically, this colour doesn't exist in the image, so there should be no transparency. When viewing, the image has a bright green background. Details are also confirmed with TweakPNG.

    Image is optimized on Win32 with Batch command line optipng.exe -v -f0 -force "%~1" -out "%~1.out.png",

    producing:

    OptiPNG version 0.7.8
    Copyright (C) 2001-2023 Cosmin Truta and the Contributing Authors.
    
    ** Processing: P:\toolbar.png
    246x34 pixels, 3x8 bits/pixel, RGB+transparency
    Reducing image to 8 bits/pixel, 62 colors (1 transparent) in palette
    Input IDAT size = 2770 bytes
    Input file size = 2845 bytes
    
    Trying:
      zc = 9  zm = 8  zs = 0  f = 0         IDAT size = 1139
      zc = 9  zm = 8  zs = 1  f = 0         IDAT too big
      zc = 1  zm = 8  zs = 2  f = 0         IDAT too big
      zc = 9  zm = 8  zs = 3  f = 0         IDAT too big
    
    Selecting parameters:
      zc = 9  zm = 8  zs = 0  f = 0         IDAT size = 1139
    
    Output file: P:\toolbar.png.out.png
    
    Output IDAT size = 1139 bytes (1631 bytes decrease)
    Output file size = 1407 bytes (1438 bytes = 50.54% decrease)
    
    ** Status report
    1 file(s) have been processed.
    

    The resulting output image is now corrupted. Viewing the image, we no longer have a green background- it has become transparent. During optimization, the RGB image was reduced to indexed (correctly). As the transparent colour was never present in the image, the first palette entry is selected (in error!) as being transparent. This can be confirmed with TweakPNG. This may also occur with greyscale + transparency - but is untested. If the original image is edited, so that the tRNS chunk references a present colour (e.g. magenta RGB(255,0,255)...), everything functions correctly.

    Expected behaviour is no transparency is set, when the defined transparent colour is not present.

     
  • Cosmin Truta

    Cosmin Truta - 2024-03-06

    This patch should fix the problem, but I'd like to take another day or two so that I can see what other changes I could make to the image reduction code. For example: opportunities to remove tRNS from grayscale images exists, just as with RGB images, but they are ignored.

    Another thing that I want to point out is that this kind of fix ought to be backported to older OptiPNG versions also, e.g. to give a chance to downstream package maintainers to apply the fix even if they don't plan on upgrading to the latest version.

     

Log in to post a comment.