GD 图像库在操作后创建透明 GIF

GD Image Library to create transparent GIF after manipulation

我正在使用 PHP 生成具有透明度的 PNG 动态图像。

在此过程中,我在图像顶部添加了一个透明的 PNG 徽标,同时保留了它的透明度。

当我使用 php imagepng($destination).

将最终产品输出为 PNG 时,我目前可以正常工作

但是,由于这张图片是 PNG,所以我的尺寸相当大,我需要将最终输出转换为 gif(以保持背景图片的透明度)。

这是我的代码:

$data = [
    'name' => [
        'x' => ($noBG ? 150 : 400),
        'y' => ($noBG ? 700 : 920),
        'size' => 40,
        'angle' => 0,
        'content' => $name,
    ],
    'pin' => [
        'x' => ($noBG ? 130 : 440),
        'y' => ($noBG ? 475 : 700),
        'size' => 75,
        'angle' => 0,
        'content' => implode('  ', str_split($pin)),
    ],
    'denomination' => [
        'x' => denominationPosition($denomination, $noBG),
        'y' => ($noBG ? 150 : 375),
        'size' => 70,
        'angle' => 0,
        'content' => $denomination,
    ],
    'defaultText' => [
        'x' => ($noBG ? 150 : 400),
        'y' => ($noBG ? 780 : 980),
        'size' => 30,
        'angle' => 0,
        'content' => 'It Doesn\'t Cost - It PAY$!',
    ],
    'logo' => [
        'x' => ($noBG ? 875 : 1200),
        'y' => ($noBG ? 675 : 880),
    ],
];

// Are we using the template with or without a background?
if($noBG){
    $style = 'assets/images/templateNoBG.png';
}else{
    $style = 'assets/images/template.png';
}


// Did we pass a logo?
if ($logo) {
    $src = imagecreatefrompng($logo);
}else{
    $src= imagecreatefrompng($noLogo);
}

// Transparent sponsor logo
imagealphablending($src, false);
imagesavealpha($src, true);

// Define our source image (the background)
$destination = imagecreatefrompng($style);

// Colors
$textColor = imagecolorallocate($destination, 255, 255, 255);
$regularFont = 'assets/fonts/Bungee-Regular.otf';

// Transparent background
imagealphablending($destination, true);
imagesavealpha($destination, true);

// Name
imagettftext($destination, $data['name']['size'], $data['name']['angle'], $data['name']['x'], $data['name']['y'], $textColor, $regularFont, $data['name']['content']);
// Pin
imagettftext($destination, $data['pin']['size'], $data['pin']['angle'], $data['pin']['x'], $data['pin']['y'], $textColor, $regularFont, $data['pin']['content']);
// Denomination
imagettftext($destination, $data['denomination']['size'], $data['denomination']['angle'], $data['denomination']['x'], $data['denomination']['y'], $textColor, $regularFont, '$' . $data['denomination']['content']);
// Default Text
imagettftext($destination, $data['defaultText']['size'], $data['defaultText']['angle'], $data['defaultText']['x'], $data['defaultText']['y'], $textColor, $regularFont, $data['defaultText']['content']);

// Merge the logo and template together
imagecopy($destination, $src, $data['logo']['x'], $data['logo']['y'], 0, 0, $logoWidth, $logoHeight);


// Create our header to flush the image to browser
//header("Content-type: image/png");
header("Content-type: image/gif");
header("Cache-Control: no-store, no-cache");

/**
 * Un-comment to ask to save file
 * header('Content-Disposition: attachment; filename="DiningCard.png"');
 */

// Render image
//imagepng($destination); // Works
imagegif($destination);

// Cleanup
imagedestroy($destination);


/**
 * Depending on the number of of numbers in the denomination,
 * pass x coordinates to help keep it in position
 */
function denominationPosition($denomination, $noBG)
{
    switch (strlen($denomination)) {
        case 1:
            return ($noBG ? 1150 : 950);
            break;
        case 2:
            return ($noBG ? 1100: 1400);
            break;
        case 3:
            return ($noBG ? 1050 : 1350);
            break;
        case 4:
            return ($noBG ? 950 : 1290);
            break;
    }

}

我 运行 遇到的问题是,当我使用 imagegif($destination) 时,背景图像失去透明度并且全部变形。

图像的背景应该是透明的,但您可以看到失真和缺少 alpha 通道。

当通过 imagepng() 作为 png 输出时,一切正常。

这是 PNG 版本:

我把它放在 bitbucket 存储库中,以防有人想查看代码 - 仅查看代码很难解决此类问题。

https://bitbucket.org/sbbdev/cardgen/src

关于如何让 gif 版本工作的任何想法?两者之间的尺寸差异对于开始工作至关重要。

如评论中所述,gif 是一种基于调色板的格式,不支持完全的 alpha 透明度。您可以通过分配透明颜色来实现接近您想要的效果,然后扫描图像并在转换为 gif 格式之前将 alpha 值设置为该透明颜色的像素为 50% 或更高。

// Render image
//imagepng($destination); // Works
$transparent = imagecolortransparent($destination, imagecolorallocate($destination, 0, 0, 0));
$width = imagesx($destination);
$height = imagesy($destination);
for ($x = 0; $x < $width; $x++) {
    for ($y = 0; $y < $height; $y++) {
        $pixel = imagecolorsforindex($destination, imagecolorat($destination, $x, $y));
        if ($pixel['alpha'] >= 64) {
            imagesetpixel($destination, $x, $y, $transparent);
        }
    }
}
imagegif($destination);

请注意,这会在图像中产生粗糙的边缘,因为像素在 gif 格式中要么完全不透明,要么完全透明。

by @astrangeloop takes about 2.5 seconds to render on my laptop. As OP has 我认为这个解决方案行不通。

我提议:

  • 手动优化服务器上​​的资产
  • 在 PHP
  • 中打开时将这些资源转换为真彩色
  • 呈现为 GIF

例如,我使用 tinypng.com 优化了 templateNoBG.png,将文件大小从 1.02MiB 减少到 227KiB:

由于图像现在是 8 位(256 色),我们需要在打开时将其设为真彩色,以便合并后的图像保留其颜色:

$destination = imagecreatefrompng($style);
imagepalettetotruecolor($destination);

当使用 imagegif 输出图像时,它再次减少到 8 位,具有更好的调色板选择,尽管由于 GIF 仅支持二进制透明度而具有明显的锯齿状边缘:

生成的图像只有 395KiB,在我的笔记本电脑上生成大约需要 120 毫秒。

这可能是可以预期的速度、质量和大小之间的最佳权衡。