Search code examples
phppnggdalphaalpha-transparency

Is it possible to retrieve the alpha value of a pixel of a PNG file in the 0-255 range?


In a recent competition I was given the task to extract binary data (another PNG) from a PNG image file's alpha channel. The data was encoded in such a way that if I read the values in the alpha channel for each pixel from the top left (e.g. 80,78,71,13,10,26,10) up to a specific point then the resulting data would form another image.

Initially I tried to complete this task using PHP, but I hit a roadblock that I could not overcome. Consider the code below:

function pad($hex){
    return strlen($hex) < 2 ? "0$hex" : $hex;
}

$channel = '';

$image = 'image.png';
$ir = imagecreatefrompng($image);
imagesavealpha($ir, true);
list($width, $height) = getimagesize($image);
for ($y = 0; $y < $height; $y++){
    for ($x = 0; $x < $width; $x++){
        $pixel = imagecolorat($ir, $x, $y);
        $colors = imagecolorsforindex($ir, $pixel);
        $a = pad(dechex($colors['alpha']));
        $channel.= $a;
    }
}

After running this, I noticed that the output did not contain the PNG magic number, and I just didn't know what went wrong. After a bit of digging I found out that $colors['alpha'] only contained values less than or equal to 127. Due to the data having been encoded with a 0-255 range in mind, I could not find a way to extract it, and ended up (successfully) solving the problem with node.js instead.

So, my question is: Is there any way I could have read the PNG file's alpha channel that would have returned the values in a 0 to 255 range as opposed to 0-127, or is this a hard-coded limitation of PHP and/or GD?

For the record, I tried to use ($colors['alpha']/127)*255 in order to try and forge the value in the incorrect range to the correct one, but to no avail.


Solution

  • It is a limitation of GD. According to https://bitbucket.org/libgd/gd-libgd/issues/132/history-behind-7-bit-alpha-component, in GD source it says:

    gd has only 7 bits of alpha channel resolution, and 127 is transparent, 0 opaque. A moment of convenience, a lifetime of compatibility.

    If you install ImageMagick PHP extension, you can get the alpha value between 0-255 for a pixel (let's say with x=300 and y=490) like this:

    $image = new Imagick(__DIR__ . DIRECTORY_SEPARATOR . 'image.png');
    $x = 300;
    $y = 490;
    $pixel = $image->getImagePixelColor($x, $y);
    $colors = $pixel->getColor(true);
    echo 'Alpha value: ' . round($colors['a'] * 255, 0);
    

    ImageMagick: https://www.imagemagick.org

    ImageMagick for PHP (called Imagick): http://php.net/manual/en/book.imagick.php