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

今回から、前回までに見てきた Layer クラスの画像処理系のメソッドを使って画像読み込み機能拡張プラグインを作っていくことにするね。
§9.1eximage マクロも作るんだよね。
そ。そのマクロのためのプラグインを作るわけね。
りょ〜かい。
それじゃ早速プラグインを作っていくね。
KAGPlugin クラスを継承して作ってこうと思うんだけど、これは OK だよね?
KAGPlugin クラスについては §4.1 参照。
うん。KAGPlugin クラスは今までに何回か使ってるよね。
だね。
あと、画像を扱うクラスだから、ImageHandlerPlugin クラスって呼ぶことにするね。
で、まずこれがコンストラクタとデストラクタ。

ImageHandlerPlugin クラスのコンストラクタとデストラクタ>

class ImageHandlerPlugin extends KAGPlugin
{
    function ImageHandlerPlugin()
    {
        // スーパークラスのコンストラクタを呼び出します
        super.KAGPlugin();
    }

    function finalize()
    {
        // スーパークラスのデストラクタを呼び出します
        super.finalize();
    }
}

スーパークラスのコンストラクタとデストラクタを呼び出してるだけ…みたいだね。
ん、まぁとりあえずコンストラクタとデストラクタでやることは特にないからね。
そーなんだ。
じゃ次は eximage マクロから呼び出されるメソッドを見ていくね。

eximage マクロから呼び出される processImage メソッド>

function processImage(elm)
{
    // 操作対象のレイヤへの参照を layer に代入します
    var layer = kag.getLayerFromElm(elm);
    if(!isCharacterLayer(layer))
        return// layer が前景レイヤでない場合は何もしません

    // x1, y1, x2, y2, x3, y3 属性が全て指定されていれば画像を変形します
    if(elm.x1 !== void && elm.y1 !== void && elm.x2 !== void && elm.y2 !== void && elm.x3 !== void && elm.y3 !== void)
        transformImage(layer, elm);

    // angle 属性が指定されていれば画像を回転します
    if(elm.angle !== void)
        rotateImage(layer, elm);
    // scale, xscale, yscale のいずれかの属性が指定されていれば画像を拡大/縮小します
    else if(elm.scale !== void || elm.xscale !== void || elm.yscale !== void)
        scaleImage(layer, elm);

    // xblur 属性と yblur 属性が指定されていれば画像をぼかします
    if(elm.xblur !== void && elm.yblur !== void)
        boxBlur(layer, elm);
}

うわ、なんか急にややこしくなったね…
確かに見た目は複雑そうに見えるけど、実はそんな難しいことはやってないんだ。
そーなの?
ん。
まぁその辺はまた後で見ていくとして、まずこの processImage メソッドがどんなメソッドかをちょっと説明しとくね。
は〜い。
processImage メソッドは eximage マクロの中でこんなふうに呼び出されるんだ。

eximage マクロ>

[macro name=eximage]
;まず image タグで普通に画像を読み込みます
[image *]
;その後 processImage メソッドを呼び出して(必要なら)画像を操作します
[eval exp="image_handler_plugin.processImage(mp)"]
[endmacro]

eximage マクロって意外とシンプルなんだね。
拡大とか回転とか色々できるからもっと複雑なのかと思ってたんだけど。
拡大とか回転とかは全部 processImage メソッドで処理するからね。
マクロの中では image タグを実行した後に processImage メソッドを呼び出すだけで OK なんだ。
あ、image タグも使うんだ?
うん。まず image タグで普通に画像を読み込んどいて、 もし拡大とか回転とかする必要があれば、その後 processImage メソッドで処理するの。
逆に言うと、その必要がなかったら processImage メソッドを呼び出しても何もしないから、 結局普通に image タグを実行するのとおんなじになるわけね。
なるほどねぇ…
ところで、[image *] ってどういう意味かはわかるよね?
え? えーっと、確かマクロに指定されてる属性をそのまま image タグの属性に指定するって意味だったよね?
そうそう。
だから eximage マクロには image タグに指定できる属性が全部指定できるわけだね。
そっか。確かにそうなるね。
あ、でも eximage マクロ専用の属性もあるんでしょ?
うん、もちろんあるよ。
そーゆー属性も全部 image タグの属性に指定されちゃうことになるけど、それって大丈夫なの?
それなら大丈夫だよ。
image タグは元々定義されてる属性(storage 属性とか layer 属性とかのことね)以外にどんな属性が指定されてるかってことは全然チェックしてないから、 eximage マクロ専用の属性を image タグに指定しても問題ないんだ。
へぇ、そーなんだ。
ちなみに image タグ以外のタグもみんなそうだから、 マクロ専用の属性を指定しても大丈夫だよ。
それじゃ processImage メソッドのスクリプトを見てくね。
あ、ちょっと待って。
この image_handler_plugin ってなんなの?
あー、確かにそれ説明してなかったね。
…でも、今まで何回かプラグインを作ったから、何となくわからない?
えっ? え〜っと…
image_handler_plugin.processImage(mp) って書いてあるから…
ImageHandlerPlugin クラスのオブジェクト、かな?
ん、そのとーり。
基本的にクラスはオブジェクトを作らないと使えないからね。
クラスを定義した後に…
System クラスなどの一部のネイティブクラスやメンバ変数を持たないクラスなど、 オブジェクトを作らなくても使えるクラスも存在します。

ImageHandlerPlugin クラスのオブジェクトの作成>

kag.addPlugin(global.image_handler_plugin = new ImageHandlerPlugin());
addPlugin メソッドについては §4.3global キーワードについては §4.5 参照。

…って感じで ImageHandlerPlugin クラスのオブジェクトを作ってるの。
あ〜、確かに前にもこんな感じでプラグインのオブジェクト作ったね。
でしょ。じゃこれで eximage マクロは OK かな?
うん、おっけー。
じゃ改めて processImage メソッドのスクリプトを見てくね。
はーい。
このメソッドで必要に応じて画像を拡大したりとかするわけだけど、 そのためにはどのレイヤの画像を処理すればいいかが判らないといけないから、 まず始めに layer っていう変数にそのレイヤへの参照を代入してるんだ。
kag.getLayerFromElm っていうメソッドの戻り値を layer に代入してるみたいだけど…これって初めて見るメソッドだよね?
getLayerFromElm メソッドは kag オブジェクト(つまり KAGWindow クラスだね)のメソッドで、 引数に指定されてる辞書配列の page 要素と layer 要素をチェックして、該当するレイヤへの参照を返すメソッドなんだ。
kag オブジェクトについては §4.2 参照。
えっと…ちょっとよくわかんないかな…
んー、そうだね…
eximage マクロの中で image_handler_plugin.processImage(mp) っていうふうに mp を引数にして processImage メソッドを呼び出してるから、引数の elm がマクロに指定されてる属性が記録されてる辞書配列になるってトコまでは解るよね?
確か mp ってゆーのがマクロに指定されてる属性が記録されてる辞書配列なんだったよね?
mp については §4.8 参照。
そうそう。
で、eximage マクロはレイヤに画像を読み込むマクロなわけだから、 image タグと同じように page 属性と layer 属性で、どのレイヤに画像を読み込むのかを指定しなくちゃいけないよね。
うん、eximage マクロの中で image タグを実行してたもんね。
つまり、processImage メソッドの引数の elm には pagelayer っていう要素が指定されてることになるよね。
elm の要素と mp の要素はおんなじになってるから、 確かにそーなるね。
例えば、elm.page"fore" になってて、 elm.layer1 になってたとするね。
うん。
この場合は、layer = kag.getLayerFromElm(elm); を実行すると、 layer には表画面の 1 番の前景レイヤへの参照が代入されるの。
ちなみに page 属性は省略すると表画面("fore")だと見なされるから、 elm.page っていう要素が指定されてなくても表画面の 1 番の前景レイヤへの参照が代入されるよ。
じゃあ、例えば elm.page"fore"elm.layer"base" だったら、 表画面の背景レイヤへの参照が layer に代入されるってこと?
うん、そういうこと。
あと elm.page"back"elm.layer"message0" だったら、 裏画面の 0 番のメッセージレイヤへの参照が layer に代入されるわけね。
なるほどね。
で、layer にレイヤへの参照が代入できたら、 次はそのレイヤが前景レイヤかどうかを調べるの。
え、なんでそんなことするの?
全部の種類のレイヤに対応するようにしようと思うと、 スクリプトがかなり複雑になっちゃうからね。
それに、背景レイヤとかメッセージレイヤの画像を拡大したり変形したりとかってあんまりやらないでしょ。
だから、eximage マクロが使えるのは前景レイヤだけってことにしてるんだ。
そーかなぁ? 背景を拡大とかできると便利そーだけど?
まぁそうかもしれないけど、前景レイヤだけに対応するのも結構タイヘンってことは後でわかると思うよ。
え、そーなの?
まぁね。
ちなみに背景の画像を拡大したかったら、 前景レイヤに背景用の画像を拡大して読み込んで、重ね合わせ順序を一番奥にしとけば、 背景が拡大されてるように見せることもできるしね。
とりあえず前景レイヤに対応するだけでもそれなりに使えるんじゃないかな。
ふぅん…まぁいっか。
じゃスクリプトの話に戻るね。
layer が前景レイヤかどうかってのは、 isCharacterLayer っていうメソッドで調べてるんだ。
で、これが isCharacterLayer メソッドのスクリプト。

<引数に指定したレイヤが前景レイヤなら true、そうでなければ false を返す isCharacterLayer メソッド>

function isCharacterLayer(layer)
{
    // 表画面の前景レイヤ配列に layer が含まれているかどうかチェックします
    var index = kag.fore.layers.find(layer);
    if(index != -1)
        return true// 表画面の前景レイヤ配列の中に該当するレイヤがあれば true を返します

    // 裏画面の前景レイヤ配列に layer が含まれているかどうかチェックします
    index = kag.back.layers.find(layer);
    if(index != -1)
        return true// 裏画面の前景レイヤ配列の中に該当するレイヤがあれば true を返します

    // 該当するレイヤがなければ前景レイヤではないので false を返します
    return false;
}

このメソッドは、引数(layer)に指定されてるレイヤが前景レイヤなら true、それ以外のレイヤだったら false を返すんだ。
kag.fore.layers.find って何かのメソッドなの?
そうだよ。
find メソッドは Array クラス(配列クラスだね)のメソッドだよ。
前にも出てきたことあるんだけど、覚えてるかな?
え、そーだったっけ?
初めて見る気がするけど…?
まぁだいぶ前の話だしね。
find メソッドは、引数に指定されてる要素が配列の中に含まれてればその要素の番号を返して、 含まれてなければ -1 を返すメソッドだよ。 §1.14 で初めて配列が出てきた時に紹介したよね。
確かにかなり前の話だねぇ…
えっと、じゃあ kag.fore.layers.find(layer) だと、 layerkag.fore.layers っていう配列に含まれてたらその要素の番号が index に代入されて、含まれてなかったら -1index に代入されるってことかな?
ん、そういうこと。
ちなみに kag.fore.layers ってどんな配列だったか覚えてる?
それは覚えてるよ。
kag.fore.layers が表画面の前景レイヤの配列で、 kag.back.layers が裏画面の前景レイヤの配列なんだよね。
kag.fore.layers, kag.back.layers については §4.2 参照。
そうそう。
じゃあここindex != -1 っていう条件はどんな時に真になる?
え〜っとね… index には kag.fore.layers.find(layer) の戻り値が代入されてて、 それが -1 じゃない時に真になるってことは…
layerkag.fore.layers に含まれてたら真になるんじゃないかな。
そ。つまり、layer が表画面の前景レイヤのどれかだったら真になるってことだね。
確かにそうなるね。
その時は layer が前景レイヤってことになるから true を返すんだね。
ん、そう。
こっちの条件もほとんど同じだからわかるよね?
今度は indexkag.back.layers(layer) の戻り値になってるから、 layer が裏画面の前景レイヤのどれかだったら真になるんだよね。
で、その時も true を返して、 どっちの条件も偽になった場合は、layer は前景レイヤじゃないってことだから false を返すってわけね。
だね。
んじゃ processImage メソッドの方に戻るね。
ここから先の ifelse if のとこでマクロに指定されてる属性の値をチェックして、 画像を回転したり変形したり拡大/縮小したりしてるんだ。
なんかすごい条件式が長いんだけど…
えっと、elmx1, y1, x2, y2, x3, y3 が全部 void じゃなかったら真になるってことみたいだから、 マクロに x1, y1, x2, y2, x3, y3 属性が全部指定されてたら、 条件が真になって transformImage っていうメソッドが呼び出されるってことかな?
ん、そういうこと。
この x1, y1, x2, y2, x3, y3 って、 もしかしてアフィン変換する時に変形後の画像の左上と右上と左下の点の座標を指定する属性?
そうだよ。
つまり、x1, y1, x2, y2, x3, y3 はそれぞれアフィン変換行列を使わずに affineCopy メソッドを呼び出す時に、 第7引数〜第12引数の A, B, C, D, E, F に指定する値だね。
affineCopy メソッドについては §9.4 参照。
じゃあ transformImage って画像を変形するメソッドってこと?
そ。transformImage メソッドは後で作るから、とりあえず今は x1, y1, x2, y2, x3, y3 属性をチェックして画像を変形するメソッドって思っといて。
はーい。
次の if のとこの条件もわかるよね?
elm.angle !== void だから、 angle 属性が指定されてたら、 rotateImage っていうメソッドが呼び出されるってことだよね。
そうそう。
ちなみに angle 属性は回転する角度を指定する属性で、 rotateImage メソッドは画像を回転させるメソッドだよ。
rotateImage メソッドって §9.5 で作った rotate メソッドのこと?
基本的にやってることは同じなんだけど、プラグイン用にちょっと変えなくちゃいけないとこがあるから、 rotateImage メソッドもまた後で作ることにするね。
そうなんだ、りょーかい。
その次の else if の条件も大丈夫だよね?
えと、今度は条件式が || でつながってるから、 scale 属性か xscale 属性か yscale 属性のどれか1つでも指定されてたら真になって scaleImage っていうメソッドが呼び出されるってコトだよね?
そう。この3つは画像の拡大率を指定する属性なんだけど、 縦方向と横方向を同じ拡大率にする場合は scale 属性を指定して、 別々の拡大率にする場合は、横方向の拡大率を xscale 属性に指定して、 縦方向の拡大率を yscale に指定するの。
§9.1 参照。
どれか1つでも指定されてたら条件が真になるってことは、例えば xscale 属性だけ指定されてても真になるってことでしょ?
その場合は yscale 属性ってどーなるの?
拡大率のデフォルトは 100%(等倍)ってことにしてるから、 xscale 属性だけ指定されてた場合は yscale 属性は 100% になってるってみなすの。
つまり横方向にだけ拡大/縮小するってことだね。
なるほどね。
あと、scaleImage っていうメソッドが属性の値をチェックして画像を拡大/縮小するメソッドってことだよね?
ん。これも後で作るね。
わかった。
…あ、そーいえばなんでここだけ if じゃなくて else if になってるの?
rotateImage メソッドは画像の回転と拡大/縮小を同時にできるようにするから、 rotateImage メソッドを使う場合は scaleImage メソッドを呼び出す必要はないんだ。
§9.5rotate メソッドを作った時にも回転と拡大/縮小を一緒にできるようにしてたでしょ。
そう言われれば確かにそーだったね。
あ、でも rotateImage メソッドで拡大/縮小もできるんだったら scaleImage メソッドっていらないんじゃない?
rotateImage メソッドは affineCopy メソッドを使って画像の拡大/縮小をするんだけど、 scaleImage メソッドの方は stretchCopy メソッドを使うの。
stretchCopy メソッドについては §9.2 参照。
違うメソッドを使って画像の拡大/縮小をするから rotateImage メソッドと scaleImage メソッドを分けてるってコト?
前にもちょっと言ったと思うけど、単に画像を拡大/縮小するだけなら affineCopy メソッドより stretchCopy メソッドを使った方が効率がいいみたいだからね。
なるほど、だから angle 属性が指定されてなくて画像を回転する必要がない時は scaleImage メソッドを使うんだね。
そういうこと。
じゃ、あとは最後の if のとこだけど、これも特に問題ないよね?
xblur 属性と yblur 属性が指定されてたら boxBlur メソッドを呼び出すんだね。
xbluryblur って確か画像をぼかす doBoxBlur メソッドの引数になってたと思うから、 boxBlur は画像をぼかすメソッドってことかな?
doBoxBlur メソッドについては §9.6 参照。
ん、そうだよ。これも後で作るね。
さて、じゃこれで processImage メソッドは一通りチェックできたね。
結局 processImage メソッドって eximage マクロに指定されてる属性をチェックして、 画像を拡大/縮小したり回転したり変形したりぼかしたりするメソッドなんだよね?
そ。ちなみに processImage メソッドでチェックしてる属性、 つまり eximage マクロ専用の属性をまとめるとこうなるよ。

eximage マクロ専用の属性>

属性名属性の意味呼び出されるメソッド
angle回転する角度rotateImage メソッド(画像を回転)
x1変形後の画像の左上の x 座標transformImage メソッド(画像を変形)
y1変形後の画像の左上の y 座標
x2変形後の画像の右上の x 座標
y2変形後の画像の右上の y 座標
x3変形後の画像の左下の x 座標
y3変形後の画像の左下の y 座標
scale画像の拡大率(%)scaleImage メソッド(画像を拡大/縮小)
xscale画像の横方向拡大率(%)
yscale画像の縦方向拡大率(%)
xblur横方向のブラー範囲(pixel)boxBlur メソッド(画像をぼかす)
yblur縦方向のブラー範囲(pixel)

あとこの属性の他に image タグで使える属性ももちろん指定できるよ。
eximage マクロって processImage メソッドを呼び出す前に image タグを実行してたもんね。
ん。じゃ、あと最後に processImage メソッドは条件分岐が結構あるから、 処理の流れをまとめて図にしてみるね。

processImage メソッドの処理の流れ>

こうして見ると結構フクザツっぽく見えるね…
確かに分岐の数が多いからね。
でも一つ一つの条件分岐は単純だから特に問題ないでしょ?
まーね。
んじゃ今回はこれくらいにして、次回は transformImage メソッドとかの実際に画像を処理するメソッドを作ってくことにするね。
は〜い。
それじゃ、また次回ね!


前へ | TOP | 次へ