8月 2014 archive

たまには何か書かないと

気がついたら1週間も放置してしまって
なんだか悲しい気持ちになりました。

神道関連についても特に調べられてないし
技術関連でも、今すぐに書けることがないんですよねぇ。
以前PHP の GD でもう少し書く、とか書きましたが
ちゃんとまとまってないので書けないのです…。

身の上話でも書いてみますね。

来月から違う職場に出向する事になったので
引き継ぎ、というか作業をまとめてるというか、
そんな作業をしているんですが、
どうも、作業の引き継ぎは苦手です。

「何を伝えていいのかわからないのです」

プログラムの引き継ぎなので
コードはそこまで変な書き方していないので
読めばわかるだろうし、
メソッド・クラスの頭には役割・引数・戻り値
をコメントで書いてあるので
それ以上何を資料として残し、伝えればいいのかわからないのです。
また、自分がどこかの職場に入る際も、
資料とかはほぼ無く、コードを読んで
内容・内情を把握することがほとんどですし、
資料があっても、だいたい資料が古くなっていて
コード等、現物を追ったほうが間違いなかったりするんですよね。

誰か
「トラブルと無縁。引き継ぎの方法」
とか本書いてくれないですかね。

JavaScript でお手軽に値を保存する方法

ブラウザを閉じた時、更新した時、
折角入力したフォームの値が消えてしまうの、悲しいですよね。
ただ、入力した都度、Cookieに保存したり、サーバと通信したりなんてやりたくない。
そんな時は、localStorage を使って保存しよう。
そのまま使うと面倒くさいので、チョット手を入れました。

配列に、localStorage への保存メソッドを追加してます。
(既存クラスを汚すのでローカルで使うだけにしてね♪)

// 配列クラスにセーブメソッドを追加
Array.prototype.Save = function(savekey){
	// キーがない場合は何もしない
	if(savekey == undefined || savekey == null) return;
	// 配列をJsonに直して記憶
	localStorage.setItem(savekey,JSON.stringify(this));
};
// 配列クラスにロードメソッドを追加
Array.prototype.Load = function(loadkey){
	var buf  = localStorage.getItem(loadkey);
	// Json を配列に直して自身に格納
	var list = JSON.parse(buf);
	for(var key in list){
		this[key] = list[key];
	}
};
// 配列クラスにセーブ削除メソッドを追加
Array.prototype.SaveRemove = function(removekey){
	localStorage.removeItem(removekey);
};

で、使い方は

var hoge = [];

hoge[0] = 'よわむし!';
hoge.Save('rydia');

こんな感じ。

保存したデータを呼び出すときは

var piyo = [];
piyo.Load('rydia');

でできるよ。

お盆と神社の深い関係(簡易版)

秩父神社

ちゃんと調べてから書くべきなんですが
旬のネタ故、簡単に書いておきます。

お盆と言ったらお墓参り。
お墓参りと言ったらお寺。

と、あまり神社は関係なさそうな行事ですが、
そうとは言い切れないようです。

お墓参りをして、ご先祖様をお迎えして共に過ごしお見送りする。
これは、日本古来の宗教観である、先祖崇拝、つまりは神道が元になっていると考えられているみたいです。

それが今のお盆の形になったのは江戸時代からのようですね。

神社でも迎え火や送り火を炊いたりして、ご先祖様を祀ったりするそうです。

色々調べていくと、なかなか面白そうです。

参考
神社本庁
Wikipedia
大野湊神社

ら、来年までにはちゃんと調べるよーっ

PHP GD での画像編集方法、基礎編

先日、「PHP GD を使って透過PNGを作る方法」という記事を上げた所、
一緒にブログを書いているもっちゃんから
コード全部説明してよ全部解説したほうが読みやすいんじゃないかな?」
と、ありがたいお言葉を頂いたので
先日のコードを全部説明…しません。

折角なので、PHP GD を使って画像の編集を行う方法を
何回かに分けて紹介していこかと思います。

今回は、読み込み・拡/縮コピー・保存 を紹介します。
まず、本日解説する命令を使った例を紹介します。

// 画像の情報を取得
$imgInfo = getimagesize('画像パス');

// コピーする座標、サイズを決定する
$width  = 0;
$height = 0;
$x      = 0;
$y      = 0;
if($imgInfo[0]<=$imgInfo[1]){
	// 横幅より、縦幅のほうが大きかった場合
	$width  = $imgInfo[0];	// 横幅はそのまま
	$height = $imgInfo[0];	// 縦幅は横幅と同じ値
	$y      = floor( ( $imgInfo[1] - $height ) / 2);		// コピーの開始座標(縦軸)を決める(横軸は0のまま)
}else{
	if($imgInfo[1] < 100){
		// 横幅のが縦幅より大きく、画像の縦幅が 100px 未満だった場合
		$width  = $imgInfo[1];	// 横幅は縦幅と同じ値にする
		$height = $imgInfo[1];	// 縦幅はそのまま
		$x      = floor( ( $imgInfo[0] - $width ) / 2);		// コピーの開始座標(横軸)を決める(縦軸は0のまま)
	}else{
		// 横幅のが縦幅より大きく、画像の縦幅が 100px 以上だった場合
		$height = floor($imgInfo[1]*0.8);					// 縦幅の長さの 80% を縦幅とする
		$width  = $height;									// 横幅を上で決めた値と同じにする
		$x      = floor( ( $imgInfo[0] - $width ) / 2);		// コピーの開始座標(横軸)を決める
		$y      = floor( ( $imgInfo[1] - $height ) / 2);	// コピーの開始座標(縦軸)を決める
	}
}

// ファイルオブジェクトを作成する
$imgObj = null;
switch($imgInfo[2]){
	case IMAGETYPE_JPEG:	// Jpeg の場合
		$imgObj = imagecreatefromjpeg('画像パス');
		break;
	case IMAGETYPE_GIF:	// Gif の場合
		$imgObj = imagecreatefromgif('画像パス');
		break;
	case IMAGETYPE_PNG:	// Png の場合
		$imgObj = imagecreatefrompng('画像パス');
		break;
}

$newImg = imagecreatetruecolor(128,128); // 空の画像を作成

// 画像を切り出し、空画像に縮小(拡大)コピーする
imagecopyresampled($newImg,$imgObj,0,0,$x,$y,128,128,$width,$height);

// 保存
imagepng($newImg,'新しい画像パス');

// メモリ開放
imagedestroy($imgObj);
imagedestroy($newImg);

この例では、元の画像から一部を切り出し、縮小して保存しています。
使用している命令は

  1. 画像情報の取得
  2. 既存画像の読み込み
  3. 新規画像の作成
  4. 拡/縮コピー
  5. 画像の保存
  6. メモリ開放

の6つです。
それでは、一つ一つ解説していきます。

画像情報の取得

$imgInfo = getimagesize('画像パス');

コードで言うとここですね。
getimagesize という命令を使って情報を取得しています。
値は配列で、下記のような内容が返ってきます。

  • 0:幅
  • 1:高さ
  • 2:イメージタイプ
  • 3:<img>タグで使える幅と高さを指定した文字列
  • bits:ビット数
  • channels:チャンネル数
  • mime:MIMEタイプ

試しに下の画像の情報を取得してみましょう。
サンプル
array(7) {
[0]=>int(700)
[1]=>int(525)
[2]=>int(2)
[3]=>string(24) “width=”700″ height=”525″”
[“bits”]=>int(8)
[“channels”]=>int(3)
[“mime”]=>string(10) “image/jpeg”
}
こんな情報が取れました。
ここで重要な要素は0~2の要素です。
幅と高さはコピーする際に必要で、
イメージタイプは、画像の読み込みに必要になります。

既存画像の読み込み

switch($imgInfo[2]){
	case IMAGETYPE_JPEG:	// Jpeg の場合
		$imgObj = imagecreatefromjpeg('画像パス');
		break;
	case IMAGETYPE_GIF:	// Gif の場合
		$imgObj = imagecreatefromgif('画像パス');
		break;
	case IMAGETYPE_PNG:	// Png の場合
		$imgObj = imagecreatefrompng('画像パス');
		break;
}

ここで画像を読み込んでいます。
Switchまで使って、面倒なことをやっていますが、
これは画像の種類によって読み込みに使う命令が異なるため、
事前に調べたイメージタイプによってどの命令を使うか決めています。
今回のサンプルの場合はイメージタイプ 2 = Jpeg なので
imagecreatefromjpeg を使って読み込んでいます。

新規画像の作成

$newImg = imagecreatetruecolor(128,128); // 空の画像を作成

ここで新規に画像を作成しています。
今回は、元画像から特定の箇所を切り出し、
128px × 128px の画像を作ろうと思いますので、
ここで imagecreatetruecolor という命令を使い、
128(幅)と128(高さ)を指定して、画像を作成します。

拡/縮コピー

imagecopyresampled($newImg,$imgObj,0,0,$x,$y,128,128,$width,$height);

ここで、先ほど作った128px × 128px の画像に既存画像から
画像を縮小コピーしています。
imagecopyresampled という命令を使用しています。
引数の意味は下記みたいな感じです。

  • 第1引数 コピー先の画像
  • 第2引数 コピー元の画像
  • 第3引数 コピー先の貼り付け開始位置(横軸)
  • 第4引数 コピー先の貼り付け開始位置(縦軸)
  • 第5引数 コピー元の貼り付け開始位置(横軸)
  • 第6引数 コピー元の貼り付け開始位置(縦軸)
  • 第7引数 コピー先の貼り付ける時の幅
  • 第8引数 コピー先の貼り付ける時の高さ
  • 第9引数 コピー元のコピーする範囲(幅)
  • 第10引数 コピー先のコピーする範囲(高さ)

ここで第7・8引数のほうが、第9・10引数より大きければ拡大、
小さければ縮小処理が入り、画像が拡/縮します。

今回の例では下記コードで画像のコピー位置、大きさを決めています。

// コピーする座標、サイズを決定する
$width  = 0;
$height = 0;
$x      = 0;
$y      = 0;
if($imgInfo[0]<=$imgInfo[1]){
    // 横幅より、縦幅のほうが大きかった場合
    $width  = $imgInfo[0];  // 横幅はそのまま
    $height = $imgInfo[0];  // 縦幅は横幅と同じ値
    $y      = floor( ( $imgInfo[1] - $height ) / 2);        // コピーの開始座標(縦軸)を決める(横軸は0のまま)
}else{
    if($imgInfo[1] < 100){
        // 横幅のが縦幅より大きく、画像の縦幅が 100px 未満だった場合
        $width  = $imgInfo[1];  // 横幅は縦幅と同じ値にする
        $height = $imgInfo[1];  // 縦幅はそのまま
        $x      = floor( ( $imgInfo[0] - $width ) / 2);     // コピーの開始座標(横軸)を決める(縦軸は0のまま)
    }else{
        // 横幅のが縦幅より大きく、画像の縦幅が 100px 以上だった場合
        $height = floor($imgInfo[1]*0.8);                   // 縦幅の長さの 80% を縦幅とする
        $width  = $height;                                  // 横幅を上で決めた値と同じにする
        $x      = floor( ( $imgInfo[0] - $width ) / 2);     // コピーの開始座標(横軸)を決める
        $y      = floor( ( $imgInfo[1] - $height ) / 2);    // コピーの開始座標(縦軸)を決める
    }
}

この式に当てはめると

サンプル

この画像は横幅のが大きく、且つ縦幅が100px 以上あるので

        // 横幅のが縦幅より大きく、画像の縦幅が 100px 以上だった場合
        $height = floor($imgInfo[1]*0.8);                   // 縦幅の長さの 80% を縦幅とする
        $width  = $height;                                  // 横幅を上で決めた値と同じにする
        $x      = floor( ( $imgInfo[0] - $width ) / 2);     // コピーの開始座標(横軸)を決める
        $y      = floor( ( $imgInfo[1] - $height ) / 2);    // コピーの開始座標(縦軸)を決める

ここの処理が実行され、位置と幅が

sample2

こんな感じで決まります。
これをさっきの命令で縮小コピーすると

縮小後

はれてこんな画像が出来上がります。

画像の保存

画像は出来上がりましたが、これはまだメモリ内にしか無く、
保存されていません。このままプログラムが終わってしまうと
せっかく作った画像が無くなってしまうので、保存をします。

imagepng($newImg,'新しい画像パス');

これで保存ができます。
ここでは png 形式で保存していますが、
imagejpeg を使えば jpeg 形式で、imagegif を使えばgif 形式で保存できます。

メモリ開放

無事保存まで行いましたが、画像はまだメモリ内に残っています。
セッションが終了すれば、自動的に開放されますが、
慣例として、開放してやります。

imagedestroy($imgObj);
imagedestroy($newImg);

忘れても特に大きな問題にはならないと思いますが…
まぁ、本当に慣例です。


 

簡単に書けるプログラムでも、実際に説明すると結構大変ですね…
次回はブレンディング周りについてちょっと書いていこうかなぁ

予定は未定にして以下ry

画像編集:オーバーレイについて調べてみた

前の記事で、画像の合成にオーバーレイという方法を使ったのですが、
どうにもうまくイメージできなかったので調べてみた。

簡単にPHPで書いてみると

if ($a < 128){
    $value = $a * $b * 2 / 255;
}else{
    $value = 2 * ($a + $b - $a * $b / 255) - 255;
}
if ($value > 255){ $value = 255;}

という式みたいです。

うん。わけわからんですね。

僕もわけがわかりませんでした。
なので、具体的な色を使って考察してみたいと思います。

臙脂(#b94047)に対して、

黒(#000000)と、

灰色(#7f7f7f)と、

白(#ffffff)
を混ぜてみたいと思います。

混ぜる際、臙脂を $a、混ぜる色を $b とします。
※臙脂色を使った理由ですが、赤(#ff0000)など、極端(ff や 00)な色は変化がわかりづらいため

まず黒をオーバーレイで混ぜてみます。
すると

こんな色(#730000)になりました。
混ぜ合わせる色が 黒 の場合、元の色が 128 未満だと黒に
128以上だった場合は若干暗くなるようです。

次に、白をオーバーレイで混ぜてみます。
すると

こんな色(#ff808e)になりました。
混ぜ合わせる色が 白 の場合、元の色が 128以上だった場合は白に
128未満だった場合は元の色の倍の値になるようです。

最後に、灰色をオーバーレイで混ぜてみます。
すると

変わりません(#b94047)。
これは、計算式に当てはめて計算すると、
ピタリ同じ数値にはならないのですが、四捨五入すると同じ数値になります。

これを利用することで、透過値だけを元画像に反映することができるんですね。

あー、スッキリした。

PHP GD を使って透過PNGを作る方法

GD を使ってそのまま透過PNGを作ろうとすると、
1ピクセルごとに透過(アルファ)値を操作する必要があり非常に処理が重くなります。
そこで、事前にテンプレートとなる透過PNGを用意し、それを重ねることで実現します。

  1. 画像準備
  2. PHPコード
  3. コード解説

画像準備

画像を準備します。

透過準備画像

少し分かりにくいですが、円の周りは透過しています。
後述しますが、描画色は 7F7F7F (R127,G127,B127)で準備します。

PHPコード

後は、下記コードで動作します。

$imgPath = '画像パス';
$imgInfo = getimagesize($imgPath);

// ファイルオブジェクトを作成する
// - ファイルタイプ別に呼び出すメソッドを変える
$imgObj = null;
switch($imgInfo['mime']){
	case 'image/jpeg':
		$imgObj = imagecreatefromjpeg($imgPath);
		break;
	case 'image/gif':
		$imgObj = imagecreatefromgif($imgPath);
		break;
	case 'image/png':
		$imgObj = imagecreatefrompng($imgPath);
		break;
}
imagelayereffect($imgObj, IMG_EFFECT_OVERLAY);    // アルファブレンディングの設定をする
imagesavealpha($imgObj, true);                    // アルファ情報を保存するようフラグを立てる

$imgAlpha = imagecreatefrompng('透過画像パス');

imagecopy($imgObj,$imgAlpha,0,0,0,0,$imgW,$imgH); // 混ぜる

imagepng($imgObj,'画像保存パス');

// メモリ開放
imagedestroy($imgObj);
imagedestroy($imgAlpha);

これを実行すると

透過前金魚

透過後金魚

こうなります。

コード解説

まず、画像を読み込みます。
その際、画像の形式によって呼び出すメソッドが異なるので注意です。

switch($imgInfo['mime']){
	case 'image/jpeg':
		$imgObj = imagecreatefromjpeg($imgPath);
		break;
	case 'image/gif':
		$imgObj = imagecreatefromgif($imgPath);
		break;
	case 'image/png':
		$imgObj = imagecreatefrompng($imgPath);
		break;
}

この後に指定している2つが要注意です。
一つ一つ解説していきます。

imagelayereffect($imgObj, IMG_EFFECT_OVERLAY);    // アルファブレンディングの設定をする

これは、画像をどのように合成(重ねあわせ)するかの設定です。
今回は IMG_EFFECT_OVERLAY を使っています。
PHPの公式サイトには

オーバーレイを使用すると、背景の黒い部分は黒のまま。
一方背景の白い部分は白のままとなります。 背景のグレーな部分は、前景のピクセルの色となります。

とあります。
細かいことは置いておいて、今回は

背景のグレーな部分は、前景のピクセルの色となります。

を利用します。ここで言っているグレーと言うのは 7f7f7f の事を指しています。
そこで、全てを 7f7f7f で塗りつぶし、透過した部分を作った画像を用意したわけです。

次に

imagesavealpha($imgObj, true);                    // アルファ情報を保存するようフラグを立てる

これで画像を保存した際に、透過情報が抜け落ちないようにしてます。

ここで注意点なのですが

imagealphablending($imgObj, false);

この指定は絶対にしてはいけません。
正直、理由はよくわかってません(すいませんっ!)が、
どうも imagelayereffect の設定が変更されてしまうみたいです。

ちゃんと読んだら、公式に
imagelayereffect に IMG_EFFECT_ALPHABLEND を設定するのと同じ
と書いてありました orz

あとは、画像を重ねあわせて保存しています。


「簡単にできるだろ」
とか思っていたら、存外うまくいかない上に
資料もあまりなく、結構ハマってしまいました。
(そもそも、サーバサイドで画像加工する機会はほとんどないですからね…)
備忘録がてら記事にしてみました。

…しかし、我ながら硬い文章だ

親の親(継承元の継承元)のメソッドを呼び出す方法

$grandParent = get_parent_class(get_parent_class($this));
$grandParent::parent_parent_method();

こんなコードでできる。
ただ、そもそも親の親を孫が直接呼び出すとか
オブジェクト思考の考え方からすれば変だと思う。

例えば

<?php
// おじいちゃん
class GrandParentClass{
	function TestMethod(){
		echo 'hoge';
	}
}

// おとん
class ParentClass extends GrandParentClass{
	function TestMethod(){
		parent::TestMethod();	// GrandParentClass::TestMethod();
		
		// 以下、ChildClass から呼び出されたくない処理
		echo 'fool !';
	}
}

// おれ
class ChildClass extends ParentClass{
	function TestMethod(){
		parent::TestMethod();	// ParentClass::TestMethod();
	}
}

こんな感じで継承していたとして

ChildClass::TestMethod

から

echo 'fool !';

を呼び出したくなかったら

<?php
// おじいちゃん
class GrandParentClass{
	function TestMethod(){
		echo 'hoge';
	}
}

// おとん
class ParentClass extends GrandParentClass{
	function TestMethod(){
		parent::TestMethod();	// GrandParentClass::TestMethod();
		
		// 以下、ChildClass から呼び出されたくない処理
		// 別のメソッドに切り出す
		$this->PrintFool();
	}
	
	// 呼び出されたくない処理を切り出したメソッド
	function PrintFool(){
		echo 'fool !';
	}
}

// おれ
class ChildClass extends ParentClass{
	function TestMethod(){
		parent::TestMethod();	// ParentClass::TestMethod();
	}
	
	function PrintFool(){
		// 処理を上書きして、何もしないようにする
	}
}

こんなかんじにすればいいと思う。

…とはいえ、会社や学校のコード規約とか
なんか暗黙の了解とか色々あって親クラスのメソッドを
切り分けるとかできないケースがあります。
僕も、会社でそんなケースにあたったので
最初に書いたような親の親のメソッドを
直接呼び出すコードを使って回避したのでした。

金魚逝きました。

金魚始めました。でアイキャッチにした金魚が逝きました。
遺影になってしまいました・・・。

実はもう一匹飼っているのですがその子も病気で現在闘病中です。
白点病という金魚飼育初心者が金魚を飼う際に
結構な確率で患わせてしまう病気らしいのですが
薬を買ってきたのできっとこれでもう大丈夫。多分。

最近、仕事でメルマガやイベント用のHTMLメールのデザインやら
動画の編集やらをすることが多くて、WEBサイトのデザイン全然できてないなぁ。
ただ、ロゴデザインをたくさんやらせてもらえるのは嬉しいです。

このブログのデザインもまだ手つけられていないので
8月は作業環境整えて作業に専念します。
クーラー欲しいよう。