9.2 画像の拡大・縮小(その1)

さて、これから画像の読み込み機能を拡張するプラグインを作っていくわけだけど、 その前にまず画像を拡大したり縮小したりするのに使うメソッドを見ていくことにするね。
りょ〜かい!
画像を拡大/縮小できるメソッドはいくつかあるんだけど、その中で重要なメソッドを2つ紹介するね。
はーい。
1つ目は stretchCopy っていうメソッド。
まずはこのスクリプトを first.ks に書き込んで実行してみて。

<0 番の前景レイヤを縦横 2.5 倍に拡大するスクリプト(first.ks の中身)>

; メッセージレイヤを非表示にします
[position page=fore layer=message0 visible=false]
; 表画面の 0 番の前景レイヤに画像を読み込みます
[image page=fore layer=0 storage="krkr" visible=true]

[iscript]
// 画像を見やすくするため背景レイヤを白色で塗りつぶします
kag.fore.base.fillRect(0, 0, kag.fore.base.width, kag.fore.base.height, 0xFFFFFF);

// 0 番の前景レイヤへの参照を変数 layer0 に代入します
var layer0 = kag.fore.layers[0];

// 親レイヤを 0 番の前景レイヤとして一時レイヤを作ります
var tempLayer = new Layer(kag, layer0);
// 一時レイヤに 0 番の前景レイヤの画像をコピーします
tempLayer.assignImages(layer0);

// 拡大後の幅(newWidth)と高さ(newHeight)を計算します
var newWidth = int (layer0.width * 2.5);
var newHeight = int (layer0.height * 2.5);
// 0 番の前景レイヤの幅と高さを拡大後の幅と高さに合わせます
layer0.setImageSize(newWidth, newHeight);
layer0.setSizeToImageSize();

// 一時レイヤの画像を拡大して 0 番の前景レイヤにコピーします
layer0.stretchCopy(0, 0, newWidth, newHeight, tempLayer, 0, 0, tempLayer.imageWidth, tempLayer.imageHeight, stLinear);

// 一時レイヤは必要なくなったので無効化します
invalidate tempLayer;
[endscript]

なんかすごいややこしそーなんだけど…
まぁとりあえず実行してみてよ。
う、うん、わかった。

<実行結果>

あ、画像が拡大されてるね。
ん、画像を縦横それぞれ 2.5 倍に拡大して表示するようにしたからね。
んじゃスクリプトの方を見ていこっか。
うん。
まず最初の position タグと image タグは大丈夫だよね?
position タグで表画面のメッセージレイヤを非表示にしてて、 image タグで表画面の 0 番の前景レイヤに画像を読み込んでるんだよね。
そ。じゃ次は iscript タグの後の TJS スクリプトを見てくね。
最初の fillRect メソッドは前にも同じことやったんだけど、覚えてる?
え、そーだったっけ?
前にパズルのピース用レイヤのテストをした時に同じことやったでしょ。
§8.3§8.4 参照。
あ、背景レイヤを白く塗りつぶす時のスクリプトだね。
そう。まぁこれは単に画像を見やすくするためにやってるだけだから、 今回はそんなに重要じゃないんだけどね。
あ、そーなんだ。
ってワケで、次いくね。
次は… layer0 っていう変数に kag.fore.layers[0] を代入してるね。
…これって何のためにやってるの?
こうすると layer0kag.fore.layers[0]、 つまり表画面の 0 番の前景レイヤへの参照になるってのはわかるよね?
えっと、layer0 が 0 番の前景レイヤへの参照になるってことは、 layer0kag.fore.layers[0] とおんなじように使えるようになるってコトなんだよ…ね?
ん、そういうことになるね。
こうすればスクリプトがちょっと短く書けるでしょ。
確かにそーだけど、じゃあそのために参照を作ったの?
あと、kag.fore.layers[0] みたいに .(ドット)や [] が多い時には、こうやって参照を作るとちょっとだけ効率的な処理ができるってのもあるかな。
へぇ、そうなんだ。
まぁホントにほんのちょっとだけなんだけどね。
じゃ次ね。
tempLayer っていうレイヤを作ってるみたいだけど、 なんでここでレイヤなんて作ってるの?
画像を拡大したり縮小したりするためには、 0 番の前景レイヤ以外にもう1つ別のレイヤを作らなくちゃいけないからだよ。
えっ、そーなの? なんで?
んー、それは stretchCopy メソッドとかの画像を拡大/縮小したりするメソッドが全部そういう仕様になってるから、 としか言いようがないんだけどね。
ふぅん、そーなんだ。
ま、詳しいことはまた後で説明するね。
レイヤを作った後に呼び出してる assignImages メソッドはどういうメソッドだったか覚えてる?
ん〜っと…なんかだいぶ前に出てきてたよーな気がするんだけど…
確か画像をコピーする、みたいな感じのメソッドじゃなかったっけ?
正確には画像を共有するメソッドだよ。
まぁ結果的には引数に指定されてるレイヤが持ってる画像がコピーされたように見えるんだけどね。
assignImages メソッドについては §4.6 参照。
あー、そういえば画像を共有するからコピーするより実行時間が早いって言ってたっけ。
ん。これで一時レイヤ(tempLayer のことね)の画像は 0 番の前景レイヤと同じになったわけだね。
画像を共有するのも stretchCopy メソッドを使うために必要なの?
そうだよ。画像を拡大する時には、拡大したい画像と同じ画像を別のレイヤに読み込ませとく必要があるの。
ふぅん、画像を拡大するのって結構大変なんだねぇ。
まぁ stretchCopy メソッドを呼び出すだけで拡大完了!ってワケにはいかないからね。
じゃ次いくね。newWidthnewHeight っていう変数はそれぞれ拡大後の画像の幅と高さになってるんだけど、 幅と高さをどうやって計算してるかはわかるよね?
んーとね、newWidthwidth * 2.5 で、 newHeightheight * 2.5 だから、 それぞれ 0 番の前景レイヤの画像(拡大前の画像だよね)の幅と高さを2.5倍した値になってるね。
int 演算子は覚えてる?
えっと、width * 2.5 とかの計算結果を整数にする演算子…じゃなかったっけ?
int 演算子については §1.6§8.5 参照。
ん、そうだね。
幅と高さはどっちも整数になるから、int 演算子を使うことで、 掛け算の結果が小数になった時に小数点以下を切り捨てて整数にしてるわけね。
なるほどね。
次の setImageSize メソッドと setSizeToImageSize メソッドは前にも何回か使ったことあるからわかるよね?
setImageSize メソッドで 0 番の前景レイヤが持ってる画像の幅と高さを拡大後の画像の幅と高さに合わせてて、 setSizeToImageSize メソッドで 0 番の前景レイヤの表示サイズをレイヤが持ってる画像のサイズに合わせてるんだよね。
setImageSize メソッドについては §7.3setSizeToImageSize メソッドについては §3.3 参照。
ん、こうすれば 0 番の前景レイヤのサイズを拡大後の画像サイズに合わせられるよね。
だよね。
これで画像を拡大する準備ができたから、 いよいよ stretchCopy メソッドの出番だよ。
なんか引数がいっぱいあるねぇ。
まぁね。
ちなみに stretchCopy メソッドの引数はこうなってるよ。

stretchCopy メソッドの引数>
引数名引数の意味デフォルト値
第1引数dleftコピー先の四角形の左端の位置
第2引数dtopコピー先の四角形の上端の位置
第3引数dwidthコピー先の四角形の幅
第4引数dheightコピー先の四角形の高さ
第5引数srcコピー元のレイヤ
第6引数sleftコピー元の四角形の左端の位置
第7引数stopコピー元の四角形の上端の位置
第8引数swidthコピー元の四角形の幅
第9引数sheightコピー元の四角形の高さ
第10引数type拡大縮小(補間)のタイプstNearest(最近傍補間)

コピー先とかコピー元ってのがいっぱいあるね。
stretchCopy メソッドはレイヤの画像を他のレイヤに拡大/縮小コピーするメソッドだから、 コピー元は拡大/縮小する前の画像が読み込まれてるレイヤで、 コピー先はコピー元の画像を拡大/縮小してコピーするレイヤになるの。
もしかして tempLayer を作ってたのって、 コピー先とコピー元の2つのレイヤがないといけないからなの?
ん、そういうこと。
じゃあ、コピー元が 0 番の前景レイヤで、 コピー先が tempLayer ってコト?
んーん、その逆。最終的に 0 番の前景レイヤに拡大後の画像をコピーしたいわけだから、 コピー先が 0 番の前景レイヤになるの。
んで、拡大コピーするためにはコピー元のレイヤが必要だから、 stretchCopy メソッドを呼び出す前に assignImages メソッドを使って tempLayer に拡大する前の画像をコピー(正確には共有だけどね)してたの。
なるほどねぇ…
う〜ん、でもこの表を見ただけじゃどうやって拡大コピーしてるのかよくわかんないね…
それじゃ図にしてみよっか。
そだね。

stretchCopy メソッドの実行例>

imageWidth プロパティと imageHeight プロパティについては §3.3 参照。

上の図の左側にある 青い四角 の部分がコピー元のレイヤで、 右側にある 赤い四角 の部分がコピー先のレイヤを表してるの。
これって 吉里吉里のアイコン画像 の部分を拡大コピーしてるの?
ん、そうだよ。
まず、第1引数の dleft と第2引数の dtop は、 それぞれコピー先のレイヤの中のどの位置に画像を拡大/縮小コピーするかを指定する引数なんだ。
レイヤ全体に画像を拡大/縮小コピーするわけじゃないんだね。
もちろんレイヤ全体に画像を拡大/縮小コピーもできるけど、 レイヤの一部に画像をコピーすることもできるよ。
で、第3引数の dwidth と第4引数の dheight がそれぞれ拡大/縮小後の画像の高さと幅ね。
何倍とかで指定するんじゃなくって、拡大/縮小後の画像のサイズを指定するんだね。
そ。だから倍率で指定したい時は、拡大/縮小前の画像サイズにその倍率を掛ければいいワケ。
そーいえばさっきのスクリプトでも layer0.width * 2.5 みたいに倍率を掛けて拡大後の画像サイズを計算してたよね。
ん。で、第5引数の src がコピー元のレイヤね。
えっと、さっきのスクリプトだとこれが tempLayer になるんだよね?
そうだよ。
あと第6引数の sleft と第7引数の stop がそれぞれコピー元レイヤのどの位置にある画像を拡大/縮小コピーするかを指定する引数で…
第8引数の swidth と第9引数の sheight がそれぞれ拡大/縮小する前の画像の幅と高さだね。
そういうこと。
だからさっきのスクリプトの場合はこうなるよね。

<first.ks での stretchCopy メソッド>

この場合はコピー元のレイヤ全体の画像をコピー先のレイヤ全体に拡大コピーしてるから、 dleft, dtop, sleft, stop は全部 0 で、 dwidth, dheight, swidth, sheight がそれぞれ layer0.imageWidth, layer0.imageHeight, tempLayer.imageWidth, tempLayer.imageHeight になるわけね。
ん? stretchCopy メソッドの引数の dwidthdtop って newWidthnewHeight になってるけど?
setImageSize メソッドで 0 番の前景レイヤの画像の幅と高さを newWidthnewHeight に設定したでしょ。
あそっか。だから newWidthlayer0.imageWidth はおんなじで、 newHeightlayer0.imageHeight もおんなじになってるんだね。
さて、後は第10引数の type なんだけど…
あ、そういえばこれって何なの?
なんかこれだけコピー元とかコピー先のレイヤが関係なさそーなんだけど…?
これはね、拡大/縮小する時に画像をどうやって補間するかを指定する引数なの。
画像を補間するって?
んー、これはちょっと説明が難しいから、実際に引数を変えて実行して見比べてみるのがわかりやすいかもね。
そーなの?
ってワケで、ちょっと実行してみるね。

<各補間法で画像を拡大した結果>

第10引数に指定できる補間の種類には4つあって、 上の図は左からそれぞれ stNearest(最近傍補間)、 stFastLinear(低精度線形補間)、 stLinear(線形補間)、 stCubic(3次元補間)を指定して拡大した画像だよ。
一番左のは他のと比べてあんまりキレイじゃないね。
まぁ stNearest が一番単純な補間方法を使ってるからね。
真ん中の2つはおんなじに見えるんだけど?
stFastLinearstLinear は同じように見えるんだけど、実はビミョーに違ってるんだ。
え、そーなの?
うん。まぁ確かに見た目はほとんど同じだけどね。
だよねぇ。
あと、一番右のは一番キレイに拡大できてるように見えるけど、 ちょっと画像がぼやけちゃってる感じだね。
ん、stCubic はキレイに拡大できるんだけど、 拡大率が高いとちょっと画像がぼやけちゃうんだよね。
ってワケで、4つの補間法にはそれぞれ特徴があるから、上手く使い分ける必要があるの。
う〜ん…でも stNearest だっけ?  あの一番左のはあんまり使えないんじゃない?
見た目にはそうなんだけど、stNearest は4つの補間法の中で一番早く画像を拡大/縮小できるから、 画質にはこだわらないけど出来るだけ早く画像を拡大/縮小したい時には使えると思うよ。
ふぅん、そーなんだ。
ちなみに画像を拡大/縮小する速度が一番早いのが stNearest で、 次に早いのが stFastLinear、 その次が stLinear で、 一番遅いのが stCubic なんだ。
えっと、それってつまり右に行くほど画質は良くなるけど、画像を拡大/縮小するのに時間がかかるってコト?
ん、そういうことだね。
だからどの補間法を使うのが良いかは画質重視なのか速度重視なのかによって変わってくるよね。
なるほどねぇ。
今回みたいに画像を普通に拡大表示する時は stFastLinearstLinear あたりでいいんじゃないかな。
あ、だからここの stretchCopy メソッドの第10引数は stLinear になってるんだね。
そ。んじゃ stretchCopy メソッドについては大体こんな感じだから、 スクリプトの方に戻るね。
って言っても、後は一時レイヤを無効化してるだけなんだけどね。
stretchCopy メソッドで画像を拡大し終わったから、 もう一時レイヤは要らなくなったってこと?
ん、だからここで無効化してるの。
これでスクリプトは一通り見てこれたわけだけど、大体解った?
うん、おっけーだよ。
それじゃ、あともう一つ画像を拡大/縮小するメソッドがあるんだけど…
今から見ていくとかなり長くなっちゃいそうだから、次回にしよっか。
今回も結構長かったもんね。
うん、りょーかい。
ん、それじゃまた次回ね!


前へ | TOP | 次へ