PHP 对具有透明 PNG 的 imagetruecolortopalette 产生错误结果

2023-12-22

我正在尝试编写一个 PHP 脚本来调整 PNG 图像的大小,然后将其转换为 PNG-8 位模式。因此,生成的文件的大小会更小,但质量不会有太大损失。

调整大小效果非常好,还保留了图像透明度:

问题是当我将图像转换为 8 位时:

imagetruecolortopalette($resizedImg, true, 255);

imagealphablending($resizedImg, false);

$transparent = imagecolorallocatealpha($resizedImg, 255, 255, 255, 127);
if(!imagefill($resizedImg, 0, 0, $transparent)) return false;

imagesavealpha($resizedImg, true);

生成的图像是这样的,图像周围和内部都有一点透明度:

如果我设置 256 种颜色而不是 255:

imagetruecolortopalette($resizedImg, true, 256);

图像将具有黑色背景:

此图像会出现类似的结果(请注意 255 种颜色的情况下的半透明度):

Original: enter image description here 255 colors: enter image description here 256 colors: enter image description here

完整函数的代码:

function resizePng($originalPath, $xImgNew='', $yImgNew='', $newPath='')
{
    if(!trim($originalPath) || !$xyOriginalPath = getimagesize("$originalPath")) return false;
    list($xImg, $yImg) = $xyOriginalPath;
    
    if(!$originalImg = imagecreatefrompng($originalPath)) return false;
    
    if(!$resizedImg = imagecreatetruecolor($xImgNew, $yImgNew)) return false;
    
    // preserve alpha
    imagealphablending($resizedImg, false);
    $transparent = imagecolorallocatealpha($resizedImg, 255, 255, 255, 127);
    if(!imagefill($resizedImg, 0, 0, $transparent)) return false;
    imagesavealpha($resizedImg, true);
    
    // copy content from originalImg to resizedImg
    if(!imagecopyresampled($resizedImg, $originalImg, 0, 0, 0, 0, $xImgNew, $yImgNew, $xImg, $yImg)) return false;
    
    // PNG-8 bit conversion
    imagetruecolortopalette($resizedImg, true, 255);
    
    // preserve alpha
    imagealphablending($resizedImg, false);
    $transparent = imagecolorallocatealpha($resizedImg, 255, 255, 255, 127);
    if(!imagefill($resizedImg, 0, 0, $transparent)) return false;
    imagesavealpha($resizedImg, true);
    
    if(!imagepng($resizedImg, ($newPath) ?: null, 8)) return false;

    return true;
}

我尝试过的:

https://stackoverflow.com/a/8144620/2342558 https://stackoverflow.com/a/8144620/2342558

// PNG-8 bit conversion
imagetruecolortopalette($resizedImg, true, 255);

imagesavealpha($resizedImg, true);
imagecolortransparent($resizedImg, imagecolorat($resizedImg,0,0));

// preserve alpha
imagealphablending($resizedImg, false);
$transparent = imagecolorallocatealpha($resizedImg, 255, 255, 255, 127);
if(!imagefill($resizedImg, 0, 0, $transparent)) return false;
imagesavealpha($resizedImg, true);

if(!imagepng($resizedImg, ($newPath) ?: null, 8)) return false;

Results:

Also: https://stackoverflow.com/a/55402802/2342558 https://stackoverflow.com/a/55402802/2342558

没有改变。

另外:其他 SO 帖子和网络上的一些

也无需调整图像大小(删除imagecopyresampled并调整变量名称)结果是相同的。

我怎样才能让它发挥作用并理解这种奇怪行为的原因?

一些信息在phpinfo():

  • PHP 7.0.33
  • GD捆绑(兼容 2.1.0)
  • PNG Support enabled
  • libPNG 1.5.13.

Edit:

在 GIMP v.2.8.22 中,我可以使用以下属性保存 Web 图像:

PNG-8
256 colors palette
Dither: Floyd-Steinberg / Floyd-Steinberg 2 / positioned

它会产生与原始图像几乎相同的缩小图像。

pngquant、tinypng 和许多其他工具也执行相同的工作,但是我需要用 PHP 来做.

Edit 2:

很遗憾,我无法使用 ImageMagick因为我的代码位于共享主机中,但没有安装。

Edit 3:

in phpinfo()结果表明imagemagick模块未安装。

Edit 4:

让我对你的回复做一些测试,也许有一个只用 PHP 的解决方案。

Edit 5:

这些是我对你的答案的尝试。

注意:我放置了一个底层网格以更好地显示 Alpha。

托马斯·惠泽尔的回答 https://stackoverflow.com/a/58075858/2342558:

企鹅有明显的色带,但鸭子还可以(尽管有时色调较暗)。

环保局的答复 https://stackoverflow.com/a/58126977/2342558:

只有当图像只有已经完全透明的像素时,它才能很好地工作(例如鸭子)。

马克·塞切尔的回答 https://stackoverflow.com/a/58132349/2342558:

它使所有具有 Alpha 的像素完全透明,如果该 Alpha 非常低,请参见企鹅下方的阴影。此外,鸭子边缘的一些像素也被转换为黑色像素或全透明像素。


您可以在 ImageMagick 中轻松完成此操作,它分布在 Linux 上,并且适用于 Windows 和 Mac OSX。除了命令行之外还有很多API。以下是如何在 ImageMagick 命令行中执行此操作。

Input:

convert image.png PNG8:result1.png

PNG8:表示 256 色和二进制透明度。这意味着要么完全透明,要么完全不透明。这会导致边缘周围出现锯齿(阶梯)。如果您愿意设置背景颜色来代替透明度,那么您可以在结果中保持平滑(抗锯齿)轮廓。所以对于白色背景。

convert image.png -background white -flatten PNG8:result2.png

ImageMagick 由 PHP Imagick 运行。所以你应该能够使用 PHP Imagick 来做到这一点。或者您可以从 PHP exec() 调用 ImageMagick 命令行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PHP 对具有透明 PNG 的 imagetruecolortopalette 产生错误结果 的相关文章

随机推荐