Opened 16 months ago

Last modified 6 months ago

#2454 new defect

image_filter resize is not working correctly with some PNG files.(nginx is changing background color)

Reported by: walidlaggoune159@… Owned by:
Priority: minor Milestone:
Component: nginx-module Version: 1.23.x
Keywords: image resize, image filter Cc: walidlaggoune159@…
uname -a: Linux 5fc57c61112f 5.15.0-60-generic #66-Ubuntu SMP Fri Jan 20 14:29:49 UTC 2023 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.23.2

Description

Hello, this is my configuration for image resizing

location ~ (?<width>\d+)\/storage\/(?<folder>.+)\/(?<image>.+)$ {
    alias /var/www/html/web/storage/app/public/$folder/$image;
    image_filter resize $width $width;
    image_filter_jpeg_quality 80;
    image_filter_buffer 20M;
}

before resize :
https://preview.redd.it/pysc1bturyfa1.png

After resize:
https://preview.redd.it/w2ku9mvzryfa1.png

Attachments (1)

image_filter_scale.patch (3.5 KB ) - added by jilles-sg@… 6 months ago.
proof of concept patch to use gdImageScale

Download all attachments as: .zip

Change History (7)

comment:1 by Roman Arutyunyan, 16 months ago

Just tried this with this image and everything seems to be ok, the background is just fine. Try updating libgd. Mine is: gd2 @2.3.3_2.

in reply to:  1 comment:2 by walidlaggoune159@…, 16 months ago

Replying to Roman Arutyunyan:

Just tried this with this image and everything seems to be ok, the background is just fine. Try updating libgd. Mine is: gd2 @2.3.3_2.

How i can check my libgd version ? (i am using nginx image with docker)

in reply to:  1 comment:3 by walidlaggoune159@…, 16 months ago

Replying to Roman Arutyunyan:

Just tried this with this image and everything seems to be ok, the background is just fine. Try updating libgd. Mine is: gd2 @2.3.3_2.

Finaly it worked (no yellow background), with nginx:1.23.3-alpine (before it was nginx:1.23.3).

but another bug appears :
original image : https://ibb.co/Br6rRqS
after resize : https://ibb.co/xgVhY6Q

comment:4 by Maxim Dounin, 16 months ago

It looks like starting with libgd 2.2.3, specifically this change, it is no longer possible to reset transparency with gdImageColorTransparent(-1). In particular, the corresponding color is still marked as transparent in the palette.

In libgd 2.3.1 there was an attempt to fix associated bugs, yet incomplete: the palette color is still marked as transparent after calling gdImageColorTransparent(-1).

Since nginx removes single-color transparency when resizing images (and restores it after resizing), the above behaviour breaks transparency in images using indexed colors with libgd from 2.2.3 to 2.3.0. Starting with libgd 2.3.1 things mostly work, though the mentioned issue might result in other bugs. Does not look like an nginx issue though.

As for the image from comment:3, it seems to be unrelated to the original issue as initially reported, and works identically in all libgd versions I've tested. The root cause is that the image is using black color both as a transparent color and a non-transparent color. That is, the image palette contains both rgba(0, 0, 0, 0) and rgba(0, 0, 0, 127). Since these colors are indistinguishable after removing transparency, both become transparent after resizing.

One possible solution would be to resize such images as truecolor (with proper alpha channel) and convert them back to indexed colors later, similarly to how we do with image_filter_transparency off;. Not sure it's a good solution though.

comment:5 by maxim, 14 months ago

Milestone: nginx-1.23

Ticket retargeted after milestone closed

by jilles-sg@…, 6 months ago

Attachment: image_filter_scale.patch added

proof of concept patch to use gdImageScale

comment:6 by jilles-sg@…, 6 months ago

This issue was also reported to libgd at https://github.com/libgd/libgd/pull/639 and a libgd developer said it may be better to use gdImageScale() instead of gdImageCopyResampled().

I created a proof of concept of this (patch against nginx_1.18.0-6ubuntu14.4 ubuntu package, Ubuntu 22.04). The patch adds a new configuration option image_filter_use_scale_function that makes it use gdImageScale() if enabled. This appeared to fix the issue on the one PNG image with a palette and partial transparency that I tested it against.

A side effect is that the resized image is truecolour and therefore has a larger file size than necessary. For our use, this is probably not a major issue since our current workaround is to ensure the source images are truecolour (not palette-based).

By the way, ngx_http_image_filter_module does not incorporate the configuration options into the ETag, so changes to them may not be reflected in a browser unless its cache is explicitly cleared or the image is explicitly reloaded.

Note: See TracTickets for help on using tickets.