1. 導入
Web開発において、ユーザーがアップロードした画像をリサイズして保存する機能は非常に一般的です。しかし、PHP標準のGDライブラリを使用して透過背景を持つPNGやGIF画像を扱う際、単にリサイズ処理を行うだけでは透過情報が失われ、背景が真っ黒になってしまうという課題があります。本記事では、透過情報を維持しつつ、安全に画像をリサイズするための実装手法を解説します。
2. 基礎知識
PHPの画像処理において、GDライブラリは標準的かつ軽量な選択肢です。特にリサイズには imagecopyresampled() 関数が用いられます。
ここで重要なのは、透過情報の扱いです。
・PNG: アルファチャネル(透明度)情報を保持する必要があります。
・GIF: 特定のカラーインデックスを透明として扱う仕組みです。
また、実務ではセキュリティの観点から、拡張子だけで判断せず finfo クラスを用いてMIMEタイプを確認することが必須となります。
3. 実装/解決策
透過情報を守るための鍵は、コピー先となる新しい画像リソースを作成した直後の「初期設定」にあります。
PNGの場合は imagealphablending() をオフにし、imagesavealpha() をオンにする必要があります。これにより、元の透過レイヤーを正しく新しい画像に引き継ぐことができます。また、リサイズ後のファイル名は推測されないよう、sha1 などを用いてユニークな文字列を生成するのが安全です。
4. サンプルプログラム
以下のコードは、アップロードされた画像を透過情報を維持したまま指定サイズにリサイズする関数です。
/
function uploadImage($tmpName, $dir, $maxWidth, $maxHeight) {
// MIMEタイプを確認し、安全性を確保
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($tmpName);
// 画像タイプに応じた読み込み
if ($mime == ‘image/jpeg’) {
$ext = ‘.jpg’;
$image1 = imagecreatefromjpeg($tmpName);
} elseif ($mime == ‘image/png’) {
$ext = ‘.png’;
$image1 = imagecreatefrompng($tmpName);
} elseif ($mime == ‘image/gif’) {
$ext = ‘.gif’;
$image1 = imagecreatefromgif($tmpName);
} else {
return false;
}
list($width1, $height1) = getimagesize($tmpName);
$scale = min($maxWidth / $width1, $maxHeight / $height1, 1.0);
$width2 = (int)($width1 $scale);
$height2 = (int)($height1 $scale);
$image2 = imagecreatetruecolor($width2, $height2);
// 透過処理の設定
if ($ext == ‘.gif’) {
$transparent1 = imagecolortransparent($image1);
if ($transparent1 >= 0) {
$index = imagecolorsforindex($image1, $transparent1);
$transparent2 = imagecolorallocate($image2, $index[‘red’], $index[‘green’], $index[‘blue’]);
imagefill($image2, 0, 0, $transparent2);
imagecolortransparent($image2, $transparent2);
}
} elseif ($ext == ‘.png’) {
// アルファブレンディングを無効化し、透過情報を保持
imagealphablending($image2, false);
$transparent = imagecolorallocatealpha($image2, 0, 0, 0, 127);
imagefill($image2, 0, 0, $transparent);
imagesavealpha($image2, true);
}
// リサイズ実行
imagecopyresampled($image2, $image1, 0, 0, 0, 0, $width2, $height2, $width1, $height1);
if (!file_exists($dir)) mkdir($dir, 0777, true);
// ユニークなファイル名生成
$filename = sha1(uniqid(mt_rand(), true)) . $ext;
$saveTo = rtrim($dir, ‘/\\’) . ‘/’ . $filename;
// 保存
if ($ext == ‘.jpg’) imagejpeg($image2, $saveTo, 80);
elseif ($ext == ‘.png’) imagepng($image2, $saveTo);
elseif ($ext == ‘.gif’) imagegif($image2, $saveTo);
imagedestroy($image1);
imagedestroy($image2);
return $saveTo;
}
?>
5. 応用・注意点
・メモリ制限: GDライブラリは画像データをメモリ上に展開するため、非常に大きな画像をリサイズすると memory_limit に達してエラーになることがあります。必要に応じて ini_set(‘memory_limit’, ‘256M’); などを調整してください。
・画質劣化: imagejpeg() の第3引数(品質)を調整することで、ファイルサイズと画質のバランスを最適化できます。
・セキュリティ: アップロードされたファイルをそのまま保存するのではなく、本サンプルのようにファイル名をランダム化し、アップロードディレクトリに実行権限を与えない設定を行うことが、Webサーバーを守るための基本です。