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

PHP GD 圖像處理全攻略:3D 統計圖、縮放、裁剪、旋轉、水印實戰教程

     編輯:PHP 相關知識

GD 庫是 PHP 內建圖形處理擴展,無需額外安裝即可實現圖片生成、縮放、裁剪、水印、統計圖繪製等核心功能。本文提供五大經典場景的**可直接落地的封裝函數**,涵蓋參數驗證、資源釋放、錯誤處理等工程化細節。

環境檢查

<?php
// 確認 GD 支援
if (!extension_loaded('gd')) die('GD extension not loaded');
$info = gd_info();
echo "GD Version: {$info['GD Version']}\n";
echo "FreeType: " . ($info['FreeType Support'] ? 'Yes' : 'No') . "\n";
echo "PNG/JPG/GIF: " . ($info['PNG Support'] && $info['JPEG Support'] && $info['GIF Read Support'] ? 'Yes' : 'No') . "\n";
?>

1. 繪製 3D 扇形統計圖 (imagefilledarc)

利用多層偏移繪製偽 3D 效果,適合簡單報表嵌入。

<?php
function draw3DPieChart(array $data, int $width = 400, int $height = 300): void {
    // $data = ['標籤' => ['value' => 數值, 'color' => '十六進制顏色', 'dark' => '陰影色']]
    $img = imagecreatetruecolor($width, $height);
    $white = imagecolorallocate($img, 0xFF, 0xFF, 0xFF);
    imagefill($img, 0, 0, $white);

    $cx = $width / 2;
    $cy = $height / 2;
    $rx = min($width, $height) * 0.4; // x 半徑
    $ry = $rx * 0.5;                  // y 半徑 (橢圓)

    $total = array_sum(array_column($data, 'value'));
    $startAngle = -90; // 從頂部開始

    // 1. 繪製 3D 側面 (向下疊加 10 層)
    for ($depth = 10; $depth > 0; $depth--) {
        $angle = $startAngle;
        foreach ($data as $item) {
            $endAngle = $angle + ($item['value'] / $total) * 360;
            $darkColor = imagecolorallocate($img,
                hexdec(substr($item['dark'], 1, 2)),
                hexdec(substr($item['dark'], 3, 2)),
                hexdec(substr($item['dark'], 5, 2))
            );
            imagefilledarc($img, $cx, $cy + $depth, $rx * 2, $ry * 2, $angle, $endAngle, $darkColor, IMG_ARC_PIE);
            $angle = $endAngle;
        }
    }

    // 2. 繪製頂面 (實際數據層)
    $angle = $startAngle;
    foreach ($data as $label => $item) {
        $endAngle = $angle + ($item['value'] / $total) * 360;
        $color = imagecolorallocate($img,
            hexdec(substr($item['color'], 1, 2)),
            hexdec(substr($item['color'], 3, 2)),
            hexdec(substr($item['color'], 5, 2))
        );
        imagefilledarc($img, $cx, $cy, $rx * 2, $ry * 2, $angle, $endAngle, $color, IMG_ARC_PIE);

        // 繪製標籤 (簡易版,生產建議計算文本位置避免重疊)
        $midAngle = deg2rad(($angle + $endAngle) / 2);
        $tx = $cx + cos($midAngle) * $rx * 0.6;
        $ty = $cy + sin($midAngle) * $ry * 0.6;
        imagestring($img, 3, $tx, $ty, "{$label} " . round($item['value']/$total*100) . "%", $white);

        $angle = $endAngle;
    }

    header('Content-Type: image/png');
    imagepng($img);
    imagedestroy($img);
}

// 使用範例
$data = [
    'Chrome'  => ['value' => 60, 'color' => '#4285F4', 'dark' => '#1a5fd8'],
    'Firefox' => ['value' => 20, 'color' => '#FF9500', 'dark' => '#cc7700'],
    'Safari'  => ['value' => 10, 'color' => '#34A853', 'dark' => '#25853f'],
    'Edge'    => ['value' => 5,  'color' => '#0078D7', 'dark' => '#005fa3'],
    'Other'   => ['value' => 5,  'color' => '#9AA0A6', 'dark' => '#787e84'],
];
draw3DPieChart($data);
?>

2. 圖片等比縮放 (保持長寬比,支援最大寬高限制)

<?php
/**
 * 等比縮放並保存
 * @param string $srcPath 來源圖路徑
 * @param string $dstPath 目標路徑
 * @param int $maxW 最大寬度
 * @param int $maxH 最大高度
 * @param int $quality JPEG 品質 0-100
 * @return bool
 */
function resizeImage(string $srcPath, string $dstPath, int $maxW = 800, int $maxH = 600, int $quality = 85): bool {
    if (!file_exists($srcPath)) return false;

    list($srcW, $srcH, $type) = getimagesize($srcPath);
    if ($srcW <= $maxW && $srcH <= $maxH) {
        // 無需縮放,直接複製或返回 true
        return copy($srcPath, $dstPath);
    }

    // 計算目標尺寸
    $ratio = min($maxW / $srcW, $maxH / $srcH);
    $dstW = (int) round($srcW * $ratio);
    $dstH = (int) round($srcH * $ratio);

    // 建立來源資源
    $createFn = match ($type) {
        IMAGETYPE_JPEG => 'imagecreatefromjpeg',
        IMAGETYPE_PNG  => 'imagecreatefrompng',
        IMAGETYPE_GIF  => 'imagecreatefromgif',
        IMAGETYPE_WEBP => 'imagecreatefromwebp',
        default => null,
    };
    if (!$createFn) return false;
    $srcImg = $createFn($srcPath);
    if (!$srcImg) return false;

    // 建立目標真彩色畫布
    $dstImg = imagecreatetruecolor($dstW, $dstH);

    // **關鍵:保持 PNG/GIF 透明度**
    if ($type === IMAGETYPE_PNG || $type === IMAGETYPE_GIF) {
        imagealphablending($dstImg, false);
        imagesavealpha($dstImg, true);
        $transparent = imagecolorallocatealpha($dstImg, 255, 255, 255, 127);
        imagefilledrectangle($dstImg, 0, 0, $dstW, $dstH, $transparent);
    }

    // 高質量重採樣縮放
    imagecopyresampled($dstImg, $srcImg, 0, 0, 0, 0, $dstW, $dstH, $srcW, $srcH);

    // 輸出
    $result = match ($type) {
        IMAGETYPE_JPEG => imagejpeg($dstImg, $dstPath, $quality),
        IMAGETYPE_PNG  => imagepng($dstImg, $dstPath, 9), // PNG 壓縮級別 0-9
        IMAGETYPE_GIF  => imagegif($dstImg, $dstPath),
        IMAGETYPE_WEBP => imagewebp($dstImg, $dstPath, $quality),
        default => false,
    };

    imagedestroy($srcImg);
    imagedestroy($dstImg);
    return $result;
}
?>

3. 圖片裁剪 (指定區域裁剪,可選縮放)

<?php
/**
 * 裁剪圖片
 * @param string $srcPath
 * @param string $dstPath
 * @param int $x 裁剪區左上角 X (相對原圖)
 * @param int $y 裁剪區左上角 Y
 * @param int $w 裁剪寬度
 * @param int $h 裁剪高度
 * @param int|null $dstW 輸出寬度 (若指定則縮放至該尺寸)
 * @param int|null $dstH 輸出高度
 */
function cropImage(string $srcPath, string $dstPath, int $x, int $y, int $w, int $h, ?int $dstW = null, ?int $dstH = null): bool {
    list($srcW, $srcH, $type) = getimagesize($srcPath);
    if ($x < 0 || $y < 0 || $x + $w > $srcW || $y + $h > $srcH) return false;

    $dstW = $dstW ?? $w;
    $dstH = $dstH ?? $h;

    $createFn = [IMAGETYPE_JPEG=>'imagecreatefromjpeg', IMAGETYPE_PNG=>'imagecreatefrompng', IMAGETYPE_GIF=>'imagecreatefromgif', IMAGETYPE_WEBP=>'imagecreatefromwebp'][$type] ?? null;
    if (!$createFn) return false;
    $srcImg = $createFn($srcPath);
    if (!$srcImg) return false;

    $dstImg = imagecreatetruecolor($dstW, $dstH);
    // 透明度處理同 resizeImage
    if (in_array($type, [IMAGETYPE_PNG, IMAGETYPE_GIF])) {
        imagealphablending($dstImg, false);
        imagesavealpha($dstImg, true);
        $tr = imagecolorallocatealpha($dstImg, 255, 255, 255, 127);
        imagefilledrectangle($dstImg, 0, 0, $dstW, $dstH, $tr);
    }

    // 裁剪並可選縮放
    imagecopyresampled($dstImg, $srcImg, 0, 0, $x, $y, $dstW, $dstH, $w, $h);

    $saveFn = [IMAGETYPE_JPEG=>'imagejpeg', IMAGETYPE_PNG=>'imagepng', IMAGETYPE_GIF=>'imagegif', IMAGETYPE_WEBP=>'imagewebp'][$type];
    $result = $saveFn($dstImg, $dstPath, $type === IMAGETYPE_JPEG || $type === IMAGETYPE_WEBP ? 85 : 9);

    imagedestroy($srcImg);
    imagedestroy($dstImg);
    return $result;
}
?>

4. 圖片旋轉 (支援 90/180/270 度,可選背景色)

<?php
/**
 * 旋轉圖片
 * @param string $srcPath
 * @param string $dstPath
 * @param int $angle 旋轉角度 (順時針),建議 90/180/270
 * @param array $bgColor 背景色 RGB [255,255,255]
 */
function rotateImage(string $srcPath, string $dstPath, int $angle, array $bgColor = [255,255,255]): bool {
    $angle = $angle % 360;
    if ($angle === 0) return copy($srcPath, $dstPath);

    list(,,$type) = getimagesize($srcPath);
    $createFn = [IMAGETYPE_JPEG=>'imagecreatefromjpeg', IMAGETYPE_PNG=>'imagecreatefrompng', IMAGETYPE_GIF=>'imagecreatefromgif'][$type] ?? null;
    if (!$createFn) return false;
    $srcImg = $createFn($srcPath);
    if (!$srcImg) return false;

    // imagerotate 角度為逆時針,需轉換
    $rotated = imagerotate($srcImg, 360 - $angle, imagecolorallocate($srcImg, ...$bgColor));
    if (!$rotated) return false;

    // 透明度處理
    if (in_array($type, [IMAGETYPE_PNG, IMAGETYPE_GIF])) {
        imagealphablending($rotated, false);
        imagesavealpha($rotated, true);
    }

    $saveFn = [IMAGETYPE_JPEG=>'imagejpeg', IMAGETYPE_PNG=>'imagepng', IMAGETYPE_GIF=>'imagegif'][$type];
    $result = $saveFn($rotated, $dstPath, 90);

    imagedestroy($srcImg);
    imagedestroy($rotated);
    return $result;
}
?>

5. 添加圖片水印 (支援隨機位置、透明度、平鋪)

<?php
/**
 * 添加水印
 * @param string $srcPath 背景圖
 * @param string $waterPath 水印圖 (建議 PNG 透明)
 * @param string $dstPath
 * @param int $position 位置: 1-9 (數字鍵盤對應), 0=隨機, 10=平鋪
 * @param int $alpha 水印透明度 0-100 (100 不透明)
 * @param int $margin 邊距
 */
function watermarkImage(string $srcPath, string $waterPath, string $dstPath, int $position = 9, int $alpha = 50, int $margin = 10): bool {
    list($srcW, $srcH, $srcType) = getimagesize($srcPath);
    list($wW, $wH, $wType) = getimagesize($waterPath);

    if ($wW > $srcW || $wH > $srcH) return false; // 水印大於背景

    $srcCreate = [IMAGETYPE_JPEG=>'imagecreatefromjpeg', IMAGETYPE_PNG=>'imagecreatefrompng', IMAGETYPE_GIF=>'imagecreatefromgif', IMAGETYPE_WEBP=>'imagecreatefromwebp'][$srcType] ?? null;
    $waterCreate = [IMAGETYPE_PNG=>'imagecreatefrompng', IMAGETYPE_GIF=>'imagecreatefromgif', IMAGETYPE_JPEG=>'imagecreatefromjpeg'][$wType] ?? null;
    if (!$srcCreate || !$waterCreate) return false;

    $srcImg = $srcCreate($srcPath);
    $waterImg = $waterCreate($waterPath);
    if (!$srcImg || !$waterImg) return false;

    // 設定水印透明度 (需 GD 2.0.1+)
    imagefilter($waterImg, IMG_FILTER_COLORIZE, 0, 0, 0, (100 - $alpha) * 2.55); // 近似處理
    // 更精確:遍歷像素修改 alpha 通道 (略)

    // 計算位置
    $positions = [
        1 => [$margin, $srcH - $wH - $margin],           // 左下
        2 => [($srcW - $wW) / 2, $srcH - $wH - $margin], // 下中
        3 => [$srcW - $wW - $margin, $srcH - $wH - $margin], // 右下
        4 => [$margin, ($srcH - $wH) / 2],               // 左中
        5 => [($srcW - $wW) / 2, ($srcH - $wH) / 2],     // 正中
        6 => [$srcW - $wW - $margin, ($srcH - $wH) / 2], // 右中
        7 => [$margin, $margin],                         // 左上
        8 => [($srcW - $wW) / 2, $margin],               // 上中
        9 => [$srcW - $wW - $margin, $margin],           // 右上 (預設)
    ];

    if ($position === 0) { // 隨機
        $posX = rand($margin, $srcW - $wW - $margin);
        $posY = rand($margin, $srcH - $wH - $margin);
    } elseif ($position === 10) { // 平鋪
        for ($x = 0; $x < $srcW; $x += $wW + $margin) {
            for ($y = 0; $y < $srcH; $y += $wH + $margin) {
                imagecopy($srcImg, $waterImg, $x, $y, 0, 0, $wW, $wH);
            }
        }
        goto save_output;
    } else {
        [$posX, $posY] = $positions[$position] ?? $positions[9];
    }

    imagecopy($srcImg, $waterImg, (int)$posX, (int)$posY, 0, 0, $wW, $wH);

save_output:
    $saveFn = [IMAGETYPE_JPEG=>'imagejpeg', IMAGETYPE_PNG=>'imagepng', IMAGETYPE_GIF=>'imagegif', IMAGETYPE_WEBP=>'imagewebp'][$srcType];
    $result = $saveFn($srcImg, $dstPath, $srcType === IMAGETYPE_JPEG || $srcType === IMAGETYPE_WEBP ? 90 : 9);

    imagedestroy($srcImg);
    imagedestroy($waterImg);
    return $result;
}
?>

工程化關鍵提醒

  1. 記憶體管理:大圖處理極易 OOM。`ini_set('memory_limit', '512M')`、分塊處理、或改用 Imagick (流式處理)。
  2. 透明度保護:PNG/GIF/WebP **必須** `imagealphablending(false); imagesavealpha(true);` 否則透明變黑底。
  3. 錯誤處理:所有 `imagecreatefrom*` 可能返回 `false`,嚴格判斷。
  4. 安全性**:上傳圖片**必須**驗證 MIME、重命名、移除 EXIF、禁用腳本執行權限。
  5. 效能優化**:高併發場景引入隊列異步處理、CDN 緩存衍生圖、考慮 WebP 格式輸出。

掌握這五大核心操作與工程化細節,PHP GD 即可滿足 90% 以上的圖片處理業務需求。

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