How to reliably detect transparency in PNG image with PHP

Published the Sunday, 28 January 2024 at 07:40

The primary need is to find out if a PNG has transparency using PHP.

All the code I found didn't seemed to work correctly for the collection of PNG I had to convert.

I finished using the following function:

function png_has_transparency($im) {
        //Retrieve content from imagick object
        $content = $im->getImageBlob();

        //Detect 32-bit png (each pixel has tranparency level)
        if (ord(substr($content, 25, 1)) & 4) {
                //Fetch iterator
                $p = $im->getPixelIterator();

                //Loop on each row
                foreach($p as $r) {
                        //Loop on each row pixel
                        foreach($r as $pix) {
                                //Check if pixel has partial transparency
                                if ($pix->getColorValue(Imagick::COLOR_ALPHA) != 1) {
                                        return true;
                                }
                        }
                }
        //Check 8-bit png transparency
        } elseif (stripos($content, 'PLTE') !== false || stripos($content, 'tRNS') !== false) {
                return true;
        }

        //Didn't found clue of transparency
        return false;
}

This function works with the only two transparency possibilities: 8 and 32-bit PNG.

The first case is a 32-bit PNG with transparency enabled, we have then to check every pixel to detect if it has transparent part or not.

The second case is a 8-bit PNG, then we only have to look the file content for transparency markers.

In this function configuration, we only read part of the file in 32-bit PNG until we detect one transparent pixel or parse content until transparency marker is detected in 8-bit PNG.

The worst case scenario will be 32-bit PNG with transparency flag without transparency or 8-bit PNG without transparency flag.

Depending on how likely you are to have transparency in each cases you might want to reverse the flow of this function.

Big thanks to these articles which expains how these parts work in a bit more detail:

Hope this helps someone else out there.