在 PHP 圖形處理中,將一張圖片 (Logo、水印、縮圖、二維碼) 合成到另一張背景圖上,是極高頻的剛需場景。GD 庫的核心函數 imagecopy() 及其進階版本 imagecopyresampled()、imagecopymerge() 為此提供了強大支撐。本文將從基礎複製到縮放合成、透明度控制、水印實戰,全方位講解圖片合成技術。
| 函數 | 用途 | 關鍵特性 |
|---|---|---|
imagecopy() | **等比複製** | 源圖像素 1:1 複製到目標,不縮放,速度最快 |
imagecopyresized() | **縮放複製** (低質量) | 支援縮放,但無插值算法,縮放後鋸齒明顯 |
imagecopyresampled() | **高質量縮放複製** | 雙三次插值,**生產環境首選**,畫質平滑 |
imagecopymerge() | **帶透明度合成** | 支援 pct 參數 (0-100) 控制源圖不透明度,適用水印 |
imagecopymergegray() | 灰度合成 | 轉灰度後合成,較少用 |
<?php
// 1. 載入背景圖 (畫布)
$background = imagecreatefromjpeg('photo.jpg');
if (!$background) die('背景圖載入失敗');
// 2. 載入水印圖 (源圖)
$watermark = imagecreatefrompng('logo.png'); // PNG 支援透明通道
if (!$watermark) die('水印圖載入失敗');
// 3. 獲取水印尺寸
$wmarkWidth = imagesx($watermark);
$wmarkHeight = imagesy($watermark);
// 4. 計算放置位置:右下角 (留 10px 邊距)
$destX = imagesx($background) - $wmarkWidth - 10;
$destY = imagesy($background) - $wmarkHeight - 10;
// 5. 【核心】複製合成
// imagecopy($dst, $src, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h)
imagecopy($background, $watermark, $destX, $destY, 0, 0, $wmarkWidth, $wmarkHeight);
// 6. 輸出
header('Content-Type: image/jpeg');
imagejpeg($background, null, 90); // 品質 90%
// 7. 釋放資源
imagedestroy($background);
imagedestroy($watermark);
?>
當水印圖過大,需按比例縮放後再合成,必須使用 imagecopyresampled()。
<?php
$bg = imagecreatefromjpeg('bg.jpg');
$logo = imagecreatefrompng('big_logo.png');
$bgW = imagesx($bg);
$bgH = imagesy($bg);
$logoW = imagesx($logo);
$logoH = imagesy($logo);
// 目標:Logo 寬度佔背景 20%,高度等比縮放
$targetW = $bgW * 0.2;
$targetH = $logoH * ($targetW / $logoW);
// 計算置中座標
$destX = ($bgW - $targetW) / 2;
$destY = ($bgH - $targetH) / 2;
// 高質量縮放複製
// imagecopyresampled($dst, $src, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)
imagecopyresampled($bg, $logo, $destX, $destY, 0, 0, $targetW, $targetH, $logoW, $logoH);
header('Content-Type: image/jpeg');
imagejpeg($bg);
imagedestroy($bg);
imagedestroy($logo);
?>
<?php
function addWatermark($srcPath, $savePath, $wmText = 'Copyright © 2024') {
// 1. 載入源圖 (自動檢測類型)
$info = getimagesize($srcPath);
$mime = $info['mime'];
$createFunc = 'imagecreatefrom' . (($mime === 'image/jpeg') ? 'jpeg' : (($mime === 'image/png') ? 'png' : 'gif'));
$image = $createFunc($srcPath);
// 2. 啟用 Alpha 混合 (關鍵:讓 PNG 透明通道正常工作)
imagealphablending($image, true);
imagesavealpha($image, true);
// --- A. 圖片水印 (半透明 Logo) ---
$logo = imagecreatefrompng('watermark_logo.png');
$logoW = imagesx($logo); $logoH = imagesy($logo);
$margin = 20;
$dstX = imagesx($image) - $logoW - $margin;
$dstY = imagesy($image) - $logoH - $margin;
// 使用 imagecopymerge 控制不透明度 (50%)
imagecopymerge($image, $logo, $dstX, $dstY, 0, 0, $logoW, $logoH, 50);
imagedestroy($logo);
// --- B. 文字水印 (對角線平鋪) ---
$font = __DIR__ . '/fonts/SourceHanSansTC-Regular.otf';
$textColor = imagecolorallocatealpha($image, 255, 255, 255, 80); // 白色,透明度 80/127
$fontSize = 20;
$angle = -30; // 傾斜角度
$bbox = imagettfbbox($fontSize, $angle, $font, $wmText);
$textW = $bbox[2] - $bbox[0];
$textH = $bbox[1] - $bbox[7];
// 平鋪繪製
for ($x = -$textW; $x < imagesx($image); $x += $textW + 50) {
for ($y = -$textH; $y < imagesy($image); $y += $textH + 50) {
imagettftext($image, $fontSize, $angle, $x, $y, $textColor, $font, $wmText);
}
}
// 3. 保存
imagejpeg($image, $savePath, 90);
imagedestroy($image);
return true;
}
// 使用
addWatermark('original.jpg', 'watermarked.jpg');
?>
// 處理 PNG 透明背景時,必須開啟:
imagealphablending($canvas, true); // 開啟混合模式
imagesavealpha($canvas, true); // 保存完整 Alpha 通道 (而非單色透明)
若不開啟 `imagesavealpha(true)`,輸出的 PNG 透明區域會變黑或變白。
| 現象 | 原因 | 解決 |
|---|---|---|
| 水印背景有白邊/黑邊 | PNG 非真透明,或 Alpha 混合未開啟 | 確保水印為真 Alpha 透明 PNG;開啟 `imagealphablending` + `imagesavealpha` |
| 合成後圖片變模糊 | 使用 `imagecopyresized` 而非 `resampled` | 改用 `imagecopyresampled()` |
| 透明度不生效 | 用 `imagecopy` 複製帶透明度的 PNG | 用 `imagecopymerge($dst, $src, ..., pct)` 或確保 Alpha 混合開啟 |
| 座標錯位 | 混淆 src/dst 參數順序 | 記住順序:`$dst, $src, $dst_x, $dst_y, $src_x, $src_y...` |
imagecopy 系列函數是 PHP GD 圖片合成的基石。掌握 四大核心變體 的適用場景:
結合座標計算、記憶體管理、錯誤處理,即可構建穩健的圖片水印、海報生成、縮圖裁剪服務。對於高併發、超大圖、複雜濾鏡場景,建議遷移至 ImageMagick (Imagick) 或 Go/Rust 圖像微服務。