9.9 画像読み込み機能拡張プラグイン(その2)

今回は ImageHandlerPlugin クラスの processImage メソッドで使ってる画像処理のメソッドを作っていくことにするね。
transformImage メソッドと scaleImage メソッドと rotateImage メソッドと boxBlur メソッドの4つだったよね。
そうそう。
それじゃまずは boxBlur メソッドから作ってこっか。
あ、transformImage メソッドからじゃないんだ?
簡単なメソッドから作っていこうかなーと思ってね。
その方が説明もしやすいし。
そーなんだ。
ってワケで、これが boxBlur メソッドだよ。

boxBlur メソッド>

function boxBlur(layer, elm)
{
    // doBoxBlur メソッドを呼び出して画像をぼかします
    layer.doBoxBlur(+elm.xblur, +elm.yblur);
}

ホントにカンタンだね…
ってゆーか doBoxBlur メソッドを呼び出してるだけ?
doBoxBlur メソッドについては §9.6 参照。
ん、別に他にすることないから。
引数は大丈夫だよね?
えっと、第1引数と第2引数がそれぞれ eximage マクロに指定されてる xblur 属性と yblur 属性を数値にした値になるんだよね?
そ。これは特に問題ないよね。
うん。
じゃ次は scaleImage メソッドね。
まずこれがスクリプト。

scaleImage メソッド>

function scaleImage(layer, elm)
{
    var tmpLayer = new Layer(kag, layer); // 一時レイヤを作ります
    tmpLayer.assignImages(layer); // 一時レイヤに操作対象のレイヤの画像をコピーします

    // 横方向・縦方向拡大率を計算します
    var xscale = elm.xscale !== void ? +elm.xscale : (elm.scale !== void ? +elm.scale : 100);
    var yscale = elm.yscale !== void ? +elm.yscale : (elm.scale !== void ? +elm.scale : 100);
    var newWidth = int (layer.imageWidth * xscale / 100); // 拡大/縮小後の幅を計算します
    var newHeight = int (layer.imageHeight * yscale / 100); // 拡大/縮小後の高さを計算します
    newWidth = 1 if newWidth < 1; // 幅は必ず 1 ピクセル以上になるようにします
    newHeight = 1 if newHeight < 1; // 高さも必ず 1 ピクセル以上になるようにします

    layer.setImageSize(newWidth, newHeight); // 操作対象のレイヤのサイズを変更します
    layer.setSizeToImageSize(); // 操作対象のレイヤのサイズを画像のサイズに合わせます

    // 一時レイヤの画像を拡大/縮小して操作対象のレイヤにコピーします
    layer.stretchCopy(0, 0, newWidth, newHeight, tmpLayer, 0, 0, tmpLayer.imageWidth, tmpLayer.imageHeight, stLinear);

    invalidate tmpLayer; // 一時レイヤはもう必要ないので無効化します

    // pos 属性が指定されている場合は操作対象のレイヤの位置を調整します
    setPosition(layer, elm.pos) if elm.pos !== void;
}

今度はだいぶ長くなったね…
まぁ一時レイヤとか作ったりしてるしね。
ただ、基本的にやってることは §9.2 で画像を拡大した時とほとんど同じだよ。
そーなの?
最初の一時レイヤを作ってるとこ前にやったのとほぼ同じでしょ?
前は layer0 だったのが layer になってるけど、確かにほとんどおんなじだね。
前は 0 番の前景レイヤの画像を拡大するって始めから決めてたけど、 今回はどの前景レイヤの画像でも拡大できるように、 レイヤへの参照(layer のことね)を引数にしてるの。
あ、なるほど。
えっと、次の xscaleyscale っていう変数は前はなかったよね。
しかもなんか代入の式が複雑だし…
この式は三項演算子を2回使ってるんだけど、どうなってるかわかる?
※三項演算子については §1.20 参照。
ん〜っと…どーなってるんだろ?
じゃあ『var xscale = elm.xscale !== void ? A : B;』っていう式だとどうなる?
その式だったら、elm.xscalevoid じゃなかったら xscaleA っていう値が代入されて、 elm.xscalevoid だったら xscale には B っていう値が代入されるんだよね?
ん、そうだね。
んじゃ『var xscale = elm.scale !== void ? B1 : B2;』っていう式は?
elm.scalevoid じゃなかったら xscaleB1 っていう値が代入されて、 elm.scalevoid だったら xscaleB2 っていう値が代入されるよね。
だね。
で、1つ目の式の B の所に2つ目の式を入れてみると、 elm.xscalevoid じゃない時は xscaleA が代入されて、 elm.xscalevoid だったら、今度は elm.scale の値をチェックして…
elm.scalevoid じゃなかったら xscaleB1 を代入して、 elm.scalevoid だったら B2 を代入するってコト?
そういうこと。つまりこの式はこう処理されるわけね。

xscale への代入式の計算の流れ>

う〜ん、なんかややこしーね…
ねぇ、これって…

var xscale = elm.xscale !== void ? +elm.xscale : (elm.scale !== void ? +elm.scale : 100); と等価なスクリプト>

var xscale;
if(elm.xscale !== void)
    xscale = +elm.xscale;
else if(elm.scale !== void)
    xscale = +elm.scale;
else
    xscale = 100;

これじゃダメなの?
もちろんそれでも OK だよ。
こっちのがわかりやすいと思うんだけどなぁ…
scaleImage メソッドはスクリプトが結構長いから、 三項演算子を2回使って代入文を書いてみたんだ。こうすると1行で書けるからね。
ただ、どっちでも結果は同じだから、どんなふうに書くかは自由だよ。
確かにこっちだとわかりやすいけどスクリプトは7行になっちゃってるね。
まぁこんなふうに同じ処理をするスクリプトでも色んな書き方ができるわけだけど、 他の人が見たり自分が後から見た時でも何が書いてあるかすぐにわかるようなスクリプトを書くようにするといいんじゃないかな。
そだね。
じゃ scaleImage メソッドの話に戻って…
xscaleyscale はそれぞれ横方向と縦方向の拡大率(何%に拡大/縮小するかってことね)を表す変数なんだ。
えっと、xscale の方は eximage マクロに xscale 属性が指定されてたら xscale 属性の値を数値にして代入してて、 xscale 属性は指定されてないけど scale 属性が指定されてたら scale 属性の値を数値にして代入してて、 どっちの属性も指定されてなかったら 100 を代入してるんだよね?
ん、ちょっとややこしいけどそういうことだね。
yscale の方は xscaleyscale に置き換わってるだけで代入の仕方は同じね。
scale 属性って確か縦方向と横方向の拡大率が同じになる時に指定するんだったよね?
そ。xscale 属性と yscale 属性を指定せずに scale 属性だけ指定すると xscaleyscale 両方の変数に +elm.scale が代入されるようになってるでしょ。
確かにそれだと縦方向と横方向でおんなじ拡大率になるね。
あと、どの属性も指定されてなかった時に代入する 100 ってのは画像のサイズを 100% にするってことだから、つまり拡大も縮小もしないってことね。
次は newWidthnewHeight に拡大/縮小後の画像の幅と高さを代入してるんだけど、 この計算は前にもやったから OK だよね?
layer.imageWidthxscale(横方向の拡大率だよね)を掛けてるのはわかるんだけど、 何でその後 100 で割ってるの?
拡大率は%(パーセント)単位で指定するから、 画像の幅は (xscale÷100) 倍になるの。
あー、なるほどね。
だから高さも layer.imageHeightyscale / 100 を掛けてるんだね。
そういうこと。
ここまではわかったけど、その次にあるこの2つの式って何やってるの?
画像のサイズが小さくなり過ぎないようにしてるの。
それってどーゆーこと?
例えば、幅が 40 ピクセルの画像を拡大率 1% で縮小したら、縮小後の幅は何ピクセルになる?
1%?…って 0.01 倍ってことだよね…
えっと、40×0.01 だから 0.4 ピクセル?
画像の幅は整数だから、int 演算子を使って小数点以下を切り捨てると…
0 ピクセル?
…にはできないから、 newWidth1 より小さくなってたら newWidth1 を代入することで、 画像の幅が最低でも 1 ピクセルになるようにしてるってワケ。
あと高さも同じようにして 1 ピクセル以上になるようにしてるの。
※画像の幅(第1引数)や高さ(第2引数)を 0 以下にして setImageSize メソッドを呼び出すと例外が発生します。
なるほどねぇ…でもそんなに小さくしたりすることってあるのかなぁ…?
ま、普通はしないだろうけど、念のためってことで。
でその後は一時レイヤのサイズを拡大後の画像のサイズに合わせてstretchCopy メソッドで画像を拡大コピーしてるんだけど、 これは前にやったのとおんなじやり方だよ。
その後一時レイヤを無効化するのも前とおんなじみたいだね。
ん、そうだね。
最後に setPosition っていうメソッドを呼び出してるけど、 これって前はなかったよね?
setPosition メソッドはね、 pos 属性が指定されてる時にレイヤの位置を調整するために作ってるんだ。
pos 属性って、image タグの pos 属性のこと?
そうだよ。
pos 属性って eximage マクロの中で image タグを実行する時にチェックしてるんじゃないの?
確かに image タグを実行する時に pos 属性が指定されてたら、レイヤの位置を自動的に調整してくれるんだけど…
でしょ?
でも、image タグを実行する時にはまだ画像が拡大/縮小されてないから、 pos 属性の値をチェックしてレイヤの位置を調整した後で画像が拡大/縮小されるわけね。
うん…でもそれって何か問題あるの?
レイヤの位置を調整した後で画像のサイズを変えると、例えばこんな感じになっちゃうの。

pos 属性を "c" にして画像の読み込みと拡大を行った場合>

この図は pos 属性を "c" にして画像を読み込んで、その後画像を2倍に拡大した時の例なんだ。
左側が画像を拡大する前、つまり image タグを実行した直後の状態で、 右側が画像を拡大した後、つまり stretchCopy メソッドを呼び出した直後の状態だよ。
なんか拡大した後の画像の下の方が切れちゃってるね。
stretchCopy メソッドで画像を拡大しても、 画像の左上の点の位置は変わらないからこうなっちゃうの。
ホントはこうなって欲しいよね。

pos 属性を "c" にした場合の拡大後の画像の正しい位置>

そーだね。これだとちゃんと pos 属性を "c" にした時の位置になってるね。
こんなふうに画像を拡大した後にレイヤを正しい位置に表示するために、 pos 属性が指定されてたらここで setPosition メソッドを呼び出してレイヤの位置を設定し直してるってワケ。
なるほどねぇ…単に画像を拡大すればおっけーってわけじゃないんだね。
left 属性とか top 属性でレイヤの位置を設定してるんなら画像を拡大するだけでいいんだけど、 pos 属性を使ってるとそうはいかないんだよね。
じゃ setPosition メソッドのスクリプトを見ていくね。

setPosition メソッド>

function setPosition(layer, pos)
{
    var center = kag.scPositionX[pos]; // pos 属性の値に相当する画像の水平方向中心位置を取得します
    if(center !== void)
    {
        // pos 属性の値に従って位置を設定します
        layer.setOptions(%["left" => center - layer.width \ 2, "top" => kag.scHeight - layer.height]);
    }
}

setPosition メソッドには引数が2つあって、 第1引数の layer を第2引数の pos で指定した位置に表示するんだ。
えっと、最初の行で center っていう変数に代入してる kag.scPositionX[pos] ってなんなの?
scPositionX は Config.tjs の中に出てくるんだけど、見覚えない?
え、そーなの?
う〜ん…なんだったっけ?
pos 属性でレイヤの中心位置をどこに合わせるかを設定する部分だよ。
初期状態の Config.tjs だとこうなってるよね。

scPositionX の設定(Config.tjs より抜粋)>

//-------------------------------------------- ウィンドウや動作の設定 -----

function KAGWindow_config()
{
    (中略)

// ◆ 前景レイヤの左右中心位置指定
// +---------+
// |         |
// | |  |  | |
// | |  |  | |
// +---------+
// image タグあるいは img タグの pos 属性で指定する前景レイヤの 位置名 (
// left や lecft_center など ) とそれぞれに対応する 中心位置 ( x 座標 ) を
// 指定します。;scPositionX.位置名 = 中心位置; の形式で指定します。
;scPositionX.left = 160;
;scPositionX.left_center = 240;
;scPositionX.center = 320;
;scPositionX.right_center = 400;
;scPositionX.right = 480;


// 簡易な記号
// ( pos 属性に指定する left, left_center, center, right_center, right を
//  それぞれ l, lc, c, rc, r でも指定できるように別名を定義します )
;scPositionX.l = scPositionX.left;
;scPositionX.lc = scPositionX.left_center;
;scPositionX.c = scPositionX.center;
;scPositionX.rc = scPositionX.right_center;
;scPositionX.r = scPositionX.right;

    (以下略)
}

あー、これなら見たことあるよ〜。
でしょ。
例えば、pos 属性に "c" を指定した時は scPositionX.c の値になるから…
scPositionX.c = scPositionX.center; って書いてあるから、 scPositionX.cscPositionX.center とおんなじ値なんだね。
scPositionX.center = 320; だから、結局 scPositionX.c320 ってコトかな?
ん、そう。
ウィンドウのサイズを 640×480 ピクセルにしてるっていう想定で設定してあるから、 pos"c" にした時にレイヤの真ん中が 320 の位置、 つまりウィンドウのちょうど真ん中になるわけね。
なるほどね。
んじゃ setPosition メソッドの話に戻るね。
1行目で centerkag.scPosition[pos] の値を代入してるわけだけど、こうすると例えば pos"c" だった時に、Config.tjs で設定してる scPositionX.c の値が center に代入されるの。
えっと、引数の pos"c" だったら center = kag.scPositionX["c"]; になるよね。
あ、そーいえば scPositionX って辞書配列なの?
そうだよ。だから kag.scPositionX["c"]kag.scPositionX.c はおんなじ意味になるよね。
※辞書配列については §1.15 参照。
だね。
あ、でも Config.tjs の方は kag.scPositionX じゃなくて scPositionX になってるんだけど?
この KAGWindow_config っていうメソッドkag オブジェクトのコンテキスト上で実行されるから、単に scPositionX って書いてあっても、実際は kag.scPositionX っていう意味になるんだ。
KAGWindow_config メソッドは KAGWindow クラス(kag オブジェクトのクラス)のコンストラクタの中で (KAGWindow_config incontextof this)(); という形で実行されています。
えっと、それってどーゆーコト?
§2.11 でコンテキストについて一応説明してるんだけど、もう忘れちゃったかな?
うーん、そー言われればコンテキストって前に出てきた気がするけど、 第2章って難しい話が多かったからねぇ…
簡単に言うと、kag オブジェクトのコンテキスト上で KAGWindow_config メソッドを実行するってことは、 このメソッドを kag オブジェクトのメソッドとして実行するって感じだね。
kag オブジェクトのメソッドの中だと kag. を付けなくても scPositionX って書くだけで kag オブジェクトの scPositionX って意味になるでしょ。
なるほど、確かにそーなるね。
じゃあ、例えば scPositionX.left = 160;kag.scPositionX.left = 160; とおんなじ意味ってコトでいーんだよね?
まぁそういうコトだね。
じゃ setPosition メソッドのスクリプトに戻って…
あとは centervoid じゃなかったら setOptions メソッドを呼び出してレイヤの位置を設定すれば OK。
center って void になることあるの?
pos 属性が正しく指定されてれば void にはならないんだけど、一応チェックしてるの。
ふぅん、そうなんだ。
setOptions メソッドは前に使ったことあるけど覚えてる?
え〜っとね…確か layopt タグを実行した時に呼び出されるって言ってなかったっけ?
そうそう。だから引数の辞書配列には layopt タグと同じ属性が指定できるわけね。
setOptions メソッドについては §6.2 参照。
引数の辞書配列には lefttop が指定されてるから、 left 属性と top 属性を指定して layopt タグを実行するのとおんなじなわけだね。
そ。じゃ lefttop の計算の仕方はわかる?
top の方にある scHeight ってのがよくわかんないんだけど…?
これも Config.tjs に出てくる値だよ。
え、そーなの?
これなんだけど、見たことない?

scHeight の設定(Config.tjs より抜粋)>

//-------------------------------------------- ウィンドウや動作の設定 -----

function KAGWindow_config()
{
// ◆ 画面サイズ
// scWidth に画面の幅、scHeight に画面の高さをピクセル単位で指定します。
// 標準的に使われている 640x480 や 800x600 のような画面サイズではないサイズ
// に設定すると、フルスクリーンにできない場合があります。
;scWidth = 640;
;scHeight = 480;

    (以下略)
}

あ〜、画面の高さを指定する値だね。
だからこの kag.scHeight は画面の高さになってるってワケ。
kag が付いてるのは scPositionX の時とおんなじ理由なの?
そだよ。
どっちも Config.tjs の KAGWindow_config メソッドの中にあるからね。
そーなんだ。じゃあ…

lefttop の計算法>

こんな感じで lefttop の値を計算してるのかな?
ん、そうそう。
さて、それじゃこれで setPosition メソッドと scaleImage メソッドがチェックできたから、今回はこの辺にしとこっか。
そーだね。まだメソッドが残ってるけど、結構長くなっちゃったもんね。
残りの transformImage メソッドと rotateImage メソッドは次回見ていくことにするね。
りょ〜かい!
それじゃ、また次回ね!


前へ | TOP | 次へ