為圖片新增浮水印是版權保護、品牌曝光的常見需求。本文提供支援 **PNG/JPG/GIF** 三格式、九宮格定位、Alpha 透明度控制、完整錯誤碼回傳的生產級函式 `img_water_mark()`,並解析 GD 庫圖片資源建立與合成細節。
<?php
/**
* 圖片加浮水印
* @param string $srcImg 源圖路徑
* @param string $waterImg 浮水印圖路徑 (建議 PNG 含透明通道)
* @param string|null $savePath 儲存目錄 (null=源圖目錄)
* @param string|null $saveName 儲存檔名 (null=源圖檔名)
* @param int $position 1-9 九宮格 (1=左上, 3=中上, 5=右下預設, 9=右下)
* @param int $alpha 透明度 0-100 (0=全透明, 100=不透明)
* @return string|int 成功回傳新檔路徑,失敗回傳負數錯誤碼
*/
function img_water_mark(
string $srcImg,
string $waterImg,
?string $savePath = null,
?string $saveName = null,
int $position = 5,
int $alpha = 30
): string|int {
// 1. 解析源圖資訊
$srcInfo = @getimagesize($srcImg);
if (!$srcInfo) return -1; // 源圖不存在或非圖片
$waterInfo = @getimagesize($waterImg);
if (!$waterInfo) return -2; // 浮水印不存在或非圖片
// 2. 建立 GD 圖片資源 (支援三格式)
$srcRes = image_create_from_ext($srcImg);
if (!$srcRes) return -3; // 源圖資源建立失敗
$waterRes = image_create_from_ext($waterImg);
if (!$waterRes) return -4; // 浮水印資源建立失敗
// 3. 開啟 Alpha 混合 (關鍵:保持浮水印透明度)
imagealphablending($srcRes, true);
imagesavealpha($srcRes, true);
imagealphablending($waterRes, true);
imagesavealpha($waterRes, true);
// 4. 計算九宮格座標
[$dstX, $dstY] = calculate_position($srcInfo, $waterInfo, $position);
// 5. 合成 (imagecopymerge 支援透明度)
// 注意:imagecopymerge 對 PNG Alpha 通道支援有限,純 PNG 疊加建議用 imagecopy
if ($waterInfo[2] === IMAGETYPE_PNG && $alpha >= 100) {
imagecopy($srcRes, $waterRes, $dstX, $dstY, 0, 0, $waterInfo[0], $waterInfo[1]);
} else {
imagecopymerge($srcRes, $waterRes, $dstX, $dstY, 0, 0, $waterInfo[0], $waterInfo[1], $alpha);
}
// 6. 依源圖格式儲存
$saveFile = ($savePath ?? dirname($srcImg)) . '/' . ($saveName ?? basename($srcImg));
$result = match ($srcInfo[2]) {
IMAGETYPE_GIF => imagegif($srcRes, $saveFile),
IMAGETYPE_JPEG => imagejpeg($srcRes, $saveFile, 90), // 品質 90
IMAGETYPE_PNG => imagepng($srcRes, $saveFile, 6), // 壓縮 6
default => false
};
// 7. 清理資源
imagedestroy($srcRes);
imagedestroy($waterRes);
return $result ? $saveFile : -5; // 儲存失敗
}
// 輔助:根據副檔名/MIME 建立資源
function image_create_from_ext(string $file): \GdImage|false {
$info = @getimagesize($file);
return match ($info[2] ?? 0) {
IMAGETYPE_GIF => @imagecreatefromgif($file),
IMAGETYPE_JPEG => @imagecreatefromjpeg($file),
IMAGETYPE_PNG => @imagecreatefrompng($file),
IMAGETYPE_WEBP => @imagecreatefromwebp($file),
default => false
};
}
// 輔助:九宮格座標計算
function calculate_position(array $src, array $water, int $pos): array {
$srcW = $src[0]; $srcH = $src[1];
$wW = $water[0]; $wH = $water[1];
$positions = [
1 => [0, 0], // 左上
2 => [($srcW - $wW) / 2, 0], // 中上
3 => [$srcW - $wW, 0], // 右上
4 => [0, ($srcH - $wH) / 2], // 左中
5 => [($srcW - $wW) / 2, ($srcH - $wH) / 2], // 正中
6 => [$srcW - $wW, ($srcH - $wH) / 2], // 右中
7 => [0, $srcH - $wH], // 左下
8 => [($srcW - $wW) / 2, $srcH - $wH], // 中下
9 => [$srcW - $wW, $srcH - $wH], // 右下
];
return $positions[$pos] ?? [0, 0];
}
?>
<?php
// 基本使用 (右下角、30% 透明度)
$newFile = img_water_mark('photo.jpg', 'logo.png');
if (is_int($newFile)) {
echo "錯誤碼: $newFile";
} else {
echo "成功: $newFile";
}
// 進階:中上位置、完全不透明、自訂輸出
$newFile = img_water_mark(
'banner.png',
'watermark.png',
'/var/www/uploads/watermarked',
'wm_' . date('YmdHis') . '.png',
2, // 中上
100 // 不透明
);
?>
| 技術點 | 說明 | 最佳實踐 |
|---|---|---|
| Alpha 混合順序 | 先 `imagealphablending($dst, true)` 再 `imagesavealpha($dst, true)` | 目標圖開啟混合並保存 Alpha,源圖 (浮水印) 同理 |
| imagecopy vs imagecopymerge | `copy` 完整複製含 Alpha;`copymerge` 指定透明度但會丟失源圖 Alpha 細節 | 浮水印為 PNG 且需完整透明度用 `copy`;需可調透明度用 `copymerge` |
| GIF 動畫支援 | GD 僅處理第一幀,動畫 GIF 加浮水印會變靜態 | 需保持動畫請用 Imagick / FFmpeg 逐幀處理 |
| 記憶體管理 | 大圖 (如 4000x3000) 佔用 ~48MB/張 | 處理完立即 `imagedestroy()`;批次處理間 `unset` 變數 |
| JPEG 品質/ PNG 壓縮 | `imagejpeg($res, $file, 90)`、`imagepng($res, $file, 6)` | JPEG 85-90 平衡品質/大小;PNG 6-9 壓縮等級 |
| 代碼 | 含義 | 排查方向 |
|---|---|---|
| -1 | 源圖不存在/非圖片 | 檢查路徑、檔案權限、MIME 型別 |
| -2 | 浮水印不存在/非圖片 | 同 -1 |
| -3 | 源圖 GD 資源建立失敗 | GD 未支援該格式、檔案損壞、記憶體不足 |
| -4 | 浮水印 GD 資源建立失敗 | 同 -3 |
| -5 | 儲存失敗 | 目錄不可寫、磁碟滿、格式不支援儲存 |
<?php
function img_text_watermark(
string $srcImg, string $text, int $position = 5,
int $fontSize = 20, string $color = 'ffffff', int $alpha = 50
): string|int {
$srcInfo = @getimagesize($srcImg);
if (!$srcInfo) return -1;
$srcRes = image_create_from_ext($srcImg);
if (!$srcRes) return -3;
// 計算文字區域 (需 TTF 字體檔)
$fontFile = __DIR__ . '/fonts/msjh.ttc'; // 微軟正黑體
$box = imagettfbbox($fontSize, 0, $fontFile, $text);
$textW = $box[2] - $box[0];
$textH = $box[1] - $box[7];
[$x, $y] = calculate_position($srcInfo, [0=>$textW, 1=>$textH], $position);
// 分配顏色 (含 Alpha)
$r = hexdec(substr($color, 0, 2));
$g = hexdec(substr($color, 2, 2));
$b = hexdec(substr($color, 4, 2));
$textColor = imagecolorallocatealpha($srcRes, $r, $g, $b, 127 - $alpha * 1.27); // 0-100 -> 0-127
imagettftext($srcRes, $fontSize, 0, $x, $y + $textH, $textColor, $fontFile, $text);
// 儲存邏輯同 img_water_mark...
return $saveFile;
}
?>
此實作涵蓋圖片/文字浮水印、多格式支援、透明度精準控制、錯誤碼語意化,可直接整合至 CMS、電商、相簿系統的上傳處理管線。