【问题标题】:PHP GD Use one image to mask another image, including transparencyPHP GD 使用一张图片遮盖另一张图片,包括透明度
【发布时间】:2011-11-04 09:55:24
【问题描述】:

我正在尝试创建一个获取图像的 PHP 脚本:


http://i.stack.imgur.com/eNvlM.png

然后应用PNG图像:


http://i.stack.imgur.com/iJr2I.png

作为面具。

最终结果需要保持透明:


http://i.stack.imgur.com/u0l0I.png

如果我想在 GD 中执行此操作,ImageMagick 现在不是一个真正的选择。

我该怎么做呢?

phalacee's post (in "PHP/GD, how to copy a circle from one image to another?") 似乎是正确的,但我特别需要使用图像作为蒙版,而不是形状。

【问题讨论】:

    标签: php gd


    【解决方案1】:

    获得类似效果的另一种方法是将 png 文件粘贴到具有唯一背景颜色的新图像上以暂时去除透明度,并将 png 图像的透明颜色设置为黑色圆圈颜色。然后,当您将它放在 jpeg 图像上时,将新的透明颜色设置为蒙版的颜色。

    // Load the Black Circle PNG image
    $png = imagecreatefrompng( 'mask.png' );
    $width = imagesx( $png );
    $height = imagesy( $png );
    
    // Create a mask image
    $mask = imagecreatetruecolor( $width, $height );
    // We'll use Magenta as our new transparent colour - set it as the solid background colour.
    $magenta = imagecolorallocate( $mask, 255, 0, 255 );
    imagefill( $mask, 0, 0, $magenta );
    
    // Copy the png image onto the mask. Destroy it to free up memory.
    imagecopyresampled( $mask, $png, 0, 0, 0, 0, $width, $height, $width, $height );
    imagedestroy( $png );
    
    // Set the black portion of the mask to transparent.
    $black = imagecolorallocate( $mask, 0, 0, 0 );
    imagecolortransparent( $mask, $black );
    
    // Load JPEG image.
    $jpg = imagecreatefromjpeg( 'image.jpg' );
    $j_width = imagesx( $jpg );
    $j_height = imagesx( $jpg );
    
    // Enable alpha blending and copy the png image
    imagealphablending( $jpg, true );
    imagecopyresampled( $jpg, $mask, 0, 0, 0, 0, $j_width, $j_height, $width, $height );
    imagedestroy( $mask );
    
    // Set the new transparent colour and output new image to browser as a png.
    $magenta = imagecolorallocate( $jpg, 255, 0, 255 );
    imagecolortransparent( $jpg, $magenta );
    imagepng( $jpg );
    

    如果重采样或半透明像素让您失望,您可以禁用混合并在 $mask 图像上绘制透明形状,而不是使用 png 作为蒙版。

    // Load JPEG Image.
    $jpg = imagecreatefromjpeg( 'image.jpg' );
    $width = imagesx( $jpg );
    $height = imagesx( $jpg );
    
    // Create mask at same size with an opaque background.
    $mask = imagecreatetruecolor( $width, $height );
    $magenta = imagecolorallocate( $mask, 255, 0, 255 );
    imagefill( $mask, 0, 0, $magenta );
    
    // Disable alpha blending and draw a transparent shape onto the mask.
    $transparent = imagecolorallocatealpha( $mask, 255, 255, 255, 127 );
    imagealphablending( $mask, false );
    imagefilledellipse( $mask, round( $width / 2 ), round( $height / 2 ), $width, $height, $transparent );
    
    // Paste the mask onto the original image and set the new transparent colour.
    imagealphablending( $jpg, true );
    imagecopyresampled( $jpg, $mask, 0, 0, 0, 0, $width, $height, $width, $height );
    imagedestroy( $mask );
    $magenta = imagecolorallocate( $jpg, 255, 0, 255 );
    imagecolortransparent( $jpg, $magenta );
    
    // Output new image to browser as a png.
    imagepng( $jpg );
    

    注意:以上代码未经测试,但希望能满足您的需求。

    【讨论】:

      【解决方案2】:

      这里对该脚本进行了一点升级 - 我发现如果源图像本身具有透明度,则遮罩(使用上面的脚本)会绘制一个背面像素,而不是源图像的透明像素。下面的扩展脚本考虑了源图像的透明度,并保留了它。

      // Load source and mask
      $source = imagecreatefrompng( '1.png' );
      $mask = imagecreatefrompng( '2.png' );
      // Apply mask to source
      imagealphamask( $source, $mask );
      // Output
      header( "Content-type: image/png");
      imagepng( $source );
      
      function imagealphamask( &$picture, $mask ) {
      // Get sizes and set up new picture
      $xSize = imagesx( $picture );
      $ySize = imagesy( $picture );
      $newPicture = imagecreatetruecolor( $xSize, $ySize );
      imagesavealpha( $newPicture, true );
      imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );
      
      // Resize mask if necessary
      if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) {
          $tempPic = imagecreatetruecolor( $xSize, $ySize );
          imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) );
          imagedestroy( $mask );
          $mask = $tempPic;
      }
      
      // Perform pixel-based alpha map application
      for( $x = 0; $x < $xSize; $x++ ) {
          for( $y = 0; $y < $ySize; $y++ ) {
              $alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );
      
                  if(($alpha['red'] == 0) && ($alpha['green'] == 0) && ($alpha['blue'] == 0) && ($alpha['alpha'] == 0))
                  {
                      // It's a black part of the mask
                      imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Stick a black, but totally transparent, pixel in.
                  }
                  else
                  {
      
                      // Check the alpha state of the corresponding pixel of the image we're dealing with.    
                      $alphaSource = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) );
      
                      if(($alphaSource['alpha'] == 127))
                      {
                          imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Stick a black, but totally transparent, pixel in.
                      } 
                      else
                      {
                          $color = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) );
                          imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $color['alpha'] ) ); // Stick the pixel from the source image in
                      }
      
      
                  }
          }
      }
      
      // Copy back to original picture
      imagedestroy( $picture );
      $picture = $newPicture;
      }
      

      【讨论】:

        【解决方案3】:

        有一个名为 WideImage 的库,它支持 alpha 掩码 http://wideimage.sourceforge.net/documentation/manipulating-images/

        【讨论】:

          【解决方案4】:
          for ($y = 0; $y < $ySize; $y++) {
            $alpha = imagecolorsforindex($mask, imagecolorat($mask, $x, $y));
            $alpha = 127 - floor($alpha['red'] / 2);
            if (127 == $alpha) {
              continue;
            }
            $color = imagecolorsforindex($picture, imagecolorat($picture, $x, $y));
            imagesetpixel($newPicture, $x, $y, imagecolorallocatealpha(
              $newPicture, $color['red'], $color['green'], $color['blue'], $alpha));
          }
          

          这里是第一个功能的小升级。由于您已经拥有透明图像,因此无需复制蒙版像素。这对执行会有一点帮助。

          【讨论】:

            【解决方案5】:

            我喜欢你的脚本,当像素完全透明时删除额外的颜色信息是个好主意。如果有人想使用这种方法,我应该指出一个小错误(IMO)。

            $color = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) );
            

            应该是

            $color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
            

            如果像素是 100% 透明的,我也不是 100% 确定你为什么要在这里检查 rgb 值

            if(($alpha['red'] == 0) && ($alpha['green'] == 0) && ($alpha['blue'] == 0) && ($alpha['alpha'] == 0))
            ...
            

            我不确定掩码文件中的 alpha 混合是否适用于您的方法,因为它仅在 rgba 值都等于 0 时使用。

            Jules 的脚本也很不错,尽管它希望蒙版是蒙版的灰度表示(这是很常见的做法)。

            在 Matt 的查询中,他正在寻找一个脚本,该脚本仅从现有图像中获取 alpha 透明度并将其应用于另一个图像。这是 Jules 脚本的一个简单 mod,只是为了从掩码图像中获取 alpha,并保留源图像的 alpha。

            <?php
            // Load source and mask
            $source = imagecreatefrompng( '1.png' );
            $mask = imagecreatefrompng( '2.png' );
            // Apply mask to source
            imagealphamask( $source, $mask );
            // Output
            header( "Content-type: image/png");
            imagepng( $source );
            
            function imagealphamask( &$picture, $mask ) {
                // Get sizes and set up new picture
                $xSize = imagesx( $picture );
                $ySize = imagesy( $picture );
                $newPicture = imagecreatetruecolor( $xSize, $ySize );
                imagesavealpha( $newPicture, true );
                imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );
            
                // Resize mask if necessary
                if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) {
                    $tempPic = imagecreatetruecolor( $xSize, $ySize );
                    imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) );
                    imagedestroy( $mask );
                    $mask = $tempPic;
                }
            
                // Perform pixel-based alpha map application
                for( $x = 0; $x < $xSize; $x++ ) {
                    for( $y = 0; $y < $ySize; $y++ ) {
                        $alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );
                        //small mod to extract alpha, if using a black(transparent) and white
                        //mask file instead change the following line back to Jules's original:
                        //$alpha = 127 - floor($alpha['red'] / 2);
                        //or a white(transparent) and black mask file:
                        //$alpha = floor($alpha['red'] / 2);
                        $alpha = $alpha['alpha'];
                        $color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
                        //preserve alpha by comparing the two values
                        if ($color['alpha'] > $alpha)
                            $alpha = $color['alpha'];
                        //kill data for fully transparent pixels
                        if ($alpha == 127) {
                            $color['red'] = 0;
                            $color['blue'] = 0;
                            $color['green'] = 0;
                        }
                        imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $alpha ) );
                    }
                }
            
                // Copy back to original picture
                imagedestroy( $picture );
                $picture = $newPicture;
            }
            
            ?>
            

            【讨论】:

              【解决方案6】:

              马特,

              如果您使用黑色背景上的椭圆形白色填充而不是透明背景上的黑色填充来制作 png,则以下函数会执行此操作。

              <?php
              // Load source and mask
              $source = imagecreatefrompng( '1.png' );
              $mask = imagecreatefrompng( '2.png' );
              // Apply mask to source
              imagealphamask( $source, $mask );
              // Output
              header( "Content-type: image/png");
              imagepng( $source );
              
              function imagealphamask( &$picture, $mask ) {
                  // Get sizes and set up new picture
                  $xSize = imagesx( $picture );
                  $ySize = imagesy( $picture );
                  $newPicture = imagecreatetruecolor( $xSize, $ySize );
                  imagesavealpha( $newPicture, true );
                  imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );
              
                  // Resize mask if necessary
                  if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) {
                      $tempPic = imagecreatetruecolor( $xSize, $ySize );
                      imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) );
                      imagedestroy( $mask );
                      $mask = $tempPic;
                  }
              
                  // Perform pixel-based alpha map application
                  for( $x = 0; $x < $xSize; $x++ ) {
                      for( $y = 0; $y < $ySize; $y++ ) {
                          $alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );
                          $alpha = 127 - floor( $alpha[ 'red' ] / 2 );
                          $color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
                          imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $alpha ) );
                      }
                  }
              
                  // Copy back to original picture
                  imagedestroy( $picture );
                  $picture = $newPicture;
              }
              
              ?>
              

              【讨论】:

              • 谢谢,这真的很有帮助
              • 我知道这篇文章已经关闭了一段时间,但是当你们运行这个脚本时运行时是什么样的?平均需要 20 秒左右...我的源/蒙版图像是 250 x 170 像素...这就是你们得到的吗?
              • 忽略,不知道我做错了什么,但现在效果很好:P 谢谢大家!
              • $alpha = 127 - floor( $alpha[ 'red' ] / 2 ); $color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) ); 行应替换为$color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) ); $alpha = 127 - floor((127-$color['alpha']) * ($alpha[ 'red' ]/255));,以防止丢失源图像的 Alpha 通道。
              • @Jules_Text 你能告诉我你为什么在这一行使用red 索引:$alpha = 127 - floor( $alpha[ 'red' ] / 2 ); 而不是greenblue 索引?
              猜你喜欢
              • 1970-01-01
              • 2023-03-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多