php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #55034 ImageCopyResampled doesn't calculate colours properly with alpha
Submitted: 2011-06-11 15:48 UTC Modified: 2016-07-19 16:20 UTC
Votes:3
Avg. Score:3.7 ± 1.2
Reproduced:3 of 3 (100.0%)
Same Version:2 (66.7%)
Same OS:1 (33.3%)
From: php at mrphlip dot com Assigned: cmb (profile)
Status: Not a bug Package: GD related
PHP Version: 5.3.6 OS: Ubuntu Natty
Private report: No CVE-ID: None
 [2011-06-11 15:48 UTC] php at mrphlip dot com
Description:
------------
It appears that ImageCopyResampled uses a naive averaging, which isn't correct when there is an alpha channel involved. It should instead use a weighted average for the colour channel, weighting each input pixel according to its opacity (so a more opaque input pixel has more weight in the average).

This comes up particularly if you have eg a solid white shape against a solid black-but-transparent background. The original image, every pixel is either white or transparent. But shrink it down, and the object will have a thin dark halo around the edge, because of averaging those black pixels into the result. The expected result would fade from full opacity white, to half opacity white, to transparent any-colour... but the actual result fades from full opacity white, to half opacity *grey*, to transparent black.

Test script:
---------------
# create an image with an almost-transparent white pixel and an almost-opaque black pixel
$img1 = ImageCreateTrueColor(2, 1);
ImageAlphaBlending($img1, FALSE);
ImageSetPixel($img1, 0, 0, ImageColorAllocateAlpha($img1, 255, 255, 255, 0x70));
ImageSetPixel($img1, 1, 0, ImageColorAllocateAlpha($img1, 0, 0, 0, 0x10));
# scale the image down to a single pixel - make it mix the two together
$img2 = ImageCreateTrueColor(1, 1);
ImageAlphaBlending($img2, FALSE);
ImageCopyResampled($img2, $img1, 0, 0, 0, 0, 1, 1, 2, 1);
# find out what colour the resulting pixel is
$col = ImageColorAt($img2, 0, 0);
print "R: " . (($col >> 16) & 0xFF) . "<br>";
print "G: " . (($col >> 8) & 0xFF) . "<br>";
print "B: " . ($col & 0xFF) . "<br>";
print "A: " . (($col >> 24) & 0xFF) . "<br>";
# clean up
ImageDestroy($img1);
ImageDestroy($img2);

Expected result:
----------------
R: 30
G: 30
B: 30
A: 64

Each of the colour channels has been weighted in the average... the almost-transparent white pixel with a weight of 0xF (0x7F - 0x70), and the almost-opaque black pixel with a weight of 0x6F (0x7F - 0x10), giving a weighted average of (255 * 0xF + 0 * 0x6F) / (0xF + 0x6F) == 30. The alpha channel is averaged in the normal way.

Actual result:
--------------
R: 127
G: 127
B: 127
A: 64

Each channel has been averaged separately, with no regard for the alpha channel.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-06-11 15:53 UTC] php at mrphlip dot com
-Summary: ImageCopyResampled doesn't calculate alpha properly +Summary: ImageCopyResampled doesn't calculate colours properly with alpha
 [2011-06-11 15:53 UTC] php at mrphlip dot com
Changed summary to be more accurate... it calculates the alpha fine, it's the colours it doesn't calculate correctly in the presence of an alpha channel.
 [2011-06-13 06:38 UTC] php at mrphlip dot com
Hmm... I've just tried this on a different machine, and it works correctly there.

I notice that the two machines link to GD differently... on my machine (where the bug happens), I don't have ImageAntiAlias, but on the other machine (where it works), I do... which I know is related to how GD is compiled. I'm not sure whether this is related, but I figure more information can't hurt.
 [2012-04-21 11:59 UTC] php at heavyconsulting dot net
I have this problem on a new server running
Debian Squeeze with php5-gd 5.3.3-7+squeeze8.

I do not have this problem on my older server
running CentOS with php-gd-5.1.6-24.el5_4.5.

The test-script produces the expected result on the old CentOS server, and the wrong result on the new Debian server.

My application uses imagecopyresampled to rescale and place simple white images with alpha channel on top of a background image. 
This works great on the old server, but on the new one it results in a black border around the edge of the white graphic.
 [2012-04-21 12:54 UTC] php at heavyconsulting dot net
I did some more research, and the difference is due to different GD-libraries.
The Debian/Ubuntu PHP-packages uses the original GD-library, while CentOS uses 
PHPs "bundled" GD-library.

The original library is missing a lot of fixes and functions that the PHP-variant 
have. According to some Debian people, we can expect them to use the bundled 
library sometime around when "hell freezes over", or when the PHP-team pushes 
their changes and improvements to the main GD-library.

So, in other words, this is probably not a bug.
 [2012-04-21 16:17 UTC] php at heavyconsulting dot net
After some more research I found a solution that fixed this issue for me.
Using instructions found on http://drupal.org/node/878778 i was able to build a 
new php5-gd deb-package on a new virtual machine I installed with the same 
version of debian that my server uses.

Then I could simply copy the new deb-file to the server and install it.

I now have the bundled GD with all the missing functions and it handles 
alpha/colours correctly when using ImageCopyResampled, so I am now happy.

In other words, this is not a bug with PHP. It is a bug with the original shared 
GD-library, and it has been fixed in the PHP-bundled GD-library, but not all 
distributions use that version.
 [2016-07-19 16:20 UTC] cmb@php.net
-Status: Open +Status: Not a bug -Assigned To: +Assigned To: cmb
 [2016-07-19 16:20 UTC] cmb@php.net
This issue has been reported against upstream libgd[1] and will
most likely be resolved soon.

[1] <https://github.com/libgd/libgd/issues/201>
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Apr 19 19:01:28 2024 UTC