开发者

How to check if a PNG image has transparency using GD?

How do I check开发者_开发技巧 if a PNG image has transparent pixels using PHP's GD extension?


I know this is old, but I just found this on the comments of the PHP docs. (link)

Here is the function which determines whether the PNG image contains alpha or not:

<?php
    function is_alpha_png($fn){
      return (ord(@file_get_contents($fn, NULL, NULL, 25, 1)) == 6);
    }
?>

The color type of PNG image is stored at byte offset 25. Possible values of that 25'th byte is:

  • 0 - greyscale
  • 2 - RGB
  • 3 - RGB with palette
  • 4 - greyscale + alpha
  • 6 - RGB + alpha

Only works for PNG images though.


It doesn't look like you can detect transparency at a glance.

The comments on the imagecolorat manual page suggest that the resulting integer when working with a true-color image can actually be shifted four times total, with the fourth being the alpha channel (the other three being red, green and blue). Therefore, given any pixel location at $x and $y, you can detect alpha using:

$rgba = imagecolorat($im,$x,$y);
$alpha = ($rgba & 0x7F000000) >> 24;
$red = ($rgba & 0xFF0000) >> 16;
$green = ($rgba & 0x00FF00) >> 8;
$blue = ($rgba & 0x0000FF);

An $alpha of 127 is apparently completely transparent, while zero is completely opaque.

Unfortunately you might need to process every single pixel in the image just to find one that is transparent, and then this only works with true-color images. Otherwise imagecolorat returns a color index, which you must then look up using imagecolorsforindex, which actually returns an array with an alpha value.


I know this is an old thread, but in my opinion it needs improvement since walking through a huge png by checking all pixels only to find out it is not transparent is a waste of time. So after some googleing I found Jon Fox's Blog and I improved his code with the help of the W3C PNG Specification further to be reliable, fast and have a minimum on memory imprint:

function IsTransparentPng($File){
    //32-bit pngs
    //4 checks for greyscale + alpha and RGB + alpha
    if ((ord(file_get_contents($File, false, null, 25, 1)) & 4)>0){
        return true;
    }
    //8 bit pngs
    $fd=fopen($File, 'r');
    $continue=true;
    $plte=false;
    $trns=false;
    $idat=false;
    while($continue===true){
        $continue=false;
        $line=fread($fd, 1024);
        if ($plte===false){
            $plte=(stripos($line, 'PLTE')!==false);
        }
        if ($trns===false){
            $trns=(stripos($line, 'tRNS')!==false);
        }
        if ($idat===false){
            $idat=(stripos($line, 'IDAT')!==false);
        }
        if ($idat===false and !($plte===true and $trns===true)){
            $continue=true;
        }
    }
    fclose($fd);
    return ($plte===true and $trns===true);
}


It can be done!

I've combined all answers and comments into one function which should be fast & reliable:

function hasAlpha($imgdata) {
    $w = imagesx($imgdata);
    $h = imagesy($imgdata);

    if($w>50 || $h>50){ //resize the image to save processing if larger than 50px:
        $thumb = imagecreatetruecolor(10, 10);
        imagealphablending($thumb, FALSE);
        imagecopyresized( $thumb, $imgdata, 0, 0, 0, 0, 10, 10, $w, $h );
        $imgdata = $thumb;
        $w = imagesx($imgdata);
        $h = imagesy($imgdata);
    }
    //run through pixels until transparent pixel is found:
    for($i = 0; $i<$w; $i++) {
        for($j = 0; $j < $h; $j++) {
            $rgba = imagecolorat($imgdata, $i, $j);
            if(($rgba & 0x7F000000) >> 24) return true;
        }
    }
    return false;
}

//SAMPLE USE:
hasAlpha( imagecreatefrompng("myfile.png") );   //returns true if img has transparency


Pretty strait forward function, it will check if there is any transparent pixel in the image, if it is, it will return true.

$im = imagecreatefrompng('./transparent.png');
if(check_transparent($im)) {
    echo 'DA';
}
else {
    echo 'NU';
}

function check_transparent($im) {

    $width = imagesx($im); // Get the width of the image
    $height = imagesy($im); // Get the height of the image

    // We run the image pixel by pixel and as soon as we find a transparent pixel we stop and return true.
    for($i = 0; $i < $width; $i++) {
        for($j = 0; $j < $height; $j++) {
            $rgba = imagecolorat($im, $i, $j);
            if(($rgba & 0x7F000000) >> 24) {
                return true;
            }
        }
    }

    // If we dont find any pixel the function will return false.
    return false;
}


This is how I detect 8-32 bit transparency. It only work with PNG's.

function detect_transparency($file){

    if(!@getimagesize($file)) return false;

    if(ord(file_get_contents($file, false, null, 25, 1)) & 4) return true;

    $content = file_get_contents($file);
    if(stripos($content,'PLTE') !== false && stripos($content, 'tRNS') !== false) return true;

    return false;
}


cronoklee's function is very good, but when I was using it I found a bug. It does not work for images with 8 bit pallet. Here is the fixed variant:

public function hasAlpha($imgdata)
{
    $w = imagesx($imgdata);
    $h = imagesy($imgdata);

    if($w>100 || $h>100){ //resize the image to save processing
        $thumb = imagecreatetruecolor(100, 100);
        imagealphablending($thumb, FALSE);
        imagecopyresized( $thumb, $imgdata, 0, 0, 0, 0, 100, 100, $w, $h );
        $imgdata = $thumb;
        $w = imagesx($imgdata);
        $h = imagesy($imgdata);
    }
    //run through pixels until transparent pixel is found:
    for($i = 0; $i<$w; $i++) {
        for($j = 0; $j < $h; $j++) {
            $ci = imagecolorat($imgdata, $i, $j);
            $rgba = imagecolorsforindex($imgdata, $ci);
            if($rgba['alpha']) { return true; }
        }
    }
    return false;
}


Improved cronoklee's function. Removed unnecessary bit shifting for each pixel, reduced false negatives count, added explanation in function description.

/**
 * Estimates, if image has pixels with transparency. It shrinks image to 64 times smaller
 * size, if necessary, and searches for the first pixel with non-zero alpha byte.
 * If image has 1% opacity, it will be detected. If any block of 8x8 pixels has at least
 * one semi-opaque pixel, the block will trigger positive result. There are still cases,
 * where image with hardly noticeable transparency will be reported as non-transparent,
 * but it's almost always safe to fill such image with monotonic background.
 *
 * Icons with size <= 64x64 (or having square <= 4096 pixels) are fully scanned with
 * absolutely reliable result.
 *
 * @param  resource $image
 * @return bool
 */
function hasTransparency ($image): bool {
  if (!is_resource($image)) {
    throw new \InvalidArgumentException("Image resource expected. Got: " . gettype($image));
  }

  $shrinkFactor      = 64.0;
  $minSquareToShrink = 64.0 * 64.0;

  $width  = imagesx($image);
  $height = imagesy($image);
  $square = $width * $height;

  if ($square <= $minSquareToShrink) {
    [$thumb, $thumbWidth, $thumbHeight] = [$image, $width, $height];
  } else {
    $thumbSquare = $square / $shrinkFactor;
    $thumbWidth  = (int) round($width / sqrt($shrinkFactor));
    $thumbWidth < 1 and $thumbWidth = 1;
    $thumbHeight = (int) round($thumbSquare / $thumbWidth);
    $thumb       = imagecreatetruecolor($thumbWidth, $thumbHeight);
    imagealphablending($thumb, false);
    imagecopyresized($thumb, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $width, $height);
  }

  for ($i = 0; $i < $thumbWidth; $i++) { 
    for ($j = 0; $j < $thumbHeight; $j++) {
      if (imagecolorat($thumb, $i, $j) & 0x7F000000) {
        return true;
      }
    }
  }

  return false;
}

Usage:

hasTransparency( imagecreatefrompng("myfile.png") );   //returns true if img has transparency
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜