9月 20

ActionScript3で表示を画像に変換するPNGEncについて

ActionScript3でビットマップを保存する方法があるのですが、オブジェクトをビットマップに変換(キャッシュ)したものをサーバに送信して画像として1つのファイルに保存するわけです。もちろんJavaScriptに送信すれば画像としても表示できますが、クライアントのみで完結させても面白くないので省きます。
さて、ビットマップを送信できる状態にするためのライブラリとして、PNGEncというパッケージに行き着くのですが、公開が古くて何箇所か修正が必要だという解説ページはあるのですが、だったらなぜ修正版を掲載しないのかという素朴な疑問に行き着くわけで、修正版を載せておくことにします。
以下のコードを「PNGEnc.as」としてテキストで保存します。インポートや使い方については、ここでは省略します。

package {

import flash.geom.*;
import flash.display.*;
import flash.utils.*;

public class PNGEnc {

    public static function encode(img:BitmapData):ByteArray {
        // Create output byte array
        var png:ByteArray = new ByteArray();
        // Write PNG signature
        png.writeUnsignedInt(0x89504e47);
        png.writeUnsignedInt(0x0D0A1A0A);
        // Build IHDR chunk
        var IHDR:ByteArray = new ByteArray();
        IHDR.writeInt(img.width);
        IHDR.writeInt(img.height);
        IHDR.writeUnsignedInt(0x08060000); // 32bit RGBA
        IHDR.writeByte(0);
        writeChunk(png,0x49484452,IHDR);
        // Build IDAT chunk
        var IDAT:ByteArray= new ByteArray();
        for(var i:int=0;i < img.height;i++) {
            // no filter
            IDAT.writeByte(0);
            var p:uint;
            if ( !img.transparent ) {
                for(var j:int=0;j < img.width;j++) {
                    p = img.getPixel(j,i);
                    IDAT.writeUnsignedInt(
                        uint(((p&0xFFFFFF) << 8)|0xFF));
                }
            } else {
                for(var j2:int=0;j2 < img.width;j2++) {
                    p = img.getPixel32(j2,i);
                    IDAT.writeUnsignedInt(
                        uint(((p&0xFFFFFF) << 8)|((p >>> 24))));
                }
            }
        }
        IDAT.compress();
        writeChunk(png,0x49444154,IDAT);
        // Build IEND chunk
        writeChunk(png,0x49454E44,null);
        // return PNG
        return png;
    }

    private static var crcTable:Array;
    private static var crcTableComputed:Boolean = false;

    private static function writeChunk(png:ByteArray, 
            type:uint, data:ByteArray) {
        if (!crcTableComputed) {
            crcTableComputed = true;
            crcTable = [];
            for (var n:uint = 0; n < 256; n++) {
                var c2:uint = n;
                for (var k:uint = 0; k < 8; k++) {
                    if (c2 & 1) {
                        c2 = uint(uint(0xedb88320) ^ 
                            uint(c2 >>> 1));
                    } else {
                        c2 = uint(c2 >>> 1);
                    }
                }
                crcTable[n] = c2;
            }
        }
        var len:uint = 0;
        if (data != null) {
            len = data.length;
        }
        png.writeUnsignedInt(len);
        var p:uint = png.position;
        png.writeUnsignedInt(type);
        if ( data != null ) {
            png.writeBytes(data);
        }
        var e:uint = png.position;
        png.position = p;
        var c:uint = 0xffffffff;
        for (var i:int = 0; i < (e-p); i++) {
            c = uint(crcTable[
                (c ^ png.readUnsignedByte()) & 
                uint(0xff)] ^ uint(c >>> 8));
        }
        c = uint(c^uint(0xffffffff));
        png.position = e;
        png.writeUnsignedInt(c);
    }
}

}
2月 11

ActionScript3で一度loadした画像をキャッシュとして扱うのにハマった

Flash的にはインスタンスの複製とでもいうのだろうけど、こういう独自の呼び方はなかなか覚えられない傾向があるで覚える気もない(だから覚えないんだろうけど)。
ライブラリに最初から埋め込んである画像はクラスとして書き出しておけばnew hoge(0,0);とかでいくらでも表示を量産できるんだけど、Loaderでloadした画像で同じことをしようとしてはまった。
いろいろ端折ってるけど、たとえばこういう使い方

var loaderObj:Object = new Object(); // グローバルに宣言

var bm:Bitmap = Bitmap( loader.content ); // loadした内容をbmへ
loaderObj[ 'huga' ] = loader.content; // loadの内容をオブジェクトに入れて

var bm2:Bitmap = Bitmap( loaderObj[ 'huga'] ); // 他の関数から呼んでみる


実際に有効なのはbm2のみ。
オブジェクトに丸ごと放り込みさえすれば後は使い放題なのかと思いきや、1対1の関係らしい。
「actionScript3 loader」をググってヒットしたページを順番に眺めていたらヒントがあった。
Loaderではなく、bmを見に行けばいいらしい。

var bm:Bitmap = Bitmap( loader.content ); // loadした対象をbmへ
loaderObj[ 'huga' ] = bm; // loadの内容ではなくbmをオブジェクトに入れて

var bm2:Bitmap = Bitmap( loaderObj[ 'huga' ].bitmapData ); // 他の関数から呼んでみる


ここまで書いておいて、もしかすると?と思って書き直してみた。

var bm:Bitmap = Bitmap( loader.content ); // loadした対象をbmへ
loaderObj[ 'huga' ] = Bitmap( loader.content ); // loadの内容ではなBitmapの・・・(なんて呼び方?キャストではないしオブジェクトの変換?)をオブジェクトに入れて

var bm2:Bitmap = Bitmap( loaderObj[ 'huga' ].bitmapData ); // 他の関数から呼んでみる


リファレンス眺めてもこの挙動につながる箇所を見つけられなかったけど、とりあえずBitmapとしてオブジェクトに入れておけば、自由に扱えるらしいことが分かった。
BitmapでLoaderの何らかの制限から開放されているといったところか。
「Loaderはファイルを読むためのオブジェクト」とあるので、それ以外の用法は制限されるのか、一度Bitmapにすればその制限から開放されて通常の(期待した)使用ができるのか。
loaderObj[ ‘huga’ ]自体の型には何も違いはないのだけれど。
Loaderだから何々ができないという書き方がどこにもない(探した中では)のが悲しい。

ちなみに、clone()なしでも表示は量産できるだけど、オリジナルをnullにするとかどうにかすると道連れになるかどうかとかなのかもしれないけど、今のとこ必要ないので気にしないことにする。オブジェクトに入れたものが何者なのかこんがらがったままだし。

1月 18

ActionScript3のthisに悶え苦しんだ

Javascriptがそれなりに使いこなせれば、ActionScript2も特に問題がなかったわけですが、今頃になっていじり始めたActionScript3(以下AS3)で悶え苦しみました。
イベントやシンボルの扱いが変わったことも、調べさえすれば特に問題にならず、着実に開発は進んでいたのですが・・・
さて、行き詰ったのが「this」の扱い。
いままでは、とにかく「オレオレ!」でよかったので、イベントに関数を宛がってやれば、関数からオブジェクトをごにょるのはとても簡単でした。
が、AS3ではなぜか常にグローバル?なオブジェクト(実在してないっぽいのだが)になってしまう。

for( var i = 0; i < 10; i ++ ){
    var mc:Object = new mcHoge();
    mc.name = 'hoge' + i;
    mc.addEventListener(
        MouseEvent.MOUSE_OVER,
        editPointAnchorMouseOverHandler
    );
    function editPointAnchorMouseOverHandler(evt:MouseEvent):void{
        trace(this.name);
    }
    obj.addChild( mc );
}


いままではこんな感じで(AS3用にイベントを書いてあるよ)thisを追跡できたのに、AS3ではできない。
マウスが乗っかるとundefinedとなる。
ちなみにobjはStageとかで読み替えてみて。
いろいろ調べてもどうでもいいウンチク(もちろん大事なのだが)ばかりで、AS3の解説本の著者がグダグダ語っていたりするがよく見ればAdobeの例文のままで悲しい思いをするだけで実例がない。「ActionScript3 this global」とかでググってもだめっぽい。
本当はクラスにすればいいんだろうけど、別ファイルにしなければならないらしい?
同じファイルの中でパッケージ宣言できないのは厳しい。
使いまわさなくても都合上クラスが便利なこともある(性格にはクラスであるべきことのほうが多いのだが)のにファイル分けなきゃいけないの?
と、それたところで軌道修正。

結局のところ、別に関数を用意して、その中でオブジェクトを作ってやって戻り値もオブジェクトにすれば、擬似的にパッケージ化できることが判明。OOPを実装したはずのAS3のコンパイラが関数をどう扱っているのかなぞである。
結局はこうなった。

for( var i = 0; i < 10; i ++ ){
    var mc:Object = fuga( i );
    obj.addChild( mc );
}
function fuga( i:int ):Object {
    var mc:Object = new mcHoge();
    mc.name = 'hoge' + i;
    mc.addEventListener(
        MouseEvent.MOUSE_OVER,
        editPointAnchorMouseOverHandler
    );
    function editPointAnchorMouseOverHandler(evt:MouseEvent):void{
        trace(this.name);
    }
    return mc;
}


実質、関数をオブジェクトと定義しているわけだが、関数は関数しか宣言できないのでオブジェクト・・・あれ?
もしかしてこう書けばいい?
var mc:Object = function( i:int ):Object{ var mc:Object = new mcHoge(); [省略]; return mc; }
とすると強制型変換に失敗したと怒られた。
理屈ではこれでいいはずなのに、なんでだろ?
まぁ、初めたばかりだし、AS3の書き方に慣れてからまた考えよう。

9月 24

Flash MXにはマイターリミットが無い

Flashに図形を描く機能を持たせたツールを作ってますが、
PostScriptにはあるはずの機能が無いことに気がつきました。
そう、線をつなげた場合の角のパラメータ、マイターリミット。
検索してみたらAdobeのサイトに行き着いたのでlistStyleの項目を読んでいくと、
このパラメータを指定できるのはFlash8からということで、少々困ったことに。

う~ん、角は尖って欲しいんだけどなぁ・・・

9月 22

ActionScript2には円を描く関数が無い?

今頃にFlash MXを引っ張り出してActionScriptの勉強してます。
図形で円を動的に描こうと思って調べているとdrawCircleなる関数があるという。
しかしよくよく調べていくと、この関数が使えるのはActionScript3からだとか。
わざわざ数式を用意するのと、インスタンスを使うのではどちらが軽いんだろう。
Flash9は高いしなぁ~

9月 18

Flashでカーソルの座標を得る

Flashを始めました。
JavaScriptで処理するにも、いろいろと限界もありますし、
ActionScriptを利用すれば非常に簡単に実行できることもあったりします。
クロスブラウザを気にしなくていいのが最大の利点でしょうか。

さて、基本となるカーソルの座標取得方法です。

_root.onMouseMove = function(){
  trace( _root._xmouse + '/' + _root._ymouse );
}