首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP GIF 等比縮放完美解決黑背景問題:透明色保留、調色板處理與類封裝

     編輯:PHP 相關知識

痛點

PHP imagecopyresized() / imagecopyresampled() 縮放 GIF 時,透明色會變成黑色背景。原因:真彩色畫布預設背景為黑,且未正確複製調色板透明索引。

核心修復邏輯

  1. 從原圖獲取透明色索引:imagecolortransparent($src)
  2. 若索引有效(>= 0 且 < imagecolorstotal($src)),讀取其 RGB:imagecolorsforindex()
  3. 在目標真彩色畫布分配相同 RGB:imagecolorallocate($dst, $r, $g, $b)
  4. 填充目標畫布背景為該色:imagefill($dst, 0, 0, $transparentColor)
  5. 設定目標畫布透明色:imagecolortransparent($dst, $transparentColor)
  6. 執行縮放複製
  7. 輸出時 imagegif() 會自動保留透明色

完整類封裝

<?php
declare(strict_types=1);

class GifResizer {
    private float $scale = 0.5; // 縮放比例 0.1~1.0

    public function __construct(float $scale = 0.5) {
        $this->scale = max(0.01, min(1.0, $scale));
    }

    public function resize(string $sourcePath, string $destPath): bool {
        if (!is_file($sourcePath) || !is_readable($sourcePath)) {
            throw new \InvalidArgumentException("Source file not readable: {$sourcePath}");
        }

        // 僅處理 GIF
        $info = getimagesize($sourcePath);
        if ($info[2] !== IMAGETYPE_GIF) {
            throw new \InvalidArgumentException('Only GIF images are supported');
        }

        $src = imagecreatefromgif($sourcePath);
        if (!$src) throw new \RuntimeException('Failed to create GD resource from GIF');

        $srcW = imagesx($src);
        $srcH = imagesy($src);
        $dstW = (int)round($srcW * $this->scale);
        $dstH = (int)round($srcH * $this->scale);

        // 等比修正(防止長寬比失真)
        if ($dstW && $srcW < $srcH) {
            $dstW = (int)round($dstH / $srcH * $srcW);
        } else {
            $dstH = (int)round($dstW / $srcW * $srcH);
        }

        // 建立真彩色目標畫布
        $dst = imagecreatetruecolor($dstW, $dstH);
        if (!$dst) {
            imagedestroy($src);
            throw new \RuntimeException('Failed to create truecolor canvas');
        }

        // === 關鍵:透明色處理 ===
        $transparentIndex = imagecolortransparent($src);
        if ($transparentIndex >= 0 && $transparentIndex < imagecolorstotal($src)) {
            $rgb = imagecolorsforindex($src, $transparentIndex);
            $transparentColor = imagecolorallocate($dst, $rgb['red'], $rgb['green'], $rgb['blue']);
            imagefill($dst, 0, 0, $transparentColor);
            imagecolortransparent($dst, $transparentColor);
        } else {
            // 無透明色:關閉 alpha 混合,避免黑背景
            imagealphablending($dst, false);
            $transparent = imagecolorallocatealpha($dst, 0, 0, 0, 127);
            imagefill($dst, 0, 0, $transparent);
            imagesavealpha($dst, true);
        }

        // 高質量重採樣
        imagecopyresampled($dst, $src, 0, 0, 0, 0, $dstW, $dstH, $srcW, $srcH);

        // 輸出
        $result = imagegif($dst, $destPath);

        // 清理
        imagedestroy($src);
        imagedestroy($dst);

        return $result;
    }

    // 批次處理
    public function resizeBatch(array $pairs): array {
        $results = [];
        foreach ($pairs as $src => $dest) {
            try {
                $results[$src] = $this->resize($src, $dest) ? 'success' : 'failed';
            } catch (\Throwable $e) {
                $results[$src] = 'error: ' . $e->getMessage();
            }
        }
        return $results;
    }
}

// 使用示例
$resizer = new GifResizer(0.5); // 50% 縮放
$resizer->resize('/path/source.gif', '/path/thumb.gif');

// 批次
$results = $resizer->resizeBatch([
    '/img/a.gif' => '/thumb/a.gif',
    '/img/b.gif' => '/thumb/b.gif',
]);
print_r($results);

關鍵細節對比表

步驟錯誤做法(黑背景)正確做法
目標畫布建立imagecreate() (調色板模式)imagecreatetruecolor() (真彩色)
透明色索引獲取忽略imagecolortransparent($src)
RGB 分配不分配imagecolorallocate($dst, r, g, b)
背景填充不填充或填充黑imagefill($dst, 0, 0, $transparentColor)
設定目標透明色忽略imagecolortransparent($dst, $transparentColor)
縮放函式imagecopyresized()imagecopyresampled() (高質量)
無透明色 GIF黑背景imagealphablending(false) + imagesavealpha(true)

效能與品質權衡

  • imagecopyresampled:雙三次插值,品質最好,CPU 較高
  • imagecopyresized:最近鄰插值,速度快,鋸齒明顯
  • 大批量處理建議:imagick 擴展(多線程、更優演算法)或 gifski / gifsicle 命令列工具

現代替代方案:Imagick

<?php
$imagick = new Imagick('/path/source.gif');
$imagick->setIteratorIndex(0); // 僅第一幀,或遍歷所有幀
$imagick->resizeImage(
    $imagick->getImageWidth() * 0.5,
    $imagick->getImageHeight() * 0.5,
    Imagick::FILTER_LANCZOS,
    1
);
// 保留動畫需遍歷幀,略
$imagick->writeImage('/path/thumb.gif');

Imagick 原生支援動畫 GIF 多幀縮放、更優壓縮、無黑背景問題,生產環境首選。

總結

GIF 縮放黑背景的根因是 真彩色畫布預設黑背景 + 未正確複製透明色索引。核心修復只需 5 行代碼:獲取索引 → 讀取 RGB → 分配色彩 → 填充背景 → 設定透明。封裝為 GifResizer 類即可復用。新專案建議直接使用 Imagick 或外部工具 gifsicle,徹底規避 GD 歷史包袱。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved