GD 利用、bitmap 形式画像の読み書き
オンラインの phpマニュアルには、user note として、各関数解説ページの下部に、利用法や、注意点などが載っている。 GD では未対応のビットマップを読み込んだり、書き出す関数が紹介されていたので、少々手を加えて、使いやすくしてみました。GDによる画像読み書き簡易化関数のimagecreatefromfileやimage_outputで、ビットマップ形式も取り扱いたいときに、この2つの関数を「gd_bmp_util.php」で保存してincludeします。
利用上の注意: strlen や substr, strpos で1byte単位の作業を行う必要がありますが、
多バイト文字利用サイトでは、時に mbstring.func_overload が 0 以外のことがあります。
strlen や substr, strpos を多バイト対応にしてしまうと、バイト列作業が狂いますので、 「 Overload str*() functions」 少なくともこれはoffとしなければなりません。
よって、mbstring.func_overload は 0,1,4,5 のいずれかを指定してください。
この項目は、php.ini でしか設定出来ないけれど、共有サーバーだと、通常0になってるはず。一応 phpinfo() などでご確認下さい。
strlen や substr, strpos を多バイト対応にしてしまうと、バイト列作業が狂いますので、 「 Overload str*() functions」 少なくともこれはoffとしなければなりません。
よって、mbstring.func_overload は 0,1,4,5 のいずれかを指定してください。
この項目は、php.ini でしか設定出来ないけれど、共有サーバーだと、通常0になってるはず。一応 phpinfo() などでご確認下さい。
bitmap 形式画像の読み込み : ImageCreateFromBMP
参考url http://jp2.php.net/manual/ja/function.imagecreate.php#53879 : 16bit 対応修正 http://jp2.php.net/manual/ja/function.imagecreate.php#81604
bitmap 形式画像を出力 : imageBMP
出力形式は 24bit true color ビットマップ のみです。
参考url http://php.benscom.com/manual/ja/ref.image.php#63689
bitmap 形式でモノクローム画像を出力 : imageMonoBMP
モノクローム画像の要望もあるようなので、読み込み時の情報逆算で出力関数を作ってみました。
とりあえず、パレットデータは、黒かそれ以外で判定、8ピクセルで1バイトデータになるので横幅は8の倍数限定として、細かい換算を省きました。
- 4ビットや8ビットカラー出力用は、パレット番号変換が一般化しにくいので見送りです。
先にパレット番号へ変換した配列で画像データを与えて、pack していけばよさそうではあるけど。
- 以下に3つの関数をまとめて表示します。
/* * GD 利用、bitmap 形式画像の読み込み * comment 編集miz [2009/04/25] →公開サイト移動 [2012/08/28] * @param $filename : 実行中phpからアクセスできる画像パス名 * @return GD truecolor イメージオブジェクト 作成失敗時 false /*********************************************/ /* Fonction: ImageCreateFromBMP */ /* Author: DHKold */ /* Date: The 15th of June 2005 */ /* Version: 2.0B */ /* http://php.benscom.com/manual/ja/function.imagecreate.php#53879 */ /*********************************************/ function ImageCreateFromBMP($filename){ // 画像ファイルをバイナリーモードでopen if (! $f1 = fopen($filename,"rb")) return FALSE; //1 : 概要データのロード:file_type, file_size, reserved, bitmap_offset $FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1,14)); //1' : file type のチェック if ($FILE['file_type'] != 19778) return FALSE; // 19778=> 0x4D42 ->`MB` 先頭2バイトに BM と入っている、リトルエンディアンで読み出すとMB となる //2 : BMPデータのロード: $BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'. '/Vcompression/Vsize_bitmap/Vhoriz_resolution'. '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1,40)); $BMP['colors'] = pow(2,$BMP['bits_per_pixel']); //2' : pixel情報のセット if ($BMP['size_bitmap'] == 0) $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset']; $BMP['bytes_per_pixel'] = $BMP['bits_per_pixel']/8; $BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']); $BMP['decal'] = ($BMP['width']*$BMP['bytes_per_pixel']/4); $BMP['decal'] -= floor($BMP['width']*$BMP['bytes_per_pixel']/4); $BMP['decal'] = 4-(4*$BMP['decal']); if ($BMP['decal'] == 4) $BMP['decal'] = 0; //3 : PALETTEデータのロード: // 16 bit images (= color 65536 )以上ではパレットを持っていないので、8bit colorまでを対象とする $PALETTE = array(); if ($BMP['colors'] < 65536){ $PALETTE = unpack('V'.$BMP['colors'], fread($f1,$BMP['colors']*4)); } //4 : imageデータのロード:ビットごとの色情報読みとり $IMG = fread($f1,$BMP['size_bitmap']); //4' : file からの読みとり完了 fclose($f1); //5 : GD による TrueColor イメージ作成 $res = imagecreatetruecolor($BMP['width'],$BMP['height']); $P = 0; $Y = $BMP['height']-1; $VIDE = chr(0); // 桁合わせ用 //5' : TrueColor イメージの各ビットに色設定 while ($Y >= 0){ $X=0; while ($X < $BMP['width']){ if ($BMP['bits_per_pixel'] == 24){ $COLOR = unpack("V",substr($IMG,$P,3).$VIDE); }elseif ($BMP['bits_per_pixel'] == 16){ $COLOR = unpack("v",substr($IMG,$P,2)); $blue = ($COLOR[1] & 0x001f) << 3; $green = ($COLOR[1] & 0x07e0) >> 3; $red = ($COLOR[1] & 0xf800) >> 8; $COLOR[1] = $red * 65536 + $green * 256 + $blue; }elseif ($BMP['bits_per_pixel'] == 8){ // 8bit palette mode, 256colors $COLOR = unpack("n",$VIDE.substr($IMG,$P,1)); $COLOR[1] = $PALETTE[$COLOR[1]+1]; }elseif ($BMP['bits_per_pixel'] == 4){ $COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1)); if (($P*2)%2 == 0) $COLOR[1] = ($COLOR[1] >> 4) ; else $COLOR[1] = ($COLOR[1] & 0x0F); $COLOR[1] = $PALETTE[$COLOR[1]+1]; }elseif ($BMP['bits_per_pixel'] == 1){ $COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1)); if (($P*8)%8 == 0) $COLOR[1] = $COLOR[1] >>7; elseif (($P*8)%8 == 1) $COLOR[1] = ($COLOR[1] & 0x40)>>6; elseif (($P*8)%8 == 2) $COLOR[1] = ($COLOR[1] & 0x20)>>5; elseif (($P*8)%8 == 3) $COLOR[1] = ($COLOR[1] & 0x10)>>4; elseif (($P*8)%8 == 4) $COLOR[1] = ($COLOR[1] & 0x8)>>3; elseif (($P*8)%8 == 5) $COLOR[1] = ($COLOR[1] & 0x4)>>2; elseif (($P*8)%8 == 6) $COLOR[1] = ($COLOR[1] & 0x2)>>1; elseif (($P*8)%8 == 7) $COLOR[1] = ($COLOR[1] & 0x1); $COLOR[1] = $PALETTE[$COLOR[1]+1]; }else{ return FALSE; } imagesetpixel($res,$X,$Y,$COLOR[1]); // 1 dot 処理完了 $X++; $P += $BMP['bytes_per_pixel']; } // x 方向完了 $Y--; $P+=$BMP['decal']; } // all line end //6 : 作業終了: TrueColor イメージを返す return $res; } /* * bitmap 形式画像を出力 * @param $image : GD2.0 true color object (ImageCreateTrueColorなどで作ったイメージオブジェクトを指定) * @param $filename : 出力先ファイル名、省略時は、php://output =標準出力へ出力。 * return 成功時 true , 出力先に Windows BMP file(24bit true color 形式のみ対応) が出来る 失敗時 false */ function imageBMP (&$image, $filename = false){ if (!$image) return false; if ( empty($filename) ) $filename = 'php://output'; $f = fopen ($filename, "w"); if ( false === $f) return false; /* データ型も比較 書き換え時に間違って!==としてたので修正 2015-10-18 */ //Image dimensions $biWidth = imagesx($image); $biHeight = imagesy($image); $biBPLine = $biWidth * 3; $biStride = ($biBPLine + 3) & ~3; $biSizeImage = $biStride * $biHeight; $bfOffBits = 54; $bfSize = $bfOffBits + $biSizeImage; //BITMAPFILEHEADER fwrite ($f, 'BM', 2); fwrite ($f, pack ('VvvV', $bfSize, 0, 0, $bfOffBits)); //BITMAPINFO (BITMAPINFOHEADER) fwrite ($f, pack ('VVVvvVVVVVV', 40, $biWidth, $biHeight, 1, 24, 0, $biSizeImage, 0, 0, 0, 0)); $numpad = $biStride - $biBPLine; for ($y = $biHeight - 1; $y >= 0; --$y){ // 画像の下端からデータ書き出し for ($x = 0; $x < $biWidth; ++$x){ $col = imagecolorat ($image, $x, $y); fwrite ($f, pack ('V', $col), 3); } for ($i = 0; $i < $numpad; ++$i) fwrite ($f, pack ('C', 0)); } fclose ($f); return true; } /* * モノクローム(1ビットカラー、2値画像) BMP出力 * * width は8の倍数のみ としている , 8で割り切れないときは出力せずfalseを返す。 * * $palette = [0x000000, 0xffffff]; 黒以外は 白に置き換え * @param $image : GD2.0 object (ImageCreate などで作ったイメージオブジェクトを指定) * @param $filename : 出力先ファイル名、省略時は、php://output =標準出力へ出力。 * return 成功時 true , 出力先に Windows BMP file(1bit color 形式) が出来る 失敗時 false */ function imageMonoBMP(&$image, $filename = false ){ if (!$image) return false; $bits_per_pixel = 1 ; $palette = [0x000000, 0xffffff]; $colors = 2; if ( empty($filename) ) $filename = 'php://output'; $f = fopen ($filename, "w"); if ( false === $f) return false; //Image dimensions $biWidth = imagesx($image); if(($biWidth % 8) !=0 ) return false; $biHeight = imagesy($image); $biBPLine = $biWidth / 8; // 8pixel で 1 byte $biStride = ($biBPLine + 3) & ~3; // 1行を4の倍数幅に合わせる $biSizeImage = $biStride * $biHeight; $bfOffBits = 0x3e; // palette なしで 54 + palette あり monochrome 2*4bit =62 = 0x3e $bfSize = $bfOffBits + $biSizeImage; //BITMAPFILEHEADER fwrite ($f, 'BM', 2); $str = pack ('VvvV', $bfSize, 0, 0, $bfOffBits); fwrite ($f, $str); //BITMAPINFO (BITMAPINFOHEADER) $str = pack ('VVVvvVVVVVV', 40, $biWidth, $biHeight, 1, $bits_per_pixel, 0, $biSizeImage, 0, 0, 0, 0); fwrite ($f, $str); // PALETTEINFO $str = pack ('V'.$colors, $palette[0], $palette[1]) ; fwrite ($f, $str); // palette 番号で画像出力 $numpad = $biStride - $biBPLine; for ($y = $biHeight - 1; $y >= 0; --$y){ // 画像の下端からデータ書き出し for ($x = 0; $x < $biWidth; $x+=8){ $c = 0; for($i=0; $i<8; $i++){ // 8ピクセルを1バイト数値にする $col = imagecolorat ($image, $x+$i, $y); $col = ($col==0)?0:1; $c += $col << (7-$i) ; // 左に ビットシフトする (各シフトは "2をかける" 意味 ) } $str = pack ('V', $c) ; fwrite ($f, $str, 1); // 1 byte 分は書くように確保 } for ($i = 0; $i < $numpad; ++$i) fwrite ($f, pack ('C', 0)); } fclose ($f); return true; }
[初回設置日 2012-08-28]
[修正日 2015-10-18] : imageBMP関数内の fopen返値を false との比較にしたとき 型考慮比較演算子の勘違い修正
[ 2015-10-18] : モノクローム画像出力関数も追加
[最終更新日 ]