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.
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