views:

164

answers:

2

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 no gamma correction gamma corrected - no transparency

A: 

There is a problem with imagecopyresampled() and transparency. Take a look at this comment on php.net for a possible solution.

Sander Marechal
no, the transparency is preserved without any trouble when I remove the imagegammacorrect() function calls. http://stackoverflow.com/questions/313070/png-transparency-with-php/313103#313103 helped out with that.
Ty W
Ah, a challenge then! :-D See my new answer.
Sander Marechal
+1  A: 

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

Sander Marechal
interesting, I'll have to spend some time with this tomorrow when I get back to the office :)
Ty W
had a little refactoring to do in order to combine this with my existing code, but in the end it works great. Much appreciated!
Ty W