使用GD PHP在PNG图像上添加水印时出现部分黑色背景

4
我整合了一个PHP类,使用PHP的GD函数执行各种与图像相关的功能。它适用于所有类型的图像。旋转、翻转、调整大小、裁剪和在较小程度上添加水印都工作得很好。除了最后一个之外,其他所有功能都完美运行。例如,在进行一些更改后,旋转的PNG图像保留了它们的透明度,而以前它们会失去这个并且背景变成黑色,这是一个常见的问题,但现在所有问题都已经解决。然而,我仍然遇到困难的是将一个PNG图像与另一个PNG图像添加水印。它似乎对JPG和其他图像可以正常工作。这是简化后的代码:
public function writeWatermarkSimple()
{
    $watermarkFile = 'watermark.png';
    $watermarkImage = imagecreatefrompng($watermarkFile);

    imagealphablending($watermarkImage, false);
    imagesavealpha($watermarkImage, true);

    $imageFile = 'image.png';
    $baseImage = imagecreatefrompng($imageFile);

    imagealphablending($baseImage, false);
    imagesavealpha($baseImage, true);

    $marginH = imagesx($baseImage) - imagesx($watermarkImage);
    $marginV = imagesy($baseImage) - imagesy($watermarkImage);

    $cut = imagecreatetruecolor(imagesx($watermarkImage), imagesy($watermarkImage));
    imagecopy($cut, $baseImage, 0, 0, $marginH, $marginV, imagesx($watermarkImage), imagesy($watermarkImage));
    imagecopy($cut, $watermarkImage, 0, 0, 0, 0, imagesx($watermarkImage), imagesy($watermarkImage));

    imagecopymerge($baseImage, $cut, $marginH, $marginV, 0, 0, imagesx($watermarkImage), imagesy($watermarkImage), 80);

    if (!imagepng($baseImage, 'watermarked_image.png'))
    {
        return false;
    }

    return true;
}

这是根据类似问题的各种指南和建议拼凑而成的。再次强调,使用JPG图像和PNG水印完美运作,但不支持PNG & PNG。
一些示例图像: http://i.imgur.com/hHRWinj.png - 这是我正在使用的水印。 http://i.imgur.com/6sy8Ncs.png - 这是我要应用水印的图像。 http://i.imgur.com/ghovYLm.png - 这是最终结果。
我发现有趣的是,水印覆盖在图像的非透明部分时,任何部分都可以正常工作。只有其余部分具有黑色背景。
这让我相信我已经接近解决方法了,希望您们专业的知识可以帮助我找到解决方案。
感谢您的阅读。

只是提醒一下,ImageMagick已经取代了GD函数。 - Kohjah Breese
2
它确实有问题。不幸的是,我的应用故意要求非常轻量级的最低配置。不过,必须承认的是,考虑到这个特性是唯一部分存在问题的特性,我一直在考虑将该特性专供于ImageMagick,但是……您是否有那种不得不找到问题根源的问题,即使这个过程可能会让您筋疲力尽呢? :-) - SpongeBobPHPants
imagecopymerge - 在此函数中,最后一个值为80。这与alpha通道有关,我猜这就是问题所在,也许尝试更改此值会有帮助。 - Kohjah Breese
1
这是应用于基础图像的水印不透明度。http://i.imgur.com/lWsgc0X.png - 不透明度为0。http://i.imgur.com/DxvKmOX.png - 不透明度为100。 - SpongeBobPHPants
1
有哪些与水印相关的行可以删除吗?尝试缩小问题的范围吗?抱歉我不了解GD。 - Kohjah Breese
遗憾的是,我认为我们需要添加额外的代码行而不是删除它们。如果删除了某些代码行,情况会变得更糟。其中一些代码行可以防止整个透明背景丢失并变成黑色。还有一些代码行至少使水印看起来部分正确,就像现在水印的黑色背景只在某些地方是黑色的。因此,使用当前的代码,要么缺少一些代码,要么尝试稍微重新排列代码,但我看不出这会有多大的区别。 - SpongeBobPHPants
2个回答

1

所以,我不会放弃使用GD找到正确的答案。然而,我很高兴地发现,使用ImageMagick可以用更少的代码实现需要使用GD 30行代码才能实现的功能:

    $image = new Imagick();
    $image->readimage($this->_image);

    $watermark = new Imagick();
    $watermark->readimage($this->_watermark->_getImage());
    $watermark->evaluateImage(Imagick::EVALUATE_DIVIDE, 2, Imagick::CHANNEL_ALPHA);

    $image->compositeImage($watermark, imagick::COMPOSITE_OVER, $marginH, $marginV);

这是使用GD之前的效果:

http://i.imgur.com/AlS0TcO.png

这是使用ImageMagick和上述代码之后的效果:

http://i.imgur.com/zBxlC3R.png

如果有人能提供纯粹基于GD的答案,我将非常感激。


1

最近也遇到了类似的问题,虽然这些方法可能不能完全解决您的问题,但是它们是我发现的一些有用的发现。

在我的情况下,我有一个原始的.jpg图像和一个水印.png图像。水印图像具有完全透明的背景。我想在脚本中指定不透明度,并在将其放置在原始图像上之前更改水印的不透明度。关于PHP水印的大多数帖子都假定原始水印.png文件已经将固定的水印部分设置为正确的不透明度,而不是通过脚本进行更改。

  1. gd 不支持 24 位的 .png,会导致一些奇怪的问题。转换为 8 位可以通过 gd 解决。另一方面,imagick 对于 24 位的 .png 很有效,最终结果似乎更好。
  2. 对我而言,如果我打开原始水印 .png 并使用 imagecopymerge() 来设置水印透明度,则使用 gd 可以正常工作。但是,如果我尝试先缩放原始水印 .png(具有透明背景),然后我将得到与您类似的结果,即水印图像所在的黑色或白色背景部分。请参见 How do I resize pngs with transparency in PHP? ,其中提供了一个部分解决方案,通过首先用透明矩形填充新的 wm 图像来实现。对我而言,无论我尝试什么,最终结果仍然产生不透明的白色背景。
  3. 我切换到 imagick,并使用 setImageOpacity() 在应用到我的原始图像之前更改我的水印 .png 的透明度,但我仍然得到了一个黑色背景的相同效果。最终在 PHP doc 中阅读到 setImageOpacity() 的说明,如果原始的 .png 有任何透明像素,并且您尝试降低不透明度,则这些像素会变为不透明(黑色),新的透明度被应用。相反,需要使用 evaluateImage() 函数。这将只计算每个像素的 alpha 通道并除以指定的数字。
  4. 我认为 gd 的黑 / 白背景问题可能是由于在缩放/合并时处理 alpha 通道的方式与 imagick 类似,如果您想全部使用 gd,则只需找到一些类似的方法来逐像素评估和操作 alpha 通道,因为“简单”的方法似乎会将已经透明的背景变为不透明。
所以,解决方案如下:
假设您想将水印应用于45%的不透明度,并且您正在使用imagick,那么请改为以下代码:

$watermark->setImageOpacity(.45);

执行此操作

$watermark->evaluateImage(Imagick::EVALUATE_DIVIDE, (1/.45), Imagick::CHANNEL_ALPHA);

您需要将1除以不透明度,以获得函数将为每个像素的alpha通道值除以的分母。在这种情况下,1/.45 = 2.2222,然后函数将通过2.2222除以每个像素的alpha通道。这意味着一个实心像素(alpha为1)将在完成时产生1/2.2222.45 alpha或透明度。任何已经透明的像素(alpha 0)将保持透明,因为0除以任何数总是什么?零!

更改水印透明度后,您可以使用compositeImage()将水印合并到原始图像中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接