將重複的 GD 圖片操作封裝為 `Image` 類別,提供文字水印、圖片水印、縮略圖生成、區域裁切、直接輸出/儲存等功能,自動管理資源生命週期,避免記憶體洩漏。
<?php
class Image {
private $resource;
private $info;
public $type; // 副檔名:jpg, png, gif...
public function __construct(string $src) {
$this->info = @getimagesize($src);
if (!$this->info) throw new InvalidArgumentException("Invalid image: $src");
$this->type = image_type_to_extension($this->info[2], false); // jpg, png, gif...
$func = "imagecreatefrom{$this->type}";
if (!function_exists($func)) throw new RuntimeException("Unsupported image type: {$this->type}");
$this->resource = $func($src);
if (!$this->resource) throw new RuntimeException("Failed to create image resource from: $src");
}
/** 文字水印 */
public function textWatermark(string $fontFile, string $text, int $size, array $color, array $position, int $angle = 0): self {
$color = imagecolorallocatealpha($this->resource, $color[0], $color[1], $color[2], $color[3] ?? 0);
imagettftext($this->resource, $size, $angle, $position[0], $position[1], $color, $fontFile, $text);
return $this;
}
/** 圖片水印 */
public function imageWatermark(string $watermarkPath, array $dstPos, int $opacity = 50): self {
$wmInfo = @getimagesize($watermarkPath);
if (!$wmInfo) throw new InvalidArgumentException("Invalid watermark: $watermarkPath");
$wmType = image_type_to_extension($wmInfo[2], false);
$createFunc = "imagecreatefrom{$wmType}";
$watermark = $createFunc($watermarkPath);
imagecopymerge($this->resource, $watermark, $dstPos[0], $dstPos[1], 0, 0, $wmInfo[0], $wmInfo[1], $opacity);
imagedestroy($watermark);
return $this;
}
/** 縮略圖(等比縮放) */
public function thumbnail(int $maxWidth, int $maxHeight): self {
$srcW = $this->info[0];
$srcH = $this->info[1];
$ratio = min($maxWidth / $srcW, $maxHeight / $srcH);
$newW = (int)($srcW * $ratio);
$newH = (int)($srcH * $ratio);
$thumb = imagecreatetruecolor($newW, $newH);
// 保持透明度(PNG/GIF)
if ($this->type === 'png' || $this->type === 'gif') {
imagealphablending($thumb, false);
imagesavealpha($thumb, true);
$transparent = imagecolorallocatealpha($thumb, 0, 0, 0, 127);
imagefill($thumb, 0, 0, $transparent);
}
imagecopyresampled($thumb, $this->resource, 0, 0, 0, 0, $newW, $newH, $srcW, $srcH);
imagedestroy($this->resource);
$this->resource = $thumb;
$this->info[0] = $newW;
$this->info[1] = $newH;
return $this;
}
/** 裁切(固定尺寸,從指定座標) */
public function crop(int $width, int $height, int $srcX = 0, int $srcY = 0): self {
$crop = imagecreatetruecolor($width, $height);
if ($this->type === 'png' || $this->type === 'gif') {
imagealphablending($crop, false);
imagesavealpha($crop, true);
}
imagecopyresampled($crop, $this->resource, 0, 0, $srcX, $srcY, $width, $height, $width, $height);
imagedestroy($this->resource);
$this->resource = $crop;
$this->info[0] = $width;
$this->info[1] = $height;
return $this;
}
/** 直接輸出到瀏覽器 */
public function output(): void {
header("Content-Type: {$this->info['mime']}");
$func = "image{$this->type}";
$func($this->resource);
}
/** 儲存到檔案 */
public function save(string $destPath): bool {
$func = "image{$this->type}";
return $func($this->resource, $destPath);
}
/** 取得資源(進階操作) */
public function getResource() { return $this->resource; }
/** 取得資訊 */
public function getInfo() { return $this->info; }
public function __destruct() {
if ($this->resource) imagedestroy($this->resource);
}
}
?>
<?php
require 'Image.php';
try {
// 1. 基本縮圖 + 文字水印
$img = new Image('photo.jpg');
$img->thumbnail(800, 600)
->textWatermark('font.ttf', '© MySite 2024', 20, [255,255,255,50], [20, 20])
->save('thumb_watermarked.jpg');
// 2. 圖片水印(Logo)
$img2 = new Image('banner.png');
$img2->imageWatermark('logo.png', [10, 10], 70)
->output(); // 直接輸出瀏覽器
// 3. 裁切正方形頭像
$avatar = new Image('upload.jpg');
$avatar->crop(200, 200, 50, 30) // 從 (50,30) 裁切 200x200
->save('avatar_200.jpg');
// 4. 鏈式操作:縮圖 → 水印 → 裁切 → 儲存
new Image('source.jpg')
->thumbnail(1200, 800)
->textWatermark('font.ttf', 'Watermark', 30, [0,0,0,30], [30, 30])
->crop(400, 300, 100, 50)
->save('final.jpg');
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
?>
| 功能 | 實作方向 |
|---|---|
| 智能裁切 | 偵測人臉/主體區域(OpenCV/模型)自動計算裁切座標 |
| 多格式輸出 | WebP、AVIF 支援(需 GD 編譯支援或用 Imagick) |
| 圖片壓縮品質 | `imagejpeg($res, $path, 85)` 品質參數控制 |
| EXIF 保留/移除 | 隱私保護:上傳頭像自動剝離 EXIF |
| Imagick 適配器 | 大圖/多幀/複雜濾鏡用 Imagick,介面統一 |