LoginSignup
39
43

More than 5 years have passed since last update.

PHP: GDで画像のサイズ変更やサムネイル生成のやりかた

Posted at

GDを使う動機のひとつとして、ImageMagicが使えないけどGDが入っているような環境で、画像のサイムネイルを作ることがありますが、APIが分かりにくく結構面倒な作業になります。

幸いにもPHPには、ImagineというGDやImageMagicのAPIを抽象化して画像を処理できるライブラリがあります。が、サムネイル生成にImagineでは元画像の解像度が大きくなればなるほど、メモリの消費量が増えていくという欠点があり、1000x1000のこの画像を500x500にリサイズするだけで13.75MB消費します。従って、解像度の高い画像のサムネイルの作成については、直にGDのライブラリを使ったほうがメモリの消費を30〜50%くらい抑えられます。

ImagineとGDのメモリ最大消費量

画像サイズ Imagine GD
1000x1000 13.75MB 6.00MB
1000x2000 27.25MB 10.25MB
1000x3000 40.50MB 15.00MB
1000x4000 54.25MB 19.50MB

画像のサイズを変形する関数

サムネイル作成のコードを一気に書く前に、画像のサイズを変形するだけの関数を実装しておくと便利です。この関数はアスペクト比の計算を気にしないので、実装時や改修時に混乱しにくて良いです。

/**
 * 画像のサイズを変形して保存する
 * @param string $srcPath
 * @param string $dstPath
 * @param int $width
 * @param int $height
 */
function transform_image_size($srcPath, $dstPath, $width, $height)
{
    list($originalWidth, $originalHeight, $type) = getimagesize($srcPath);
    switch ($type) {
        case IMAGETYPE_JPEG:
            $source = imagecreatefromjpeg($srcPath);
            break;
        case IMAGETYPE_PNG:
            $source = imagecreatefrompng($srcPath);
            break;
        case IMAGETYPE_GIF:
            $source = imagecreatefromgif($srcPath);
            break;
        default:
            throw new RuntimeException("サポートしていない画像形式です: $type");
    }

    $canvas = imagecreatetruecolor($width, $height);
    imagecopyresampled($canvas, $source, 0, 0, 0, 0, $width, $height, $originalWidth, $originalHeight);
    imagejpeg($canvas, $dstPath);
    imagedestroy($source);
    imagedestroy($canvas);
}

アスペクト比を計算するアルゴリズム

次にアスペクト比を計算するアルゴリズムを実装します。いろんなアルゴリズムがあると思いますが、ここで紹介するのは内接サイズを計算するアルゴリズムになります。

/**
 * 内接サイズを計算する
 * @param int $width
 * @param int $height
 * @param int $containerWidth
 * @param int $containerHeight
 * @return array
 */
function get_contain_size($width, $height, $containerWidth, $containerHeight)
{
    $ratio = $width / $height;
    $containerRatio = $containerWidth / $containerHeight;
    if ($ratio > $containerRatio) {
        return [$containerWidth, intval($containerWidth / $ratio)];
    } else {
        return [intval($containerHeight * $ratio), $containerHeight];
    }
}

こんな感じで、コンテナのサイズから、最適な内接サイズをタプルで返します。

php > var_dump(get_contain_size(500, 500, 100, 100));
array(2) {
  [0]=>
  int(100)
  [1]=>
  int(100)
}
php > var_dump(get_contain_size(640, 480, 100, 100));
array(2) {
  [0]=>
  int(100)
  [1]=>
  int(75)
}
php > var_dump(get_contain_size(50, 50, 100, 100));
array(2) {
  [0]=>
  int(100)
  [1]=>
  int(100)
}

サムネイルを作る関数を実装する

最後に、上の2つの関数を組み合わせてサムネイルを作成する関数を実装します。

/**
 * 画像のサムネイルを保存する
 * @param string $srcPath
 * @param string $dstPath
 * @param int $maxWidth
 * @param int $maxHeight
 */
function make_thumbnail($srcPath, $dstPath, $maxWidth, $maxHeight)
{
    list($originalWidth, $originalHeight) = getimagesize($srcPath);
    list($canvasWidth, $canvasHeight) = get_contain_size($originalWidth, $originalHeight, $maxWidth, $maxHeight);
    transform_image_size($srcPath, $dstPath, $canvasWidth, $canvasHeight);
}

おわり

GDを使って画像のサイズ変更・アスペクト比を維持したサイムネイル画像の生成方法を紹介しました。もうPHPで画像処理するのはやめたいです(´・ω・`)

39
43
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
43