flash on 2010-10-2

by h_sakurai
import org.libspark.pv3d.Metasequoia;
♥0 | Line 1099 | Modified 2010-10-02 16:58:41 | MIT License
play

ActionScript3 source code

/**
 * Copyright h_sakurai ( http://wonderfl.net/user/h_sakurai )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/b80h
 */

package {
import org.papervision3d.view.*;
//import org.libspark.pv3d.Metasequoia;
 import flash.text.*;
    public class MetasequoiaTest extends BasicView{
        //兵士モデル
        private var soldier:Metasequoia=  new Metasequoia();
 
        public function MetasequoiaTest(){
            //メタセコイアクラスの生成
             addChild(tf);

            //mqoファイルの読込み
//            soldier.load("heisi.mqo", 1);
            
                soldier.loadText(xml+"");
            soldier.y = -150;

 
            //シーンに追加
            scene.addChild(soldier);
 
            //カメラの設定
            camera.x = 0;
            camera.y = 0;
            camera.z =-2000;

            camera.focus = 100;        //focus×zoom分、オブジェクトから離れている場合
            camera.zoom = 10;        //画面に同じ大きさで描画される
 
            //描画開始
            startRendering();
        }
    }
}
var xml:XML=(<root><![CDATA[Metasequoia Document
Format Text Ver 1.0

Scene {
    pos -26.7442 -27.9070 1500.0000
    lookat 0.0000 0.0000 0.0000
    head -20.3037
    pich 0.8308
    ortho 0
    zoom2 2.7397
    amb 0.250 0.250 0.250
}
Material 1 {
    "mat1" col(1.000 0.125 0.000 1.000) dif(0.800) amb(0.600) emi(0.000) spc(0.000) power(5.00)
}
Object "obj1" {
    visible 15
    locking 0
    shading 0
    facet 59.5
    color 0.898 0.498 0.698
    color_type 0
    vertex 42 {
        -0.0001 100.0000 0.0000
        -0.0001 92.3879 66.5691
        27.0598 92.3879 47.0715
        38.2683 92.3879 0.0000
        -38.2683 92.3879 0.0000
        -27.0598 92.3879 47.0715
        -0.0001 70.7107 123.0037
        49.9999 70.7107 86.9767
        70.7106 70.7107 0.0000
        -70.7108 70.7107 0.0000
        -50.0000 70.7107 86.9767
        -0.0001 38.2683 160.7121
        65.3280 38.2683 113.6406
        92.3878 38.2683 0.0000
        -92.3879 38.2683 0.0000
        -65.3283 38.2683 113.6405
        -0.0001 0.0000 173.9534
        70.7106 0.0000 123.0037
        99.9999 0.0000 0.0000
        -100.0000 0.0000 0.0000
        -70.7108 0.0000 123.0037
        -0.0001 100.0000 0.0000
        -0.0001 92.3879 66.5691
        27.0598 92.3879 47.0715
        38.2683 92.3879 0.0000
        -38.2683 92.3879 0.0000
        -27.0598 92.3879 47.0715
        -0.0001 70.7107 123.0037
        49.9999 70.7107 86.9767
        70.7106 70.7107 0.0000
        -70.7108 70.7107 0.0000
        -50.0000 70.7107 86.9767
        -0.0001 38.2683 160.7121
        65.3280 38.2683 113.6406
        92.3878 38.2683 0.0000
        -92.3879 38.2683 0.0000
        -65.3283 38.2683 113.6405
        -0.0001 0.0000 173.9534
        70.7106 0.0000 123.0037
        99.9999 0.0000 0.0000
        -100.0000 0.0000 0.0000
        -70.7108 0.0000 123.0037
    }
    face 38 {
        3 V(0 2 1) M(0) UV(0.06250 0.00000 0.12500 0.12500 0.00000 0.12500)
        3 V(0 3 2) M(0) UV(0.18750 0.00000 0.25000 0.12500 0.12500 0.12500)
        3 V(0 5 4) M(0) UV(0.81250 0.00000 0.87500 0.12500 0.75000 0.12500)
        3 V(0 1 5) M(0) UV(0.93750 0.00000 1.00000 0.12500 0.87500 0.12500)
        4 V(1 2 7 6) M(0) UV(0.00000 0.12500 0.12500 0.12500 0.12500 0.25000 0.00000 0.25000)
        4 V(2 3 8 7) M(0) UV(0.12500 0.12500 0.25000 0.12500 0.25000 0.25000 0.12500 0.25000)
        4 V(4 5 10 9) M(0) UV(0.75000 0.12500 0.87500 0.12500 0.87500 0.25000 0.75000 0.25000)
        4 V(5 1 6 10) M(0) UV(0.87500 0.12500 1.00000 0.12500 1.00000 0.25000 0.87500 0.25000)
        4 V(6 7 12 11) M(0) UV(0.00000 0.25000 0.12500 0.25000 0.12500 0.37500 0.00000 0.37500)
        4 V(7 8 13 12) M(0) UV(0.12500 0.25000 0.25000 0.25000 0.25000 0.37500 0.12500 0.37500)
        4 V(9 10 15 14) M(0) UV(0.75000 0.25000 0.87500 0.25000 0.87500 0.37500 0.75000 0.37500)
        4 V(10 6 11 15) M(0) UV(0.87500 0.25000 1.00000 0.25000 1.00000 0.37500 0.87500 0.37500)
        4 V(11 12 17 16) M(0) UV(0.00000 0.37500 0.12500 0.37500 0.12500 0.50000 0.00000 0.50000)
        4 V(12 13 18 17) M(0) UV(0.12500 0.37500 0.25000 0.37500 0.25000 0.50000 0.12500 0.50000)
        4 V(14 15 20 19) M(0) UV(0.75000 0.37500 0.87500 0.37500 0.87500 0.50000 0.75000 0.50000)
        4 V(15 11 16 20) M(0) UV(0.87500 0.37500 1.00000 0.37500 1.00000 0.50000 0.87500 0.50000)
        3 V(21 23 22) M(0) UV(0.06250 0.00000 0.12500 0.12500 0.00000 0.12500)
        3 V(21 24 23) M(0) UV(0.18750 0.00000 0.25000 0.12500 0.12500 0.12500)
        3 V(21 26 25) M(0) UV(0.81250 0.00000 0.87500 0.12500 0.75000 0.12500)
        3 V(21 22 26) M(0) UV(0.93750 0.00000 1.00000 0.12500 0.87500 0.12500)
        4 V(22 23 28 27) M(0) UV(0.00000 0.12500 0.12500 0.12500 0.12500 0.25000 0.00000 0.25000)
        4 V(23 24 29 28) M(0) UV(0.12500 0.12500 0.25000 0.12500 0.25000 0.25000 0.12500 0.25000)
        4 V(25 26 31 30) M(0) UV(0.75000 0.12500 0.87500 0.12500 0.87500 0.25000 0.75000 0.25000)
        4 V(26 22 27 31) M(0) UV(0.87500 0.12500 1.00000 0.12500 1.00000 0.25000 0.87500 0.25000)
        4 V(27 28 33 32) M(0) UV(0.00000 0.25000 0.12500 0.25000 0.12500 0.37500 0.00000 0.37500)
        4 V(28 29 34 33) M(0) UV(0.12500 0.25000 0.25000 0.25000 0.25000 0.37500 0.12500 0.37500)
        4 V(30 31 36 35) M(0) UV(0.75000 0.25000 0.87500 0.25000 0.87500 0.37500 0.75000 0.37500)
        4 V(31 27 32 36) M(0) UV(0.87500 0.25000 1.00000 0.25000 1.00000 0.37500 0.87500 0.37500)
        4 V(32 33 38 37) M(0) UV(0.00000 0.37500 0.12500 0.37500 0.12500 0.50000 0.00000 0.50000)
        4 V(33 34 39 38) M(0) UV(0.12500 0.37500 0.25000 0.37500 0.25000 0.50000 0.12500 0.50000)
        4 V(35 36 41 40) M(0) UV(0.75000 0.37500 0.87500 0.37500 0.87500 0.50000 0.75000 0.50000)
        4 V(36 32 37 41) M(0) UV(0.87500 0.37500 1.00000 0.37500 1.00000 0.50000 0.87500 0.50000)
        4 V(18 19 20 17) M(0) UV(0.25000 0.50000 0.75000 0.50000 0.87500 0.50000 0.12500 0.50000)
        3 V(3 21 4) M(0) UV(0.25000 0.12500 0.81250 0.00000 0.75000 0.12500)
        4 V(3 4 9 8) M(0) UV(0.25000 0.12500 0.75000 0.12500 0.75000 0.25000 0.25000 0.25000)
        4 V(8 9 14 13) M(0) UV(0.25000 0.25000 0.75000 0.25000 0.75000 0.37500 0.25000 0.37500)
        4 V(13 14 19 18) M(0) UV(0.25000 0.37500 0.75000 0.37500 0.75000 0.50000 0.25000 0.50000)
        3 V(16 17 20) M(0) UV(0.00000 0.50000 0.12500 0.50000 0.87500 0.50000)
    }
}
Eof

]]></root>);
import flash.text.*;

             var tf:TextField = new TextField();

/**
 * Metasequoia.as
 * 
 * @see http://snippets.libspark.org/
 * @see http://snippets.libspark.org/trac/wiki/rch850/Metasequoia
 * 
 * Copyright (c) 2007-2008 rch850
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

//package org.libspark.pv3d {
    import flash.display.BitmapData;
    import flash.events.*;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.net.URLRequest;
    import flash.utils.ByteArray;
    import flash.utils.Dictionary;
//    import org.libspark.pv3d.decoders.TGADecoder;
    import org.papervision3d.core.geom.TriangleMesh3D;
    import org.papervision3d.core.geom.renderables.Triangle3D;
    import org.papervision3d.core.geom.renderables.Vertex3D;
    import org.papervision3d.core.math.Matrix3D;
    import org.papervision3d.core.math.NumberUV;
    import org.papervision3d.core.proto.DisplayObjectContainer3D;
    import org.papervision3d.core.proto.GeometryObject3D;
    import org.papervision3d.core.proto.MaterialObject3D;
    import org.papervision3d.events.FileLoadEvent;
    import org.papervision3d.events.InteractiveScene3DEvent;
    import org.papervision3d.materials.BitmapFileMaterial;
    import org.papervision3d.materials.BitmapMaterial;
    import org.papervision3d.materials.ColorMaterial;
    import org.papervision3d.materials.utils.MaterialsList;
    import org.papervision3d.objects.DisplayObject3D;
//    import com.voidelement.images.BMPDecoder;    
    /**
    * メタセコイアのファイル(.mqo)を読み込むためのクラス。
    * 
    * var mqo = new Metasequoia();
    * mqo.addEventListener(...);
    * mqo.load("hoge.mqo");
    */
    class Metasequoia extends TriangleMesh3D {
        /**
        * コンストラクタ
        */
        function Metasequoia() {
            this.materials = new MaterialsList();
            super(null, new Array(), new Array(), null);
        }
        
        /**
        * @param file 読み込むファイルの URL。絶対パスで指定してください。
        * @param scale 読み込むときの拡大率。1 が原寸大です。
        */
        public function load(file:String, scale:Number = 1):void {
            _filename = file;
            _scale = scale;
            loadMetasequoia();
        }

        public function loadXML(xml:XML):void {
            loadText(xml.text)
        }        
        /**
         * ファイルの文字コード。よほどのことが無い限り shift_jis だと思います。
         */
        public var charset:String = "shift_jis";
        
        /**
         * 面の両側にマテリアルを貼るかどうかを指定します。
         */
        public var doubleSided:Boolean = false;
        
        /**
         * インタラクティビティを設定します。
         */
        public var interactive:Boolean = false;
        
        private var _loader:URLLoader;
        private var _filename:String;
        private var _materialsToLoad:int =0;
        private var _materialNames:Array;
        private var _scale:Number = 1;
        private var _prevMesh:DisplayObject3D;
        private var _prevDepth:int;
        
        private function loadMetasequoia():void {
            _loader = new URLLoader();
            _loader.dataFormat = URLLoaderDataFormat.BINARY;
            _loader.addEventListener(Event.COMPLETE, completeHandler);
            _loader.addEventListener(IOErrorEvent.IO_ERROR, defaultHandler);
            _loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, defaultHandler);
            _loader.addEventListener(ProgressEvent.PROGRESS, defaultHandler);
            _loader.load(new URLRequest(_filename));
        }
        
        private function completeHandler(evt:Event):void {
            var byteArray:ByteArray = ByteArray(_loader.data);
            loadText(byteArray.readMultiByte(byteArray.length, charset));
            dispatchEvent(evt.clone());
        }
        
        private function defaultHandler(evt:Event):void {
            dispatchEvent(evt.clone());
        }
        public function loadText(plainText:String):void {
            _filename = "./a.moq";
            loadText1(plainText);
        }

        public function loadText1(plainText:String):void {
            var lines:Array = plainText.split("\n");
            var l:int = 0;

            // Material チャンクを読み込む
            l = parseMaterialChunk(lines, 0);
            
            _prevDepth = 0;
            _prevMesh = this;
            // Object チャンクを読み込めなくなるまで読み込む
            while (l != -1) {
                l = parseObjectChunk(lines, l);
            }
            geometry.ready = true;
        }
        
        /**
        * Material チャンクの開始行を返します。
        * 見つからなかった場合には -1 を返します。
        */
        private function getMaterialChunkLine(lines:Array, startLine:int = 0):int {
            for (var i:uint = startLine; i < lines.length; ++i) {
                if (lines[i].indexOf("Material") == 0) {
                    return int(i);
                }
            }
            return -1;
        }
        
        /**
        * Material チャンクを読み込み、その最後の行番号を返します。
        * エラーが起こった場合は -1 を返します。
        */
        private function parseMaterialChunk(lines:Array, startLine:int):int {

            var l:int = getMaterialChunkLine(lines, startLine);
            if (l == -1) {
                return -1;
            }
            
            // 解析中の行の文字列
            var line:String = lines[l];
            // マテリアル数を取得
            var num:Number = parseInt(line.substr(9));
            
            if (isNaN(num)) {
                return -1;
            }
            ++l;
            _materialNames = new Array();
            
            // } で閉じているところの行番号
            var endLine:int = l + int(num);
            
            // mqo ファイルのあるディレクトリのパス
            var path:String = _filename.slice(0, _filename.lastIndexOf("/") + 1);
            
            for (; l < endLine; ++l) {
                var material:MaterialObject3D;
                line = lines[l];
                
                // マテリアルの名前を取得
                var nameBeginIndex:int = line.indexOf("\"");
                var nameEndIndex:int = line.indexOf("\"", nameBeginIndex + 1);
                var name:String = line.substring(nameBeginIndex + 1, nameEndIndex);
                _materialNames.push(name);
                
                // テクスチャファイル名
                var tex:String = getParam(line, "tex");
                
                if (tex) {
                    // テクスチャファイル名を取り囲む " " を取り除く
                    tex = tex.substr(1, tex.length - 2);
                    _materialsToLoad++;
                    if (tex.toLowerCase().search(/\.tga$/) != -1) {
                        material = loadTGAMaterial(path + tex);
                    } else if (tex.toLowerCase().search(/\.bmp$/) != -1) {
                        material = loadBMPMaterial(path + tex);
                    } else {
                        // テクスチャの URL を絶対にして読み込む
                        material = new BitmapFileMaterial(path + tex);
                        material.addEventListener(FileLoadEvent.LOAD_COMPLETE, materialLoadCompleteHandler);
                        material.addEventListener(FileLoadEvent.LOAD_ERROR, materialLoadErrorHandler);
                    }
                    // あまり重さが変わらないのでせっかくだからスムージング
                    material.smooth = true;
                } else {
                    // 形式 - col(1.000 1.000 0.000 1.000)
                    var colorstr:String = getParam(line, "col");
                    if (colorstr != null) {
                        var color:Array = colorstr.match(/\d+\.\d+/g);
                        var r:int = parseFloat(color[0]) * 255;
                        var g:int = parseFloat(color[1]) * 255;
                        var b:int = parseFloat(color[2]) * 255;
                        var a:Number = parseFloat(color[3]) * 100;
                        //trace("rgb = " + r + "," + g + "," + b);
                        material = new ColorMaterial((r << 16) | (g << 8) | b);
                    } else {
                        material = MaterialObject3D.DEFAULT;
                    }
                }
                
                material.doubleSided = this.doubleSided;
                material.interactive = this.interactive;
                material.name = name;
                
                materials.addMaterial(material, name);
            }
            
            return endLine;
        }

        
        /**
         * Creates a BitmapMaterial from TGA file and returns it.
         */
        private function loadTGAMaterial(url:String):BitmapMaterial {
            var material:BitmapMaterial = new BitmapMaterial();
            var loader:URLLoader = new URLLoader();
            loader.dataFormat = URLLoaderDataFormat.BINARY;
            loader.addEventListener(Event.COMPLETE, function(event:Event):void {
                var tga:TGADecoder = new TGADecoder(loader.data);
                material.bitmap = tga.bitmap;
                material.maxU = material.maxV = 1;
                material.resetMapping();
            });
            loader.load(new URLRequest(url));
            return material;
        }
        
        private function loadBMPMaterial(url:String):BitmapMaterial
        {
            var material:BitmapMaterial = new BitmapMaterial();
            var loader:URLLoader = new URLLoader();
            loader.dataFormat = URLLoaderDataFormat.BINARY;
            loader.addEventListener(Event.COMPLETE, function(event:Event):void {
                var bmpLoader:URLLoader = event.target as URLLoader;
                var decoder:BMPDecoder = new BMPDecoder();
                material.bitmap = decoder.decode(bmpLoader.data);
                material.maxU = material.maxV = 1;
                material.resetMapping();
            });
            loader.load(new URLRequest(url));
            return material;
        }
        
        private function materialLoadCompleteHandler(evt:FileLoadEvent):void {
            _materialsToLoad--;
            if(_materialsToLoad == 0){
                //COLLADA のソースにあった謎の一行。不具合の元になるのでコメントアウト
                //materials = new MaterialsList();
                dispatchEvent(new FileLoadEvent(FileLoadEvent.COLLADA_MATERIALS_DONE));
            }
        }
        
        private function materialLoadErrorHandler(evt:FileLoadEvent):void {
            _materialsToLoad--;
            if(_materialsToLoad == 0){
                dispatchEvent(new FileLoadEvent(FileLoadEvent.COLLADA_MATERIALS_DONE));
            }
        }
        
        /**
        * Object チャンクの開始行を返します。
        * 見つからなかった場合には -1 を返します。
        */
        private function getObjectChunkLine(lines:Array, startLine:int = 0):int {
            for (var i:uint = startLine; i < lines.length; ++i) {
                if (lines[i].indexOf("Object") == 0) {
                    return int(i);
                }
            }
            return -1;
        }
        
        /**
        * Object チャンクを読み込み、その最後の行番号を返します。
        * エラーが起こった場合は -1 を返します。
        */
        private function parseObjectChunk(lines:Array, startLine:int):int {
            var l:int = getObjectChunkLine(lines, startLine);
            if (l == -1) {
                return -1;
            }
            
            // 解析中の行の文字列
            var line:String = lines[l];
            
            // オブジェクト名を取得
            var objectName:String = line.substring(8, line.indexOf("\"", 8));
            ++l;
            
            var mesh:TriangleMesh3D = new TriangleMesh3D(null, new Array(), new Array(), objectName);
            var vertices:Array = mesh.geometry.vertices;
            var faces:Array = mesh.geometry.faces;
            
            // vertex チャンクを検索
            var vline:int = getChunkLine(lines, "vertex", l);
            if (vline == -1) {
                return -1;
            }
            
            // プロパティを読み込む
            var properties:Dictionary = new Dictionary();
            for (; l < vline; ++l) {
                line = lines[l];
                var props:Array = RegExp(/^\s*([\w]+)\s+(.*)$/).exec(line);
                properties[props[1]] = props[2];
            }
            
            line = lines[l];
            l = vline + 1;
            
            // 頂点数を取得
            var numVertices:int = parseInt(line.substring(line.indexOf("vertex") + 7));
            var vertexEndLine:int = l + numVertices;
            var firstVertexIndex:int = vertices.length;
            
            // vertex チャンクを読み込む
            for (; l < vertexEndLine; ++l) {
                line = lines[l];
                var coords:Array = line.match(/(-?\d+\.\d+)/g);
                var x:Number = parseFloat(coords[0]) * _scale;
                var y:Number = parseFloat(coords[1]) * _scale;
                var z:Number = -parseFloat(coords[2]) * _scale;
                vertices.push(new Vertex3D(x, y, z));
            }
            
            // face チャンクを検索
            l = getChunkLine(lines,  "face", l);
            if (l == -1) {
                return -1;
            }
            line = lines[l++];
            
            // 面数を取得
            var numFaces:int = parseInt(line.substring(line.indexOf("face") + 5));
            var faceEndLine:int = l + numFaces;
            
            // face チャンクを読み込む
            for (; l < faceEndLine; ++l) {
                if (properties["visible"] == "15") {
                    parseFace(faces, lines[l], vertices, firstVertexIndex, properties);
                }
            }
            
            // Resolve parent-child relationship.
            var depth:int;
            try {
                depth = parseInt(properties["depth"]);
            } catch (e:Error) {
                depth = 0;
            }
            var parentMesh:DisplayObjectContainer3D = _prevMesh;
            if (depth <= 0) {
                parentMesh = this;
                depth = 0;
            } else {
                while (depth <= _prevDepth) {
                    parentMesh = DisplayObject3D(parentMesh).parent;
                    --_prevDepth;
                }
            }
            parentMesh.addChild(mesh);
            _prevMesh = mesh;
            _prevDepth = depth;
            
            return l;
        }
        
        private function parseFace(faces:Array, line:String, vertices:Array, vertexOffset:int,
                properties:Dictionary):void {
            var vstr:String = getParam(line, "V");
            var mstr:String = getParam(line, "M");
            var uvstr:String = getParam(line, "UV");
            
            var v:Array = (vstr != null) ? vstr.match(/\d+/g) : [];
            var uv:Array = (uvstr != null) ? uvstr.match(/-?\d+\.\d+/g) : [];
            var a:Vertex3D;
            var b:Vertex3D;
            var c:Vertex3D;
            var d:Vertex3D;
            var material:MaterialObject3D;
            var uvA:NumberUV;
            var uvB:NumberUV;
            var uvC:NumberUV;
            var uvD:NumberUV;
            var face:Triangle3D;
            
            if (v.length == 3) {
                c = vertices[parseInt(v[0]) + vertexOffset];
                b = vertices[parseInt(v[1]) + vertexOffset];
                a = vertices[parseInt(v[2]) + vertexOffset];
                
                if (mstr != null) {
                    material = materials.getMaterialByName(_materialNames[parseInt(mstr)]);
                }
                
                if (uv.length != 0) {
                    uvC = new NumberUV(parseFloat(uv[0]), 1 - parseFloat(uv[1]));
                    uvB = new NumberUV(parseFloat(uv[2]), 1 - parseFloat(uv[3]));
                    uvA = new NumberUV(parseFloat(uv[4]), 1 - parseFloat(uv[5]));
                    face = new Triangle3D(this, [a, b, c], material, [uvA, uvB, uvC]);
                } else {
                    face = new Triangle3D(this, [a, b, c], material,
                        [new NumberUV(0, 0), new NumberUV(1, 0), new NumberUV(0, 1)]);
                }
                
                faces.push(face);
                
                if (properties["mirror"] == "1") {
                    var mirrorAxis:int = parseInt(properties["mirror_axis"]);
                    a = mirrorVertex(a, mirrorAxis);
                    b = mirrorVertex(b, mirrorAxis);
                    c = mirrorVertex(c, mirrorAxis);
                    vertices.push(a);
                    vertices.push(b);
                    vertices.push(c);
                    face = new Triangle3D(this, [c, b, a], material, face.uv.reverse());
                    faces.push(face);
                }
            } else if (v.length == 4) {
                d = vertices[parseInt(v[0]) + vertexOffset];
                c = vertices[parseInt(v[1]) + vertexOffset];
                b = vertices[parseInt(v[2]) + vertexOffset];
                a = vertices[parseInt(v[3]) + vertexOffset];
                
                if (mstr != null) {
                    material = materials.getMaterialByName(_materialNames[parseInt(mstr)]);
                }
                
                if (uv.length != 0) {
                    uvD = new NumberUV(parseFloat(uv[0]), 1 - parseFloat(uv[1]));
                    uvC = new NumberUV(parseFloat(uv[2]), 1 - parseFloat(uv[3]));
                    uvB = new NumberUV(parseFloat(uv[4]), 1 - parseFloat(uv[5]));
                    uvA = new NumberUV(parseFloat(uv[6]), 1 - parseFloat(uv[7]));
                } else {
                    uvD = new NumberUV(0, 0);
                    uvC = new NumberUV(1, 0);
                    uvB = new NumberUV(0, 1);
                    uvA = new NumberUV(1, 1);
                }
                face = new Triangle3D(this, [a, b, c], material, [uvA, uvB, uvC]);
                faces.push(face);
                face = new Triangle3D(this, [c, d, a], material, [uvC, uvD, uvA]);
                faces.push(face);
                
                if (properties["mirror"] == "1") {
                    mirrorAxis = parseInt(properties["mirror_axis"]);
                    a = mirrorVertex(a, mirrorAxis);
                    b = mirrorVertex(b, mirrorAxis);
                    c = mirrorVertex(c, mirrorAxis);
                    d = mirrorVertex(d, mirrorAxis);
                    vertices.push(a);
                    vertices.push(b);
                    vertices.push(c);
                    vertices.push(d);
                    face = new Triangle3D(this, [c, b, a], material, [uvC, uvB, uvA]);
                    faces.push(face);
                    face = new Triangle3D(this, [a, d, c], material, [uvA, uvD, uvC]);
                    faces.push(face);
                }
            }
        }
        
        /**
        * 頂点を軸に沿って反転させたものを返します。
        */
        private static function mirrorVertex(v:Vertex3D, axis:int):Vertex3D {
            return new Vertex3D(
                ((axis & 1) != 0) ? -v.x : v.x,
                ((axis & 2) != 0) ? -v.y : v.y,
                ((axis & 4) != 0) ? -v.z : v.z);
        }
        
        /**
        * Object チャンクの開始行を返します。
        */
        private static function getChunkLine(lines:Array, chunkName:String, startLine:int = 0):int {
            for (var i:uint = startLine; i < lines.length; ++i) {
                if (lines[i].indexOf(chunkName) != -1) {
                    return int(i);
                }
            }
            return -1;
        }
        
        /**
        * line 内で paramName(...) という形式で指定されているパラメータを返します。
        */
        private static function getParam(line:String, paramName:String):String {
            var prefix:String = paramName + "(";
            var prefixLen:int = prefix.length;
            
            var begin:int = line.indexOf(prefix, 0);
            if (begin == -1) {
                return null;
            }
            var end:int = line.indexOf(")", begin + prefixLen);
            if (end == -1){
                return null;
            }
            return line.substring(begin + prefixLen, end);
        }
    }
//}

/**
 * TGADecoder.as
 * 
 * @see http://snippets.libspark.org/
 * @see http://snippets.libspark.org/trac/wiki/rch850/Metasequoia
 *
 * Copyright (c) 2008 rch850
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

//package org.libspark.pv3d.decoders {
    import flash.display.BitmapData;
    import flash.utils.ByteArray;
    import flash.utils.Endian;
    
    class TGADecoder {
        //___________________________________________________________ const
        
        // constant value for _imageType
        private const TYPE_NONE:uint = 0x00;
        private const TYPE_INDEX_COLOR:uint = 0x01;
        private const TYPE_FULL_COLOR:uint = 0x02;
        private const TYPE_RLE_BIT:uint = 0x08;
        
        private const DIR_RIGHT_UP:int = 0;
        private const DIR_LEFT_UP:int = 1;
        private const DIR_RIGHT_DOWN:int = 2;
        private const DIR_LEFT_DOWN:int = 3;
        
        //___________________________________________________________ vars
        
        private var _bitmap:BitmapData;
        public function get bitmap():BitmapData {
            return _bitmap;
        }
        
        private var _idLength:int; // byte
        private var _colorMapType:int; // byte
        private var _imageType:int; // byte
        private var _colorMapIndex:int; // short
        private var _colorMapLength:int; // short
        private var _colorMapSize:int; // byte
        private var _originX:int; // short
        private var _originY:int; // short
        private var _width:int; // short
        public function get width():int {
            return _width;
        }
        private var _height:int; // short
        public function get height():int {
            return _height;
        }
        private var _bitDepth:int; // byte
        private var _descriptor:int; // byte
        public function get pixelDirection():int {
            // descriptor:
            //   4th bit: 0 = left to right, 1 = right to left
            //   5th bit: 0 = bottom up, 1 = top down
            return (_descriptor >> 4) & 3;
        }
        
        /**
         * Construct TGA file from ByteArray.
         */
        public function TGADecoder(bytes:ByteArray) {
            bytes.position = 0;
            bytes.endian = Endian.LITTLE_ENDIAN;
            
            _idLength = bytes.readByte();
            _colorMapType = bytes.readByte();
            _imageType = bytes.readByte();
            _colorMapIndex = bytes.readShort();
            _colorMapLength = bytes.readShort();
            _colorMapSize = bytes.readByte()
            _originX = bytes.readShort();
            _originY = bytes.readShort();
            _width = bytes.readShort();
            _height = bytes.readShort();
            _bitDepth = bytes.readByte();
            _descriptor = bytes.readByte();
            
            _bitmap = new BitmapData(_width, _height);
            
            // ignore unsupported formats.
            if ((_imageType & TYPE_FULL_COLOR) == 0
                    || (_imageType & TYPE_RLE_BIT) != 0) {
                throw new Error("Unsupported tga format.");
            }
            
            _bitmap.lock();
            try {
                if (_bitDepth == 32) {
                    loadBitmap32(bytes);
                } else if (_bitDepth == 24) {
                    loadBitmap24(bytes);
                }
            } finally {
                _bitmap.unlock();
            }
        }
        
        /**
         * Load 32 bpp bitmap.
         */
        private function loadBitmap32(bytes:ByteArray):void {
            var x:int, y:int;
            switch (pixelDirection) {
                case DIR_RIGHT_UP:
                    for (y = _bitmap.height - 1; y >= 0; --y) {
                        for (x = 0; x < _bitmap.width; ++x) {
                            _bitmap.setPixel32(x, y, bytes.readUnsignedInt());
                        }
                    }
                    break;
                case DIR_LEFT_UP:
                    for (y = _bitmap.height - 1; y >= 0; --y) {
                        for (x = _bitmap.width - 1; x >= 0; --x) {
                            _bitmap.setPixel32(x, y, bytes.readUnsignedInt());
                        }
                    }
                    break;
                case DIR_RIGHT_DOWN:
                    for (y = 0; y < _bitmap.height; ++y) {
                        for (x = 0; x < _bitmap.width; ++x) {
                            _bitmap.setPixel32(x, y, bytes.readUnsignedInt());
                        }
                    }
                    break;
                case DIR_LEFT_DOWN:
                    for (y = 0; y < _bitmap.height; ++y) {
                        for (x = _bitmap.width - 1; x >= 0; --x) {
                            _bitmap.setPixel32(x, y, bytes.readUnsignedInt());
                        }
                    }
                    break;
            }
        }
        
        /**
         * Load 24 bpp bitmap.
         */
        private function loadBitmap24(bytes:ByteArray):void {
            var x:int, y:int;
            var r:uint, g:uint, b:uint;
            switch (pixelDirection) {
                case DIR_RIGHT_UP:
                    for (y = _bitmap.height - 1; y >= 0; --y) {
                        for (x = 0; x < _bitmap.width; ++x) {
                            b = bytes.readUnsignedByte();
                            g = bytes.readUnsignedByte();
                            r = bytes.readUnsignedByte();
                            _bitmap.setPixel32(x, y, 0xFF000000 | (r << 16) | (g << 8) | b);
                        }
                    }
                    break;
                case DIR_LEFT_UP:
                    for (y = _bitmap.height - 1; y >= 0; --y) {
                        for (x = _bitmap.width - 1; x >= 0; --x) {
                            b = bytes.readUnsignedByte();
                            g = bytes.readUnsignedByte();
                            r = bytes.readUnsignedByte();
                            _bitmap.setPixel32(x, y, 0xFF000000 | (r << 16) | (g << 8) | b);
                        }
                    }
                    break;
                case DIR_RIGHT_DOWN:
                    for (y = 0; y < _bitmap.height; ++y) {
                        for (x = 0; x < _bitmap.width; ++x) {
                            b = bytes.readUnsignedByte();
                            g = bytes.readUnsignedByte();
                            r = bytes.readUnsignedByte();
                            _bitmap.setPixel32(x, y, 0xFF000000 | (r << 16) | (g << 8) | b);
                        }
                    }
                    break;
                case DIR_LEFT_DOWN:
                    for (y = 0; y < _bitmap.height; ++y) {
                        for (x = _bitmap.width - 1; x >= 0; --x) {
                            b = bytes.readUnsignedByte();
                            g = bytes.readUnsignedByte();
                            r = bytes.readUnsignedByte();
                            _bitmap.setPixel32(x, y, 0xFF000000 | (r << 16) | (g << 8) | b);
                        }
                    }
                    break;
            }
        }
    }
//}

/**
 * com.voidelement.images.BMPDecoder  Class for ActionScript 3.0 
 *  
 * @author       Copyright (c) 2007 munegon
 * @version      1.0
 *  
 * @link         http://www.voidelement.com/
 * @link         http://void.heteml.jp/blog/
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *  
 * http://www.apache.org/licenses/LICENSE-2.0 
 *  
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,  
 * either express or implied. See the License for the specific language 
 * governing permissions and limitations under the License. 
 */



//package com.voidelement.images {
    import flash.display.BitmapData;
    import flash.errors.IOError;
    import flash.utils.ByteArray;
    import flash.utils.Endian;
    
    class BMPDecoder {
        //___________________________________________________________ const
        
        private const BITMAP_HEADER_TYPE:String = "BM";
        
        private const BITMAP_FILE_HEADER_SIZE:int = 14;
        private const BITMAP_CORE_HEADER_SIZE:int = 12;
        private const BITMAP_INFO_HEADER_SIZE:int = 40;
        
        private const COMP_RGB      :int = 0;
        private const COMP_RLE8     :int = 1;
        private const COMP_RLE4     :int = 2;
        private const COMP_BITFIELDS:int = 3;
        
        private const BIT1 :int = 1;
        private const BIT4 :int = 4;
        private const BIT8 :int = 8;
        private const BIT16:int = 16;
        private const BIT24:int = 24;
        private const BIT32:int = 32;
        
        
        //___________________________________________________________ vars
        
        private var bytes:ByteArray;
        private var palette:Array;
        private var bd:BitmapData;
    
        private var nFileSize:uint;
        private var nReserved1:uint;
        private var nReserved2:uint;
        private var nOffbits:uint;
        
        private var nInfoSize:uint;
        private var nWidth:int;
        private var nHeight:int;
        private var nPlains:uint;
        private var nBitsPerPixel:uint;
        private var nCompression:uint;
        private var nSizeImage:uint;
        private var nXPixPerMeter:int;
        private var nYPixPerMeter:int;
        private var nColorUsed:uint;
        private var nColorImportant:uint;
        
        private var nRMask:uint;
        private var nGMask:uint;
        private var nBMask:uint;
        private var nRPos:uint;
        private var nGPos:uint;
        private var nBPos:uint;
        private var nRMax:uint;
        private var nGMax:uint;
        private var nBMax:uint;
        
        
        /**
         * コンストラクタ
         */
        public function BMPDecoder() {
            nRPos = 0;
            nGPos = 0;
            nBPos = 0;
        }
        
        
        /**
         * デコード
         * 
         * @param デコードしたいBMPファイルのバイナリデータ
         */
        public function decode( data:ByteArray ):BitmapData {
            bytes = data;
            bytes.endian = Endian.LITTLE_ENDIAN;
            bytes.position = 0;
            
            readFileHeader();
            
            nInfoSize = bytes.readUnsignedInt();
            
            switch ( nInfoSize ) {
                case BITMAP_CORE_HEADER_SIZE:
                    readCoreHeader();
                    break;
                case BITMAP_INFO_HEADER_SIZE:
                    readInfoHeader();
                    break;
                default:
                    readExtendedInfoHeader();
                    break;
            }
            
            bd = new BitmapData( nWidth, nHeight );
            
            switch ( nBitsPerPixel ){
                case BIT1:
                    readColorPalette();
                    decode1BitBMP();
                    break;
                case BIT4:
                    readColorPalette();
                    if ( nCompression == COMP_RLE4 ){
                        decode4bitRLE();
                    } else {
                        decode4BitBMP();
                    }
                    break;
                case BIT8:
                    readColorPalette();
                    if ( nCompression == COMP_RLE8 ){
                        decode8BitRLE();
                    } else {
                        decode8BitBMP();
                    }
                    break;
                case BIT16:
                    readBitFields();
                    checkColorMask();
                    decode16BitBMP();
                    break;
                case BIT24:
                    decode24BitBMP();
                    break;
                case BIT32:
                    readBitFields();
                    checkColorMask();
                    decode32BitBMP();
                    break;
                default:
                    throw new VerifyError("invalid bits per pixel : " + nBitsPerPixel );
            }
            
            return bd;
        }
        
        
        /**
         * BITMAP FILE HEADER 読み込み
         */
        private function readFileHeader():void {
            var fileHeader:ByteArray = new ByteArray();
            fileHeader.endian = Endian.LITTLE_ENDIAN;
            
            try {
                bytes.readBytes( fileHeader, 0, BITMAP_FILE_HEADER_SIZE );
                
                if ( fileHeader.readUTFBytes( 2 ) != BITMAP_HEADER_TYPE ){
                    throw new VerifyError("invalid bitmap header type");
                }
                
                nFileSize  = fileHeader.readUnsignedInt();
                nReserved1 = fileHeader.readUnsignedShort();
                nReserved2 = fileHeader.readUnsignedShort();
                nOffbits   = fileHeader.readUnsignedInt();
            } catch ( e:IOError ) {
                throw new VerifyError("invalid file header");
            }
        }
        
        
        /**
         * BITMAP CORE HEADER 読み込み 
         */
        private function readCoreHeader():void {
            var coreHeader:ByteArray = new ByteArray();
            coreHeader.endian = Endian.LITTLE_ENDIAN;
            
            try {
                bytes.readBytes( coreHeader, 0, BITMAP_CORE_HEADER_SIZE - 4 );
                
                nWidth  = coreHeader.readShort();
                nHeight = coreHeader.readShort();
                nPlains = coreHeader.readUnsignedShort();
                nBitsPerPixel = coreHeader.readUnsignedShort();
            } catch ( e:IOError ) {
                throw new VerifyError("invalid core header");
            }
        }
        
        
        /**
         * BITMAP INFO HEADER 読み込み
         */
        private function readInfoHeader():void {
            var infoHeader:ByteArray = new ByteArray();
            infoHeader.endian = Endian.LITTLE_ENDIAN;
            
            try {
                bytes.readBytes( infoHeader, 0, BITMAP_INFO_HEADER_SIZE - 4 );
                
                nWidth  = infoHeader.readInt();
                nHeight = infoHeader.readInt();
                nPlains = infoHeader.readUnsignedShort();
                nBitsPerPixel = infoHeader.readUnsignedShort();
                
                nCompression = infoHeader.readUnsignedInt();
                nSizeImage = infoHeader.readUnsignedInt();
                nXPixPerMeter = infoHeader.readInt();
                nYPixPerMeter = infoHeader.readInt();
                nColorUsed = infoHeader.readUnsignedInt();
                nColorImportant = infoHeader.readUnsignedInt();
            } catch ( e:IOError ) {
                throw new VerifyError("invalid info header");
            }
        }
        
        /**
         * 拡張 BITMAP INFO HEADER 読み込み
         */
        private function readExtendedInfoHeader():void {
            var infoHeader:ByteArray = new ByteArray();
            infoHeader.endian = Endian.LITTLE_ENDIAN;
            
            try {
                bytes.readBytes( infoHeader, 0, nInfoSize - 4 );
                
                nWidth  = infoHeader.readInt();
                nHeight = infoHeader.readInt();
                nPlains = infoHeader.readUnsignedShort();
                nBitsPerPixel = infoHeader.readUnsignedShort();
                
                nCompression = infoHeader.readUnsignedInt();
                nSizeImage = infoHeader.readUnsignedInt();
                nXPixPerMeter = infoHeader.readInt();
                nYPixPerMeter = infoHeader.readInt();
                nColorUsed = infoHeader.readUnsignedInt();
                nColorImportant = infoHeader.readUnsignedInt();
                
                if ( infoHeader.bytesAvailable >= 4 ) nRMask = infoHeader.readUnsignedInt();
                if ( infoHeader.bytesAvailable >= 4 ) nGMask = infoHeader.readUnsignedInt();
                if ( infoHeader.bytesAvailable >= 4 ) nBMask = infoHeader.readUnsignedInt();
            } catch ( e:IOError ) {
                throw new VerifyError("invalid info header");
            }
        }
        
        
        /**
         * ビットフィールド読み込み
         */
        private function readBitFields():void {
            if ( nCompression == COMP_RGB ){
                if ( nBitsPerPixel == BIT16 ){
                    // RGB555
                    nRMask = 0x00007c00;
                    nGMask = 0x000003e0;
                    nBMask = 0x0000001f;
                } else {
                    //RGB888;
                    nRMask = 0x00ff0000;
                    nGMask = 0x0000ff00;
                    nBMask = 0x000000ff;
                }
            } else if ( ( nCompression == COMP_BITFIELDS ) && ( nInfoSize < 52 ) ){
                try {
                    nRMask = bytes.readUnsignedInt();
                    nGMask = bytes.readUnsignedInt();
                    nBMask = bytes.readUnsignedInt();
                } catch ( e:IOError ) {
                    throw new VerifyError("invalid bit fields");
                }
            }
        }
        
        
        /**
         * カラーパレット読み込み
         */
        private function readColorPalette():void {
            var i:int;
            var len:int = ( nColorUsed > 0 ) ? nColorUsed : Math.pow( 2, nBitsPerPixel );
            palette = new Array( len );
            
            for ( i = 0; i < len; ++i ){
                palette[ i ] = bytes.readUnsignedInt();
            }
        }
        
        
        /**
         * 1bitのBMPデコード
         */
        private function decode1BitBMP():void {
            var x:int;
            var y:int;
            var i:int;
            var col:int;
            var buf:ByteArray = new ByteArray();
            var line:int = nWidth / 8;
            
            if ( line % 4 > 0 ){
                line = ( ( line / 4 | 0 ) + 1 ) * 4;
            }
            
            try {
                for ( y = nHeight - 1; y >= 0; --y ){
                    buf.length = 0;
                    bytes.readBytes( buf, 0, line );
                    
                    for ( x = 0; x < nWidth; x += 8 ){
                        col = buf.readUnsignedByte();
                        
                        for ( i = 0; i < 8; ++i ){
                            bd.setPixel( x + i, y, palette[ col >> ( 7 - i ) & 0x01 ] );
                        }
                    }
                }
            } catch ( e:IOError ) {
                throw new VerifyError("invalid image data");
            }
        }
        
        
        /**
         * 4bitのRLE圧縮BMPデコード
         */
        private function decode4bitRLE():void {
            var x:int;
            var y:int;
            var i:int;
            var n:int;
            var col:int;
            var data:uint;
            var buf:ByteArray = new ByteArray();
            
            try {
                for ( y = nHeight - 1; y >= 0; --y ){
                    buf.length = 0;
                    
                    while ( bytes.bytesAvailable > 0 ){
                        n = bytes.readUnsignedByte();
                        
                        if ( n > 0 ){
                            // エンコードデータ
                            data = bytes.readUnsignedByte();
                            for ( i = 0; i < n/2; ++i ){
                                buf.writeByte( data );
                            }
                        } else {
                            n = bytes.readUnsignedByte();
                            
                            if ( n > 0 ){
                                // 絶対モードデータ
                                bytes.readBytes( buf, buf.length, n/2 );
                                buf.position += n/2;
                                
                                if ( n/2 + 1 >> 1 << 1 != n/2 ){
                                    bytes.readUnsignedByte();
                                }
                            } else {
                                // EOL
                                break;
                            }
                        }
                    }
                    
                    buf.position = 0;
                    
                    for ( x = 0; x < nWidth; x += 2 ){
                        col = buf.readUnsignedByte();
                        
                        bd.setPixel( x, y, palette[ col >> 4 ] );
                        bd.setPixel( x + 1, y, palette[ col & 0x0f ] );
                    }
                }
            } catch ( e:IOError ) {
                throw new VerifyError("invalid image data");
            }
        }
        
        
        /**
         * 4bitの非圧縮BMPデコード
         */
        private function decode4BitBMP():void {
            var x:int;
            var y:int;
            var i:int;
            var col:int;
            var buf:ByteArray = new ByteArray();
            var line:int = nWidth / 2;
            
            if ( line % 4 > 0 ){
                line = ( ( line / 4 | 0 ) + 1 ) * 4;
            }
            
            try {
                for ( y = nHeight - 1; y >= 0; --y ){
                    buf.length = 0;
                    bytes.readBytes( buf, 0, line );
                    
                    for ( x = 0; x < nWidth; x += 2 ){
                        col = buf.readUnsignedByte();
                        
                        bd.setPixel( x, y, palette[ col >> 4 ] );
                        bd.setPixel( x + 1, y, palette[ col & 0x0f ] );
                    }
                }
            } catch ( e:IOError ) {
                throw new VerifyError("invalid image data");
            }
        }
        
        
        /**
         * 8bitのRLE圧縮BMPデコード
         */
        private function decode8BitRLE():void {
            var x:int;
            var y:int;
            var i:int;
            var n:int;
            var col:int;
            var data:uint;
            var buf:ByteArray = new ByteArray();
            
            try {
                for ( y = nHeight - 1; y >= 0; --y ){
                    buf.length = 0;
                    
                    while ( bytes.bytesAvailable > 0 ){
                        n = bytes.readUnsignedByte();
                        
                        if ( n > 0 ){
                            // エンコードデータ
                            data = bytes.readUnsignedByte();
                            for ( i = 0; i < n; ++i ){
                                buf.writeByte( data );
                            }
                        } else {
                            n = bytes.readUnsignedByte();
                            
                            if ( n > 0 ){
                                // 絶対モードデータ
                                bytes.readBytes( buf, buf.length, n );
                                buf.position += n;
                                if ( n + 1 >> 1 << 1 != n ){
                                    bytes.readUnsignedByte();
                                }
                            } else {
                                // EOL
                                break;
                            }
                        }
                    }
                    
                    buf.position = 0;
                    
                    for ( x = 0; x < nWidth; ++x ){
                        bd.setPixel( x, y, palette[ buf.readUnsignedByte() ] );
                    }
                }
            } catch ( e:IOError ) {
                throw new VerifyError("invalid image data");
            }
        }
        
        /**
         * 8bitの非圧縮BMPデコード
         */
        private function decode8BitBMP():void {
            var x:int;
            var y:int;
            var i:int;
            var col:int;
            var buf:ByteArray = new ByteArray();
            var line:int = nWidth;
            
            if ( line % 4 > 0 ){
                line = ( ( line / 4 | 0 ) + 1 ) * 4;
            }
            
            try {
                for ( y = nHeight - 1; y >= 0; --y ){
                    buf.length = 0;
                    bytes.readBytes( buf, 0, line );
                    
                    for ( x = 0; x < nWidth; ++x ){
                        bd.setPixel( x, y, palette[ buf.readUnsignedByte() ] );
                    }
                }
            } catch ( e:IOError ) {
                throw new VerifyError("invalid image data");
            }
        }
        
        /**
         * 16bitのBMPデコード
         */
        private function decode16BitBMP():void {
            var x:int;
            var y:int;
            var col:int;
            
            try {
                for ( y = nHeight - 1; y >= 0; --y ){
                    for ( x = 0; x < nWidth; ++x ){
                        col = bytes.readUnsignedShort();
                        bd.setPixel( x, y, ( ( ( col & nRMask ) >> nRPos )*0xff/nRMax << 16 ) + ( ( ( col & nGMask ) >> nGPos )*0xff/nGMax << 8 ) + ( ( ( col & nBMask ) >> nBPos )*0xff/nBMax << 0 ) );
                    }
                }
            } catch ( e:IOError ) {
                throw new VerifyError("invalid image data");
            }
        }
        
        /**
         * 24bitのBMPデコード
         */
        private function decode24BitBMP():void {
            var x:int;
            var y:int;
            var col:int;
            var buf:ByteArray = new ByteArray();
            var line:int = nWidth * 3;
            
            if ( line % 4 > 0 ){
                line = ( ( line / 4 | 0 ) + 1 ) * 4;
            }
            
            try {
                for ( y = nHeight - 1; y >= 0; --y ){
                    buf.length = 0;
                    bytes.readBytes( buf, 0, line );
                    
                    for ( x = 0; x < nWidth; ++x ){
                        bd.setPixel( x, y, buf.readUnsignedByte() + ( buf.readUnsignedByte() << 8 ) + ( buf.readUnsignedByte() << 16 ) );
                    }
                }
            } catch ( e:IOError ) {
                throw new VerifyError("invalid image data");
            }
        }
        
        /**
         * 32bitのBMPデコード
         */
        private function decode32BitBMP():void {
            var x:int;
            var y:int;
            var col:int;
            
            try {
                for ( y = nHeight - 1; y >= 0; --y ){
                    for ( x = 0; x < nWidth; ++x ){
                        col = bytes.readUnsignedInt();
                        bd.setPixel( x, y, ( ( ( col & nRMask ) >> nRPos )*0xff/nRMax << 16 ) + ( ( ( col & nGMask ) >> nGPos )*0xff/nGMax << 8 ) + ( ( ( col & nBMask ) >> nBPos )*0xff/nBMax << 0 ) );
                    }
                }
            } catch ( e:IOError ) {
                throw new VerifyError("invalid image data");
            }
        }
        
        
        /**
         * カラーマスクチェック
         */
        private function checkColorMask():void {
            if ( ( nRMask & nGMask ) | ( nGMask & nBMask ) | ( nBMask & nRMask ) ){
                throw new VerifyError("invalid bit fields");
            }
            
            while ( ( ( nRMask >> nRPos ) & 0x00000001 ) == 0 ){
                nRPos++;
            }
            while ( ( ( nGMask >> nGPos ) & 0x00000001 ) == 0 ){
                nGPos++;
            }
            while ( ( ( nBMask >> nBPos ) & 0x00000001 ) == 0 ){
                nBPos++;
            }
            
            nRMax = nRMask >> nRPos;
            nGMax = nGMask >> nGPos;
            nBMax = nBMask >> nBPos;
        }
        
        
        /**
         * 情報出力
         */
        public function traceInfo():void {
            trace("---- FILE HEADER ----");
            trace("nFileSize: " + nFileSize );
            trace("nReserved1: " + nReserved1 );
            trace("nReserved2: " + nReserved2 );
            trace("nOffbits: " + nOffbits );
            
            trace("---- INFO HEADER ----");
            trace("nWidth: " + nWidth );
            trace("nHeight: " + nHeight );
            trace("nPlains: " + nPlains );
            trace("nBitsPerPixel: " + nBitsPerPixel );
            
            if ( nInfoSize >= 40 ){
                trace("nCompression: " + nCompression );
                trace("nSizeImage: " + nSizeImage );
                trace("nXPixPerMeter: " + nXPixPerMeter );
                trace("nYPixPerMeter: " + nYPixPerMeter );
                trace("nColorUsed: " + nColorUsed );
                trace("nColorUsed: " + nColorImportant );
            }
            
            if ( nInfoSize >= 52 ){
                trace("nRMask: " + nRMask.toString( 2 ) );
                trace("nGMask: " + nGMask.toString( 2 ) );
                trace("nBMask: " + nBMask.toString( 2 ) );
            }
        }
    }
//}