Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP GD2: how to maintain alpha channel transparency and correct gamma

I was intrigued by this discussion of image scaling and subsequently found out that the PHP code I'm using to create thumbnails from uploaded images suffers from the same problem. I decided to try the PHP fix posted near the bottom (converting gamma from 2.2 to 1.0, resizing the image, converting gamma back from 1.0 to 2.2). This works to solve the issue noted in the article, however this modification to the code has the unfortunate side effect of knocking out PNG alpha channel transparency.

Here is the code I have with the gamma correction in place.

<?php
$image = imagecreatefrompng($source_file);
$resized_image = imagecreatetruecolor($new_width, $new_height);
imagealphablending($resized_image, false);
imagesavealpha($resized_image, true);
imagegammacorrect($image, 2.2, 1.0);
imagecopyresampled($resized_image, $image, 0, 0, 0, 0, $new_width, $new_height, $image_width, $image_height);
imagegammacorrect($resized_image, 1.0, 2.2);
imagepng($resized_image, $dest_file);
?>

Anyone know how to resize the image, employing the gamma correction trick, while maintaining the alpha channel transparency of the original image?

Edit

sample images:

  1. original file - PNG with alpha channel transparency
  2. resized file with both imagegammacorrect() function calls commented out
  3. resized file with both imagegammacorrect() function calls in place

You can see that the transparency is fine until you attempt to correct the gamma. (easiest way to see the transparency is working below is to inspect the paragraph tag wrapped around the images and add a background: black; to its style attribute via FireBug or similar.)

original image http://ender.hosting.emarketsouth.com/images/test-image.png no gamma correction http://ender.hosting.emarketsouth.com/images/test-image-resized-no-gamma.png gamma corrected - no transparency http://ender.hosting.emarketsouth.com/images/test-image-resized.png

like image 248
Ty W Avatar asked Nov 14 '22 08:11

Ty W


1 Answers

Here's some code that does work. Basically, it separates out the alpha channel, resizes the image using gamma correct, resizes the alpha channel without gamma correct, then copies over the alpha channel to the resized image that was done with gamma correct.

My guess is that the imagegammacorrect() function has a bug. Perhaps gamma only applies to RGB and GD tries to do the same calculation to the alpha channel as well? Color theory is not my forte.

Anyway, here's the code. Unfortunately I could not find a better way to separate out the channels than to loop over the pixels one-by-one. Oh well...

<?php
// Load image
$image = imagecreatefrompng('test-image.png');

// Create destination
$resized_image = imagecreatetruecolor(100, 100);
imagealphablending($resized_image, false); // Overwrite alpha
imagesavealpha($resized_image, true);

// Create a separate alpha channel
$alpha_image = imagecreatetruecolor(200, 200);
imagealphablending($alpha_image, false); // Overwrite alpha
imagesavealpha($alpha_image, true);

for ($x = 0; $x < 200; $x++) {
    for ($y = 0; $y < 200; $y++) {
        $alpha = (imagecolorat($image, $x, $y) >> 24) & 0xFF;
        $color = imagecolorallocatealpha($alpha_image, 0, 0, 0, $alpha);
        imagesetpixel($alpha_image, $x, $y, $color);
    }
}

// Resize image to destination, using gamma correction
imagegammacorrect($image, 2.2, 1.0);
imagecopyresampled($resized_image, $image, 0, 0, 0, 0, 100, 100, 200, 200);
imagegammacorrect($resized_image, 1.0, 2.2);

// Resize alpha channel
$alpha_resized_image = imagecreatetruecolor(200, 200);
imagealphablending($alpha_resized_image, false);
imagesavealpha($alpha_resized_image, true);

imagecopyresampled($alpha_resized_image, $alpha_image, 0, 0, 0, 0, 100, 100, 200, 200);

// Copy alpha channel back to resized image
for ($x = 0; $x < 100; $x++) {
    for ($y = 0; $y < 100; $y++) {
        $alpha = (imagecolorat($alpha_resized_image, $x, $y) >> 24) & 0xFF;
        $rgb = imagecolorat($resized_image, $x, $y);
        $r = ($rgb >> 16 ) & 0xFF;
        $g = ($rgb >> 8 ) & 0xFF;
        $b = $rgb & 0xFF;
        $color = imagecolorallocatealpha($resized_image, $r, $g, $b, $alpha);
        imagesetpixel($resized_image, $x, $y, $color);
    }
}

imagepng($resized_image, 'test-image-scaled.png');
?>

Replace hard-coded values with variables of course... And here's the result I get using your image and my code:

Resized image
(source: jejik.com)

like image 51
Sander Marechal Avatar answered Dec 04 '22 23:12

Sander Marechal