在 PHP 開發中,圖片裁切、縮圖、區域截取 是常見需求。核心函式 imagecopyresampled() 功能強大但參數眾多,初學者容易混淆「來源座標」與「目標座標」。本文將逐一拆解參數含義,並提供三種典型應用場景的完整代碼:固定區域裁切、裁切後放大、裁切後縮小。
bool imagecopyresampled(
GdImage $dst_image, // 目標圖片資源(輸出畫布)
GdImage $src_image, // 來源圖片資源
int $dst_x, // 目標畫布起始 X 座標
int $dst_y, // 目標畫布起始 Y 座標
int $src_x, // 來源圖片起始 X 座標(裁切區域左上角)
int $src_y, // 來源圖片起始 Y 座標
int $dst_w, // 目標畫布寬度(輸出寬度)
int $dst_h, // 目標畫布高度(輸出高度)
int $src_w, // 來源裁切區域寬度
int $src_h // 來源裁切區域高度
): bool
來源圖片 (Source) 目標畫布 (Destination)
+-------------------------+ +-------------------------+
| | | |
| (src_x, src_y) ●------|--------->| ● (dst_x, dst_y) |
| | | | | | |
| | src_w | | | | dst_w |
| | | | | | |
| ●-------|--------| | ●----------------|
| src_h | dst_h |
+-------------------------+ +-------------------------+
流程:從來源 (src_x, src_y) 切出 src_w × src_h 區域
→ 縮放/拉伸至 dst_w × dst_h
→ 繪製到目標畫布 (dst_x, dst_y) 位置
需求:從原圖 (7, 174) 起裁切 120×42 區域,直接輸出同尺寸圖片。
<?php
header('Content-Type: image/jpeg');
$filename = 'book_rabbit_rule.jpg';
$srcImg = imagecreatefromjpeg($filename);
// 輸出尺寸 = 裁切尺寸
$dstW = 120;
$dstH = 42;
$dstImg = imagecreatetruecolor($dstW, $dstH); // 真彩色畫布,避免調色盤失真
// 參數對照:dst(0,0) src(7,174) 輸出120x42 來源區域120x42
imagecopyresampled($dstImg, $srcImg,
0, 0, // 目標起點
7, 174, // 來源起點(裁切左上角)
$dstW, $dstH, // 目標寬高
120, 42 // 來源區域寬高
);
imagejpeg($dstImg, null, 90); // 輸出瀏覽器,品質 90
imagedestroy($dstImg);
imagedestroy($srcImg);
需求:同樣從 (100, 30) 裁切區域,但輸出放大為 500×500。
$dstW = 500;
$dstH = 500;
$dstImg = imagecreatetruecolor($dstW, $dstH);
imagecopyresampled($dstImg, $srcImg,
0, 0, // 目標從 (0,0) 開始畫
100, 30, // 來源裁切起點
$dstW, $dstH, // 目標大尺寸(放大)
120, 42 // 來源區域大小(同場景一)
);
需求:裁切同一區域,輸出為 10×10 縮圖。
$dstW = 10;
$dstH = 10;
$dstImg = imagecreatetruecolor($dstW, $dstH);
imagecopyresampled($dstImg, $srcImg,
0, 0,
100, 30,
$dstW, $dstH, // 目標小尺寸(縮小)
120, 42
);
實務上常需「依最大寬高限制等比縮放」,以下為可直接復用的函式:
/**
* 等比例縮放圖片並輸出或儲存
* @param string $srcPath 來源圖路徑
* @param int $maxW 最大寬度
* @param int $maxH 最大高度
* @param string|null $savePath 儲存路徑,null 直接輸出瀏覽器
* @param int $quality JPEG 品質 0-100
*/
function smartResize(string $srcPath, int $maxW, int $maxH, ?string $savePath = null, int $quality = 90): bool
{
[$srcW, $srcH, $type] = getimagesize($srcPath);
if (!$srcW) return false;
// 計算縮放比例
$ratio = min($maxW / $srcW, $maxH / $srcH);
if ($ratio >= 1) { // 原圖已小於限制,直接複製或輸出
if ($savePath) return copy($srcPath, $savePath);
header('Content-Type: ' . image_type_to_mime_type($type));
readfile($srcPath);
return true;
}
$dstW = (int)round($srcW * $ratio);
$dstH = (int)round($srcH * $ratio);
$srcImg = match ($type) {
IMAGETYPE_JPEG => imagecreatefromjpeg($srcPath),
IMAGETYPE_PNG => imagecreatefrompng($srcPath),
IMAGETYPE_GIF => imagecreatefromgif($srcPath),
IMAGETYPE_WEBP => imagecreatefromwebp($srcPath),
default => throw new Exception('Unsupported image type'),
};
$dstImg = imagecreatetruecolor($dstW, $dstH);
// 保持 PNG/GIF/WEBP 透明度
if ($type !== IMAGETYPE_JPEG) {
imagealphablending($dstImg, false);
imagesavealpha($dstImg, true);
$transparent = imagecolorallocatealpha($dstImg, 255, 255, 255, 127);
imagefill($dstImg, 0, 0, $transparent);
}
imagecopyresampled($dstImg, $srcImg, 0, 0, 0, 0, $dstW, $dstH, $srcW, $srcH);
$result = match ($type) {
IMAGETYPE_JPEG => imagejpeg($dstImg, $savePath, $quality),
IMAGETYPE_PNG => imagepng($dstImg, $savePath, 9),
IMAGETYPE_GIF => imagegif($dstImg, $savePath),
IMAGETYPE_WEBP => imagewebp($dstImg, $savePath, $quality),
};
imagedestroy($srcImg);
imagedestroy($dstImg);
return $result;
}
// 使用範例
smartResize('upload/photo.jpg', 800, 600, 'thumb/photo_800x600.jpg');
| 問題 | 原因 | 解決 |
|---|---|---|
| 輸出圖片模糊/鋸齒 | 用 imagecopyresized 而非 resampled | 務必用 imagecopyresampled 重採樣 |
| PNG 透明變黑底 | 未開啟 alpha blending | 見進階範例:imagesavealpha + imagealphablending |
| 記憶體耗盡 | 大圖直接載入 | 分塊處理或增加 memory_limit;考慮用 Imagick |
| 座標越界 | src_x + src_w > 圖寬 | 裁切前驗證座標範圍 |
| 輸出檔案為 0 bytes | 未檢查 imagejpeg 回傳值 | 檢查回傳值、確認目錄可寫入 |
掌握 imagecopyresampled() 的「來源區域 → 目標區域」映射關係,即可靈活實現裁切、縮放、縮圖等所有圖片幾何變換。建議將常用邏輯封裝為工具類或 Helper 函式,專案中統一呼叫,減少重複代碼與錯誤。