PHP PNG画像を投稿すると背景が暗くなる問題に対処

2020年11月20日

PHPで画像投稿処理を組む際には、大きすぎる画像を縮小する処理も盛り込みます。特にサムネイル画像を生成する場合では、画像の再加工処理は必須です。

ところが、PNG画像を投稿してみたところ背景が真っ黒になる現象が発生しました。

JPEGと違ってPNG画像には背景を透明保存する機能があります。
今回背景が透明なPNG画像を投稿して、背景が黒くなりました。

いろいろ調べて対処できましたので、忘備録として記録いたします。

現象は次の通りです。

現象確認にあたり、簡易的な画像加工ソースを組んでみました。
1600ピクセルのPNG画像を100ピクセルに縮小するサンプルです。
それがこちらです。

実行するとこのようになります。(お手数ですがリンククリックをお願いいたします。)

透過PING画像の背景がこのように黒くなりました。

なぜ黒くなるのか調査しました。

まず黒く変換されたPNG画像をPhotoshopで開いてみました。
原本画像と比較して見ると画像の状況が見えました。
それがこちらです。

左が原本画像 右が変換後の画像

左が原本画像で、右が変換後の画像です。
原本画像は背景が透明ですが、変換後は背景が黒く塗りつぶされています。

どうやらPNG画像をPHPで変換する処理に原因がありそうです。
そこで関数仕様などを調べてみたら、明確な原因が判明しました。

原因はimagecreatetruecolor()関数のデフォルト値が黒背景のため

画像を縮小変換する流れはおおまかに以下の通りです。

  1. imagecreatefrompng()関数で原本画像を加工可能状態にする
  2. imagecreatetruecolor()関数で変換用の土台画像を作成する
  3. imagecopyresampled()関数で画像を縮小加工する
  4. imagepng()関数で画像化して保存する

この中に黒く塗りつぶしている関数があるとにらみ、一つ一つ調べてみたらありました。原因は2番の imagecreatetruecolor() 関数でした。

この関数は画像を加工するための土台となる画像を作成します。
関数仕様を調べてみたところ、デフォルトで黒い画像を作ることがわかりました。

そこで試しに imagecreatetruecolor() 関数だけで画像を作るとどうなるかやってみました。それがこちらです。

黒い画像ができました。
単純に黒い画像にPNG画像を掛け合わせたため、背景が黒くなっていたのです。

ちなみにソースは以下の通りです。imagecreatetruecolor()関数だけで画像を作りました。

対応策1:背景色追加法

これは imagecreatetruecolor() で作られた画像に着色するやり方です。
webは大抵背景が白なので、白で着色します。

以下の画像のようにPNG画像の背景が白くなります。

実際にやってみたプログラムがこちらです。

ソースはこちらです。

注目すべきは以下の2つの関数です。

(1)imagecolorallocate()関数

この関数は色を作成する関数です。
以下のように使用します。

①色情報 = imagecolorallocate(②土台画像, ③赤色値, ④緑色値, ⑤青色値);

①色情報 出力作成した色情報
②土台画像 入力imagecreatetruecolor()関数で生成した画像
③赤色値入力赤色の値(1~255または0x00~0xFF)
④緑色値 入力 緑色の値(1~255または0x00~0xFF)
⑤青色値 入力 青色の値(1~255または0x00~0xFF)

③~⑤の色をすべて255(0xFF)にすると、白色になります。
白色のカラー情報が①色情報に保存されます。

(2)imagefill()関数

この関数は画像に色を塗る関数です。
以下のように使用します。

imagefill(①色を塗る画像, ②X座標, ③Y座標, ④色情報);

①色を塗る画像 入力 指定した画像情報に色が塗られます
②X座標 入力 色を塗り始めるX座標位置。0でOKです。
③Y座標 入力 色を塗り始めるY座標位置。0でOKです。
④色情報 入力 imagecolorallocate()関数で作成した色情報

デフォルトの黒色を白色にするという考え方

この方法はimagecreatetruecolor()関数で作成した画像の背景はデフォルトだと黒いため、後から白色で塗りなおすという方法です。

原本画像と比較してみると白色が塗られていることがわかります。

背景が白色のサイトや、背景が白色で違和感ない場合は問題ありませんが、そうでない場合はちょっと困りものです。

疑問:背景と画像が合わない場合どうすればよいの?

こちらのサンプルをご覧ください。
背景色が違うため、違和感が出てしまっています。

こういう場合は、PNG画像作成時に無理やり背景色に合わせる方法もありますが、汎用性が低くなります。スタイルシートで背景色を変更するとすぐにボロが出てしまいます。

なにか対応策はないのでしょうか?

ということで背景色を透明にする方法がないかググってみました。

対応策2:色を透明に指定して透過させる。

PHPの関数を調べていると、色を透明にする関数というものがありました。
それを使って試してみたものがこちらです。

PNGの背景を透明にするサンプル

確かに透明ですが・・・縁取りがついてしまい、ちょっといまいちです。

ちなみにソースはこちらです。

この方法だと縁取りがついてしまいます。
ただ、黒色以外を指定すると透明になりません。

惜しいのですが、この方法は泣く泣くボツです。

もっとうまい方法がありました!
対応策3:アルファチャンネル保存

論より証拠です。
まずはこちらのサンプルプログラムをご覧ください。

完璧に背景が透明なPNG画像になりました。
Photo shopで見比べても理想的な形になりました。

このソースは以下の通りです。

秘密はimagealphablending()関数とimagesavealpha()関数ですが、なぜこの2つの関数で完全透明になったのでしょうか?

理屈がわかると、より深く理解できます。

PHPでは透明背景状態のPNG画像を加工できない。

PNG画像の背景が透明なのは、画像にアルファチャンネルが保存されているためです。

アルファチャンネルとは透明指定をする場合に必要なものです。
Photoshopでいうところの「マスク」といったところでしょうか。

PHPで画像サイズを変更したりする場合、imagecopyresampled()関数を使用しますが、何もしなければアルファチャンネルはPNG画像に保存されません。

アルファチャンネルがPNG画像に保存されないため「何かしらの色が」背景にあてがわれてしまうのです。

ではPHPでアルファチャンネルをPNG画像にする方法がないのか?というと、保存する方法があります。

それがimagesavealpha()関数です。
この関数で指定した画像のアルファチャンネルを保存するかどうかを指定できます。

ただ、ブレンドモードと呼ばれる合成設定がONになっていると、アルファチャンネルは保存できません。そこで、ブレンドモードをオフに設定する必要があります。

ブレンドモードをOFFにする方法がimagealphablending()関数です。
ブレンドモードをオフにし、アルファチャンネルを保存指定にすることで、PNG画像のアルファチャンネルをそのまま保存できるのです。

ブレンドモードは2種類の画像合成をするためのモードですが、画像縮小する場合はPNG画像だけでよいので、合成できなくてもよいのです。

PHPのGD関数は難しいが役に立ちます。

PHPの中でも、画像を操作する関数は難しいものが多いです。
なぜならばイメージが沸かないものばかりだからです。

データベース操作系や、日付操作系、文字変換系などの関数は結果が見えるためわかりやすいですが、GD関数は結果がよくわからないものがあります。

例えば今回使用したimagecreatetruecolor()関数もその一つです。
colorとあるので、色系の関数かなとおもったら、編集するための画像の土台を作る関数です。なかなか想像もできません。

それでも、画像操作系が理解できると制作の幅が一気に広がります。
webとはそのほとんどが文章と画像です。
画像が操作できるようになると、webに関するプログラムにほとんど対応できるようになれます。

とにもかくにも、今回もよい体験をいたしました。

今回のレポートは以上です。
読んでいただいてありがとうございました。


ホームページに関するお悩み事やご相談事がございましたら私どもまでご連絡ください。 鋭意ご対応申し上げます。
ホームページのご提案もさせていただいております