GD 庫操作圖片固定四步:**打開 → 操作 → 輸出 → 銷毀**。封裝類將前三步通用化,暴露具體操作方法(水印/壓縮/裁剪),最後統一輸出/保存/銷毀。
<?php
class ImageProcessor {
private $image; // GD 圖片資源
private $info; // getimagesize 信息
private $type; // 擴展名(無點):jpg/png/gif/webp
/**
* 構造函數:打開圖片
* @param string $src 圖片路徑
*/
public function __construct(string $src) {
if (!file_exists($src)) throw new Exception('圖片不存在: ' . $src);
$this->info = getimagesize($src);
if (!$this->info) throw new Exception('非法圖片文件: ' . $src);
$this->type = image_type_to_extension($this->info[2], false); // jpg/png/gif/webp
$func = 'imagecreatefrom' . $this->type;
if (!function_exists($func)) throw new Exception('不支持的圖片類型: ' . $this->type);
$this->image = $func($src);
if (!$this->image) throw new Exception('圖片資源創建失敗');
}
/**
* 文字水印
* @param string $font 字體文件路徑
* @param string $text 水印文字
* @param int $size 字體大小
* @param array $color RGBA 顏色 [r,g,b,a] a:0-127
* @param array $position [x, y] 左上角坐標
* @param int $angle 傾斜角度
*/
public function addTextWatermark(
string $font, string $text, int $size, array $color, array $position, int $angle = 0
): self {
$col = imagecolorallocatealpha($this->image, $color[0], $color[1], $color[2], $color[3]);
imagettftext($this->image, $size, $angle, $position[0], $position[1], $col, $font, $text);
return $this; // 支持鏈式調用
}
/**
* 圖片水印
* @param string $watermarkPath 水印圖片路徑
* @param array $position [x, y] 貼合位置
* @param int $opacity 透明度 0-100
*/
public function addImageWatermark(string $watermarkPath, array $position, int $opacity = 50): self {
$info = getimagesize($watermarkPath);
$func = 'imagecreatefrom' . image_type_to_extension($info[2], false);
$watermark = $func($watermarkPath);
imagecopymerge($this->image, $watermark, $position[0], $position[1], 0, 0, $info[0], $info[1], $opacity);
imagedestroy($watermark);
return $this;
}
/**
* 壓縮縮放
* @param array $maxSize [maxWidth, maxHeight] 最大尺寸,保持比例
*/
public function resize(array $maxSize): self {
$srcW = $this->info[0];
$srcH = $this->info[1];
$ratio = min($maxSize[0] / $srcW, $maxSize[1] / $srcH);
if ($ratio >= 1) return $this; // 無需放大
$newW = (int)($srcW * $ratio);
$newH = (int)($srcH * $ratio);
$thumb = imagecreatetruecolor($newW, $newH);
// 保持透明通道
if (in_array($this->type, ['png', 'gif', 'webp'])) {
imagealphablending($thumb, false);
imagesavealpha($thumb, true);
}
imagecopyresampled($thumb, $this->image, 0, 0, 0, 0, $newW, $newH, $srcW, $srcH);
imagedestroy($this->image);
$this->image = $thumb;
$this->info[0] = $newW;
$this->info[1] = $newH;
return $this;
}
/**
* 裁剪
* @param array $size [width, height] 裁剪尺寸
* @param array $position [x, y] 起始坐標
*/
public function crop(array $size, array $position): self {
$crop = imagecreatetruecolor($size[0], $size[1]);
if (in_array($this->type, ['png', 'gif', 'webp'])) {
imagealphablending($crop, false);
imagesavealpha($crop, true);
}
imagecopyresampled($crop, $this->image, 0, 0, $position[0], $position[1], $size[0], $size[1], $size[0], $size[1]);
imagedestroy($this->image);
$this->image = $crop;
$this->info[0] = $size[0];
$this->info[1] = $size[1];
return $this;
}
/**
* 直接輸出到瀏覽器
*/
public function output(): void {
header('Content-Type: ' . $this->info['mime']);
$func = 'image' . $this->type;
$func($this->image);
}
/**
* 保存到文件
* @param string $path 保存路徑(含文件名)
* @param int $quality 壓縮質量 0-100(僅 jpg/webp 有效)
*/
public function save(string $path, int $quality = 90): bool {
$func = 'image' . $this->type;
$dir = dirname($path);
if (!is_dir($dir)) mkdir($dir, 0755, true);
$result = match ($this->type) {
'jpg', 'jpeg', 'webp' => $func($this->image, $path, $quality),
'png' => $func($this->image, $path, 9), // PNG 壓縮級別 0-9
default => $func($this->image, $path),
};
return $result;
}
/**
* 解構函數:自動銷毀資源
*/
public function __destruct() {
if ($this->image) imagedestroy($this->image);
}
}
<?php
require 'ImageProcessor.php';
try {
// 1. 文字水印 + 壓縮 + 保存
$img = new ImageProcessor('source.jpg');
$img->addTextWatermark(
'fonts/msyh.ttc', // 字體
'© MySite 2024', // 文字
24, // 大小
[255, 255, 255, 30], // 白色半透明
[20, 20] // 位置
)->resize([800, 600])->save('output/watermarked.jpg');
// 2. 圖片水印 + 裁剪 + 直接輸出
$img2 = new ImageProcessor('photo.png');
$img2->addImageWatermark('logo.png', [10, 10], 40)
->crop([400, 300], [50, 50])
->output();
// 3. 批量處理
foreach (glob('uploads/*.jpg') as $file) {
$out = 'processed/' . basename($file);
(new ImageProcessor($file))
->resize([1200, 900])
->addTextWatermark('fonts/msyh.ttc', 'Demo', 20, [0,0,0,50], [10,10])
->save($out, 85);
}
} catch (Exception $e) {
echo 'Error: ' . $e->getMessage();
}
imagecreatefromwebp/imagewebp,類中已兼容imagefilter($this->image, IMG_FILTER_GRAYSCALE) 等imagerotate()| 問題 | 原因 | 解決 |
|---|---|---|
| 透明背景變黑 | 未開啟 alphablending/savealpha | 縮放/裁剪前設置 imagealphablending(false); imagesavealpha(true); |
| 內存耗盡 | 大圖處理超過 memory_limit | 分塊處理、增加 memory_limit、用 Imagick |
| 字體找不到 | 相對路徑錯誤 | 使用絕對路徑 __DIR__ . '/fonts/msyh.ttc' |
| 輸出前有空白 | PHP 標籤前有換行/BOM | 文件純 PHP 無關閉標籤,編碼 UTF-8 無 BOM |