In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

'Frame by Frame' timer movie for video editing tool

create 'Frame by Frame' timer movie for video editing tool
which DOES NOT support importing runtime animation.

@author Test Dept
Get Adobe Flash player
by Test_Dept 10 Mar 2011
/**
 * Copyright Test_Dept ( http://wonderfl.net/user/Test_Dept )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/tdzh
 */

package {
    
    import flash.display.Graphics;
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.events.TimerEvent;
    import flash.net.FileReference;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    import flash.utils.ByteArray;
    import flash.utils.Timer;
    import flash.utils.getTimer;

    /**
     * create 'Frame by Frame' timer movie for video editing tool
     * which DOES NOT support importing runtime animation.
     *
     * @author Test Dept
     */
    [SWF(backgroundColor='#0000ff', width='465', height='465', frameRate=30)]
    public class CreateTimer extends Sprite {

        private var _info : TextField = null;
        private var _delay : Timer = null;
    
        private var _swfName : String = null;
        private var _swfBytes : ByteArray = null;
        
        public function CreateTimer() {

            Wonderfl.capture_delay(10);

            var g : Graphics = graphics;
            g.clear();
            g.beginFill(0x0000ff);
            g.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
            g.endFill();
            
            addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
        }
        
        private function addedToStageHandler(event : Event) : void {

            _info = new TextField();
            _info.defaultTextFormat = new TextFormat(null, 20, 0xffffff);
            _info.multiline = true;
            _info.autoSize = TextFieldAutoSize.LEFT;
            addChild(_info);
            
            _info.text = 'creating movie ...';
            _info.y = 0;
            
            _delay = new Timer(100, 1);
            _delay.addEventListener(TimerEvent.TIMER_COMPLETE, timerCompleteHandler);
            _delay.start();
        }
        
        private function timerCompleteHandler(event : Event) : void {

            _delay.removeEventListener(TimerEvent.TIMER_COMPLETE, timerCompleteHandler);
            
            var lengthInMinutes : int = 5;
            var frameRate : Number = stage.frameRate;
            var textColor : int = 0xffffff;
            var bgColor : int = 0x0000ff;

            var startTime : int = getTimer();
            
            var swf : SWFDocument = createTimer(
                Dsp7Seg,            
                lengthInMinutes,
                frameRate,
                textColor,
                bgColor);
            
            var endTime : int = getTimer();
            
            _swfBytes = new ByteArray();            
            swf.save(_swfBytes);
            
            var scale : Number = stage.stageWidth /
                (swf.getFrameSize().width / 20);
            
            var loader : Loader = new Loader();
            loader.loadBytes(_swfBytes);
            loader.scaleX = scale;
            loader.scaleY = scale;
            addChild(loader);
            
            var infoText : String =
                'click stage to save a movie.\r\r' +
                'length in minutes: ' + lengthInMinutes + ' min\r' +
                'frame rate: ' + frameRate + ' fps\r' +
                'frame count: ' + swf.getFrameCount() + ' (max: 16000)\r' +
                'file size: ' + _swfBytes.length +
                ' bytes\n (after decompression: ' +
                swf.getFileLength() + ' bytes)\r' +
                'creation time: ' + (endTime - startTime) + ' ms\n';


            _info.text = infoText;
            _info.y = stage.stageHeight - _info.textHeight;
            
            _swfName = 'Timer_' + lengthInMinutes +
                'min_' + frameRate + 'fps.swf';
            
            stage.addEventListener(MouseEvent.CLICK, stage_mouseClickHandler);
        }
    
        private function stage_mouseClickHandler(event : Event) : void {
            var ref : FileReference = new FileReference();
            ref.save(_swfBytes, _swfName);
        }
    }
}

/*---------------------------------------------------------
 * some functions fully compatible with 
 * JavaScript in Java Runtime (aka Rhino).
 */

function createTimer(fontClass, lengthInMinutes, frameRate, textColor, bgColor) {

    var frameCount = Math.ceil(frameRate * 60 * lengthInMinutes);
    
    //---------------------------------------------------------------
    
    function formatNumber(num, numDigits) {
        var s = '';
        num = Math.floor(num);
        while (numDigits-- > 0) {
            s = num % 10 + s;
            num = Math.floor(num / 10);
        }
        return s;
    }

    function formatTime(time) {
        time = Math.floor(time);
        var ms = time % 1000;
        time = Math.floor(time / 1000);
        var s = time % 60;
        time = Math.floor(time / 60);
        var m = time % 60;
        return formatNumber(m, 2) +
            ( (ms < 500)? ':' : '\u0020') +
            formatNumber(s, 2) +
            '.' +
            formatNumber(ms / 10, 2);
    }

    //---------------------------------------------------------------
    
    var swf = new SWFDocument();
    swf.setBackgroundColor(bgColor);
    
    var _shapes = {};
    var _chars = '0123456789:.\u0020';
    for (var i = 0; i < _chars.length; i++) {
        var c = _chars.charAt(i);
        _shapes[c] = fontClass.defineCharShape(swf, c, textColor);
        // use local font
        //_shapes[c] = swf.defineTextShape(c, textColor);
    }
    
    function getCharShape(c) {
        return _shapes[c];
    }
    
    var unitShape = getCharShape('0');
    var unitHeight = unitShape.height;
    var xOffset = -unitShape.x;
    var yOffset = -unitShape.y;
    
    var lastText = '';
    var position = {};
    var maxWidth = 0;
    
    for (var i = 0; i < frameCount; i++) {
        
        var text = formatTime(i / frameRate * 1000);
        var length = Math.max(lastText.length, text.length);
        var x = 0;
        
        for (var d = 0; d < length; d++) {
            
            var depth = d + 1; // 1~
            var replace = false;
            var nothing = false;
            
            if (d < lastText.length) {
                
                if (d < text.length) {
                    if (lastText.charAt(d) == text.charAt(d)
                        && x == position[d].x) {
                        // set nothing flag
                        nothing = true;
                    } else {
                        // set replace flag
                        replace = true;
                    }
                } else {
                    // remove last char
                    swf.removeObject2(depth);
                }
            }
            
            if (d < text.length) {
                // place a char
                var c = text.charAt(d);
                var shape = getCharShape(c);
                
                if (!nothing) {
                    swf.placeObject2(depth, shape.shapeId, replace,
                        new Matrix(1, 0, 0, 1, xOffset + x, yOffset) );
                    position[d] = {x : x};
                }
                
                x += (shape.width);
            }
        }
        
        // show frame
        swf.showFrame();
        
        maxWidth = Math.max(maxWidth, x);
        lastText = text;
    }
    
    swf.setFrameRate(frameRate);
    swf.setFrameCount(frameCount);
    swf.setFrameSize(maxWidth, unitHeight);
    
    swf.end();
    
    return swf;
}    

/**
 * 7Seg Character Data
 * 
 * Segment Layout
 *
 *    a
 *   f b
 *    g
 *   e c
 *    d
 *      .
 */

var Dsp7Seg = {
    
    defineCharShape : function(swf, c, color) {
        
        if (c.match(/^[\.\:\u0020]$/) ) {
            return this.defineSymbolShape(swf, c, color);
        }
        
        var pattern = Dsp7Seg[c];
        
        return swf.defineShape(
            new Rectangle(0, 0, Dsp7Seg._SEG_WIDTH, Dsp7Seg._SEG_HEIGHT),
            function(g) {
                Dsp7Seg.drawSegment(g, pattern, color, -1, -1);
            }
        );
    },
    
    defineSymbolShape : function(swf, c, color) {
        
        var pattern = Dsp7Seg[c];
        
        var width = 200;
        
        return swf.defineShape(
            new Rectangle(0, 0, width, Dsp7Seg._SEG_HEIGHT),
            function(g) {
                if (c == '.') {
                    g.beginFill(color);
                    g.drawCircle(width / 2, 840, 46);
                    g.endFill();
                } else if (c == ':') {
                    g.beginFill(color);
                    g.drawCircle(width / 2, 320, 46);
                    g.endFill();
                    g.beginFill(color);
                    g.drawCircle(width / 2, 700, 46);
                    g.endFill();
                }
            }
        );
    },
    
    '0' : 'abcdef',
    '1' : 'bc',
    '2' : 'abdeg',
    '3' : 'abcdg',
    '4' : 'bcfg',
    '5' : 'acdfg',
    '6' : 'acdefg',
    '7' : 'abc',
    '8' : 'abcdefg',
    '9' : 'abcdfg',
    '-' : 'g',
    '.' : '.',
    
    drawSegment : function(
        g, pattern, hiColor, loColor, bgColor
    ) {
        
        if (bgColor >= 0) {
            g.beginFill(bgColor);
            g.drawRect(0, 0, 
                Dsp7Seg._SEG_WIDTH,
                Dsp7Seg._SEG_HEIGHT);
            g.endFill();
        }
        
        var on;
        
        for (var i = 0; i < Dsp7Seg._ALL_SEGMENT.length; i++) {
            var c = Dsp7Seg._ALL_SEGMENT.charAt(i);
            on = (pattern != null && pattern.indexOf(c) != -1);
            Dsp7Seg._drawSegment(g, c, on? hiColor : loColor);
        }
        
        on = (pattern != null && pattern.indexOf('.') != -1);
        Dsp7Seg._drawPoint(g, on? hiColor : loColor);
    },
    
    _drawSegment : function(g, segment, color) {
        
        if (color < 0) {
            return;
        }
        
        var data = Dsp7Seg._SEGMENT_DATA[segment];
        var numPoints = data.length / 2;
        
        g.beginFill(color);
        
        for (var i = 0; i < numPoints; i++) {
            
            var x = data[i * 2];
            var y = data[i * 2 + 1];
            
            if (i == 0) {
                g.moveTo(x, y);
            } else {
                g.lineTo(x, y);
            }
        }
        
        g.endFill();
    },
    
    _drawPoint : function(g, color) {
        if (color < 0) {
            return;
        }
        g.beginFill(color);
        g.drawCircle(542, 840, 46);
        g.endFill();
    },
    
    _SEG_WIDTH : 636,
    _SEG_HEIGHT : 1000,
    _ALL_SEGMENT : 'abcdefg',
    _SEGMENT_DATA : {
        'a' : [575, 138, 494, 211, 249, 211, 194, 137, 213, 120, 559, 120],
        'b' : [595, 160, 544, 452, 493, 500, 459, 456, 500, 220, 582, 146],
        'c' : [525, 560, 476, 842, 465, 852, 401, 792, 441, 562, 491, 516],
        'd' : [457, 860, 421, 892, 94, 892, 69, 864, 144, 801, 394, 801],
        'e' : [181, 560, 141, 789, 61, 856, 48, 841, 96, 566, 148, 516],
        'f' : [241, 218, 200, 453, 150, 500, 115, 454, 166, 162, 185, 145],
        'g' : [485, 507, 433, 555, 190, 555, 156, 509, 204, 464, 451, 464]
    }
};

/*---------------------------------------------------------
 * SWF minimum
 */

import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.ByteArray;
import flash.utils.IDataOutput;

interface ISWFObject {
    function writeSWF(out : SWFOutputStream) : void;
}

interface ISWFSpriteTags extends ISWFObject {
    
    function placeObject2(
        depth : int,
        characterId : int,
        replace : Boolean = false,
        matrix : Matrix = null,
        colorTransform : ColorTransform = null,
        ratio : int = -1,
        name : String = null,
        clipDepth : int    = 0
    ) : void;
    
    function removeObject2(depth : int) : void;
    
    function showFrame() : void;
    
    function end() : void;
}

class SWFOutputStream  {
    
    private var _out : IDataOutput;
    private var _bitBuffer : int;
    private var _bitLength : int;
    
    public function SWFOutputStream(out : IDataOutput) {
        _out = out;
        _bitBuffer = 0;
        _bitLength = 0;
    }
    
    public function writeByte(b : int) : void {
        _out.writeByte(b);
    }
    
    public function writeBytes(buffer : ByteArray) : void {
        checkBits();
        _out.writeBytes(buffer);
    }
    
    public function writeMultiByte(text : String, encoding : String) : void {
        checkBits();
        _out.writeMultiByte(text, encoding);
    }
    
    public function flush() : void {
        checkBits();
    }
    
    public function close() : void {
        flush();
    }
    
    public function writeUI8(b : int) : void {
        
        if ( (b & 0xff) != b) {
            throw new Error('not UI8:' + b);
        }
        checkBits();
        
        writeByte(b & 0xff);
    }
    
    public function writeUI16(b : int) : void {
        
        if ( (b & 0xffff) != b) {
            throw new Error('not UI16:' + b);
        }
        checkBits();
        
        writeByte(b & 0xff);
        writeByte( (b >>> 8) & 0xff);
    }
    
    public function writeUI32(b : int) : void {
        
        if ( (b & 0xffffffff) != b) {
            throw new Error('not UI32:' + b);
        }
        checkBits();
        
        writeByte(b & 0xff);
        writeByte( (b >>> 8) & 0xff);
        writeByte( (b >>> 16) & 0xff);
        writeByte( (b >>> 24) & 0xff);
    }
    
    public function writeEncodedInt(i : int) : void {
        
        checkBits();
        
        while (true) {
            
            if ( (i & 0x7f) == i) {
                // end.
                writeByte(i);
                break;
            }
            
            writeByte( (i & 0x7f) | 0x80);
            i >>>= 7;
        }
    }
    
    public function writeString(
        s : String, 
        encoding :  String = 'Utf-8'
    ) : void {
        writeMultiByte(s, encoding);
        writeByte(0);
    }
    
    public function writeUBit(b : Boolean) : void {
        writeUBits(b? 1 : 0, 1);
    }
    
    public function writeUBits(b : int, nbits : int) : void {
        if (b < 0) {
            throw new Error('negative bits:' + b);
        }
        writeBitsImpl(b, nbits);        
    }
    
    public function writeSBits(b : int, nbits : int) : void {
        writeBitsImpl(b, nbits);        
    }
    
    private function writeBitsImpl(b : int, nbits : int) : void {
        
        function trimBits(b : int, nbits : int) : int {
            return (b << (32 - nbits) ) >>> (32 - nbits);
        }
        
        b = trimBits(b, nbits);
        
        while (_bitLength + nbits >= 8) {
            var newBits : int = _bitLength + nbits - 8;
            writeByte( (_bitBuffer << (8 - _bitLength) ) |
                (b >>> newBits) );
            b = trimBits(b, newBits);
            nbits = newBits;
            _bitBuffer = 0;
            _bitLength = 0;
        }
        
        _bitBuffer = (_bitBuffer << nbits) | b;
        _bitLength += nbits;
    }
    
    public function flushBits() : void {
        if (_bitLength > 0) {
            writeByte( (int)(_bitBuffer << (8 - _bitLength) ) );
            _bitBuffer = 0;
            _bitLength = 0;
        }
    }
    
    public function writeObject(obj : ISWFObject) : void {
        obj.writeSWF(this);
    }
    
    private function checkBits() : void {
        if (_bitLength > 0) {
            throw new Error(_bitLength + ' > 0');
        }
    }
    
    public function writeRect(rect : Rectangle) : void {
        
        var xMin : int = rect.x;
        var xMax : int = rect.x + rect.width;
        var yMin : int = rect.y;
        var yMax : int = rect.y + rect.height;
        
        var bits : int = SWFBits.getMaxSBits([
            xMin,
            xMax,
            yMin,
            yMax
        ]);
        
        writeUBits(bits, 5);
        writeSBits(xMin, bits);
        writeSBits(xMax, bits);
        writeSBits(yMin, bits);
        writeSBits(yMax, bits);
        flushBits();
    }
    
    public function writeMatrix(matrix : Matrix) : void {
        
        function toFB(value : Number) : int {
            return Math.round(value * (1 << 16) );
        }
        
        var scaleX : Number = matrix.a;
        var scaleY : Number = matrix.d;
        var rotateSkew0 : Number = matrix.b;
        var rotateSkew1 : Number = matrix.c;
        var translateX : int = matrix.tx;
        var translateY : int = matrix.ty;
        
        var hasScale : Boolean = !(scaleX == 1.0 && scaleY == 1.0);
        var hasRotate : Boolean = !(rotateSkew0 == 0 && rotateSkew1 == 0);
        
        writeUBit(hasScale);
        
        if (hasScale) {
            
            var fbScaleX : int = toFB(scaleX);
            var fbScaleY : int = toFB(scaleY);
            var nScaleBits : int = SWFBits.getMaxSBits([
                fbScaleX,
                fbScaleY
            ]);
            
            writeUBits(nScaleBits, 5);
            writeSBits(fbScaleX, nScaleBits);
            writeSBits(fbScaleY, nScaleBits);
        }
        
        writeUBit(hasRotate);
        
        if (hasRotate) {
            
            var fbRotateSkew0 : int = toFB(rotateSkew0);
            var fbRotateSkew1 : int = toFB(rotateSkew1);
            var nRotateBits : int = SWFBits.getMaxSBits([
                fbRotateSkew0,
                fbRotateSkew1
            ] );
            
            writeUBits(nRotateBits, 5);
            writeSBits(fbRotateSkew0, nRotateBits);
            writeSBits(fbRotateSkew1, nRotateBits);
        }
        
        var nTranslateBits : int = SWFBits.getMaxSBits([
            translateX, translateY]);
        
        writeUBits(nTranslateBits, 5);
        writeSBits(translateX, nTranslateBits);
        writeSBits(translateY, nTranslateBits);
        
        flushBits();
    }
    
    public function writeRGBColor(color : int) : void {
        writeUI8( (color >>> 16) & 0xff);
        writeUI8( (color >>> 8) & 0xff);
        writeUI8(color & 0xff);
    }
    
    public function writeRGBAColor(color : int, alpha : Number) : void {
        writeUI8( (color >>> 16) & 0xff);
        writeUI8( (color >>> 8) & 0xff);
        writeUI8(color & 0xff);
        writeUI8(Math.max(0, Math.min( (alpha * 255), 255) ) );
    }

    public function writeRGBAColorTransform(colorTransform : ColorTransform) : void {
        
        var redMultTerm : int = int(colorTransform.redMultiplier * 256);
        var greenMultTerm : int = int(colorTransform.greenMultiplier * 256);
        var blueMultTerm : int = int(colorTransform.blueMultiplier * 256);
        var alphaMultTerm : int = int(colorTransform.alphaMultiplier * 256);
        var redAddTerm : int = int(colorTransform.redOffset);
        var greenAddTerm : int = int(colorTransform.greenOffset);
        var blueAddTerm : int = int(colorTransform.blueOffset);
        var alphaAddTerm : int = int(colorTransform.alphaOffset);
        
        var hasAddTerms : Boolean = !(
            redAddTerm == 0 &&
            greenAddTerm == 0 &&
            blueAddTerm == 0 &&
            alphaAddTerm == 0);
        
        var hasMultTerms : Boolean = !(
            redMultTerm == 256 &&
            greenMultTerm == 256 &&
            blueMultTerm == 256 &&
            alphaMultTerm == 256);
        
        var nBits : int = SWFBits.getMaxSBits([
            redAddTerm,
            greenAddTerm,
            blueAddTerm,
            alphaAddTerm,
            redMultTerm,
            greenMultTerm,
            blueMultTerm,
            alphaMultTerm
        ]);
        
        writeUBit(hasAddTerms);
        writeUBit(hasMultTerms);
        
        writeUBits(nBits, 4);
        
        if (hasMultTerms) {
            writeSBits(redMultTerm, nBits);
            writeSBits(greenMultTerm, nBits);
            writeSBits(blueMultTerm, nBits);
            writeSBits(alphaMultTerm, nBits);
        }
        
        if (hasAddTerms) {
            writeSBits(redAddTerm, nBits);
            writeSBits(greenAddTerm, nBits);
            writeSBits(blueAddTerm, nBits);
            writeSBits(alphaAddTerm, nBits);
        }
        
        flushBits();
    }
}

class SWFBits {
    
    public function SWFBits() {
        throw new Error();    
    }
    
    public static function getMaxUBits(bits : Array) : int {
        if (bits.length == 0) {
            throw new Error('empty bits');
        }
        var maxBits : int= 0;
        for (var i : int = 0; i < bits.length; i++) {
            maxBits = Math.max(maxBits, getUBits(bits[i]) );
        }
        return maxBits;
    }
    
    public static function getMaxSBits(bits : Array) : int {
        if (bits.length == 0) {
            throw new Error('empty bits');
        }
        var maxBits : int = 0;
        for (var i : int = 0; i < bits.length; i++) {
            maxBits = Math.max(maxBits, getSBits(bits[i]) );
        }
        return maxBits;
    }
    
    public static function getUBits(bits : int) : int {
        if (bits < 0) {
            throw new Error('negative bits:' + bits);
        }
        for (var i : int = 31; i >= 0; i--) {
            if ( ( (bits >>> i) & 1) != 0) {
                return i + 1;
            }
        }
        return 1;
    }
    
    public static function getSBits(bits : int) : int {
        var sign : int = (bits >>> 31);
        
        for (var i : int = 31 - 1; i >= 0; i--) {
            if (sign != ( (bits >>> i) & 1) ) {
                return i + 2;
            }
        }
        return 2;
    }
}

class SWFShapeRecord implements ISWFObject {
    
    private var _typeFlag : Boolean;
    
    public function SWFShapeRecord(typeFlag : Boolean) {
        _typeFlag = typeFlag;
    }
    
    public function writeSWF(out : SWFOutputStream) : void {
        out.writeUBit(_typeFlag);
    }
}

class SWFLineStyle implements ISWFObject {
    
    private var _width : int;
    private var _color : int;
    private var _alpha : Number;
    
    public function SWFLineStyle(width : int, color : int, alpha : Number) {
        _width = width;
        _color = color;
        _alpha = alpha;
    }
    
    public function writeSWF(out : SWFOutputStream) : void {
        out.writeUI16(_width);
        out.writeRGBAColor(_color, _alpha);
    }
}

class SWFFillStyle implements ISWFObject {
    
    public function SWFFillStyle() {
    }
    
    public function writeSWF(out : SWFOutputStream) : void {
    }
}

class SWFSolidFill extends SWFFillStyle {
    
    private var _color : int;
    private var _alpha : Number;
    
    public function SWFSolidFill(color : int, alpha : Number) {
        _color = color;
        _alpha = alpha;
    }
    
    override public function writeSWF(out : SWFOutputStream) : void {
        out.writeUI8(0);
        out.writeRGBAColor(_color, _alpha);
    }
}

class SWFStyleChangeRecord extends SWFShapeRecord {
    
    private var _fillStyle0 : int;
    private var _fillStyle1 : int;
    private var _lineStyle : int;
    private var _stateMoveTo : Boolean;
    private var _moveDeltaX : int;
    private var _moveDeltaY : int;
    
    public var numFillBits : int;
    public var numLineBits : int;
    
    public function SWFStyleChangeRecord(
        fillStyle0 : int,
        fillStyle1 : int,
        lineStyle : int,
        stateMoveTo : Boolean,
        moveDeltaX : int,
        moveDeltaY : int
    ) {
        
        super(false);
        
        _fillStyle0 = fillStyle0;
        _fillStyle1 = fillStyle1;
        _lineStyle = lineStyle;
        _stateMoveTo = stateMoveTo;
        _moveDeltaX = moveDeltaX;
        _moveDeltaY = moveDeltaY;
    }
    
    override public function writeSWF(out : SWFOutputStream) : void {
        
        super.writeSWF(out);
        
        var stateNewStyles : Boolean = false;
        var stateLineStyle : Boolean = (_lineStyle >= 0);
        var stateFillStyle1 : Boolean = (_fillStyle1 >= 0);
        var stateFillStyle0 : Boolean = (_fillStyle0 >= 0);
        
        out.writeUBit(stateNewStyles);
        out.writeUBit(stateLineStyle);
        out.writeUBit(stateFillStyle1);
        out.writeUBit(stateFillStyle0);
        out.writeUBit(_stateMoveTo);
        
        if (_stateMoveTo) {
            var moveBits : int = SWFBits.getMaxSBits([
                _moveDeltaX,
                _moveDeltaY
            ]);
            out.writeUBits(moveBits, 5);
            out.writeSBits(_moveDeltaX, moveBits);
            out.writeSBits(_moveDeltaY, moveBits);
        }
        
        if (stateFillStyle0) {
            out.writeUBits(_fillStyle0, numFillBits);
        }
        
        if (stateFillStyle1) {
            out.writeUBits(_fillStyle1, numFillBits);
        }
        
        if (stateLineStyle) {
            out.writeUBits(_lineStyle, numLineBits);
        }
        
        if (stateNewStyles) {
            
        }
    }
}

class SWFStraightEdgeRecord extends SWFShapeRecord {
    
    private var _deltaX : int;
    private var _deltaY : int;
    
    public function SWFStraightEdgeRecord(deltaX : int, deltaY : int) {
        super(true);
        _deltaX = deltaX;
        _deltaY = deltaY;
    }
    
    override public function writeSWF(out : SWFOutputStream) : void {
        
        super.writeSWF(out);
        
        var generalLineFlag : Boolean = (_deltaX != 0 && _deltaY != 0);
        var vertLineFlag : Boolean = (_deltaX == 0);
        
        var numBits : int = SWFBits.getMaxSBits([
            _deltaX,
            _deltaY
        ]);
        
        out.writeUBit(true);
        out.writeUBits(numBits - 2, 4);
        
        out.writeUBit(generalLineFlag);
        
        if (generalLineFlag) {
            
            out.writeSBits(_deltaX, numBits);
            out.writeSBits(_deltaY, numBits);
            
        } else {
            
            out.writeUBit(vertLineFlag);
            
            if (!vertLineFlag) {
                out.writeSBits(_deltaX, numBits);
            } else {
                out.writeSBits(_deltaY, numBits);
            }
        }
    }
}

class SWFCurvedEdgeRecord extends SWFShapeRecord {
    
    private var _controlDeltaX : int;
    private var _controlDeltaY : int;
    private var _anchorDeltaX : int;
    private var _anchorDeltaY : int;
    
    public function SWFCurvedEdgeRecord(
        controlDeltaX : int,
        controlDeltaY : int, 
        anchorDeltaX : int,
        anchorDeltaY : int
    ) {
        
        super(true);
        
        _controlDeltaX = controlDeltaX;
        _controlDeltaY = controlDeltaY;
        _anchorDeltaX = anchorDeltaX;
        _anchorDeltaY = anchorDeltaY;
    }
    
    override public function writeSWF(out : SWFOutputStream) : void {
        
        super.writeSWF(out);
        
        var numBits : int = SWFBits.getMaxSBits([
            _controlDeltaX,
            _controlDeltaY,
            _anchorDeltaX,
            _anchorDeltaY
        ]);
        
        out.writeUBit(false);
        out.writeUBits(numBits - 2, 4);
        
        out.writeSBits(_controlDeltaX, numBits);
        out.writeSBits(_controlDeltaY, numBits);
        out.writeSBits(_anchorDeltaX, numBits);
        out.writeSBits(_anchorDeltaY, numBits);
    }
}

class SWFEndShapeRecord extends SWFShapeRecord {
    
    public function SWFEndShapeRecord() {
        super(false);
    }
    
    override public function writeSWF(out : SWFOutputStream) : void {
        super.writeSWF(out);
        out.writeUBits(0, 5);
    }
}

class SWFShapeWithStyle implements ISWFObject {
    
    private var _fillStyles : Array;
    private var _lineStyles : Array;
    private var _records : Array;
    
    public function SWFShapeWithStyle() {
        _fillStyles = new Array();
        _lineStyles = new Array();
        _records = new Array();
    }
    
    public function solidFill(color : int, alpha : Number) : int {
        return addFillStyle(new SWFSolidFill(color, alpha) );
    }
    
    public function lineStyle(width : int, color : int, alpha : Number) : int {
        return addLineStyle(new SWFLineStyle(width, color, alpha) );
    }
    
    public function styleChangeRecord(
        fillStyle0 : int,
        fillStyle1 : int,
        lineStyle : int,
        stateMoveTo : Boolean,
        moveDeltaX : int,
        moveDeltaY : int
    ) : void {
        _records.push(new SWFStyleChangeRecord(fillStyle0, fillStyle1, lineStyle, stateMoveTo, moveDeltaX, moveDeltaY) );
    }
    
    public function straightEdgeRecord(deltaX : Number, deltaY : Number) : void {
        _records.push(new SWFStraightEdgeRecord(deltaX, deltaY) );
    }
    
    public function curvedEdgeRecord(
        controlDeltaX : int, controlDeltaY : int, 
        anchorDeltaX : int, anchorDeltaY : int
    ) : void {
        _records.push(new SWFCurvedEdgeRecord(controlDeltaX, controlDeltaY, anchorDeltaX, anchorDeltaY) );
    }
    
    public function endShapeRecord() : void {
        _records.push(new SWFEndShapeRecord() );
    }
    
    protected function addFillStyle(fillStyle : SWFFillStyle) : int {
        _fillStyles.push(fillStyle);
        return _fillStyles.length;
    }
    
    protected function addLineStyle(lineStyle : SWFLineStyle) : int {
        _lineStyles.push(lineStyle);
        return _lineStyles.length;
    }
    
    public function writeSWF(out : SWFOutputStream) : void {
        
        var i : int;
        
        writeCount(out, _fillStyles.length);
        for (i = 0; i < _fillStyles.length; i++) {
            out.writeObject(_fillStyles[i]);
        }
        
        writeCount(out, _lineStyles.length);
        for (i = 0; i < _lineStyles.length; i++) {
            out.writeObject(_lineStyles[i]);
        }
        
        var numFillBits : int = SWFBits.getUBits(_fillStyles.length);
        var numLineBits : int = SWFBits.getUBits(_lineStyles.length);
        
        out.writeUBits(numFillBits, 4);
        out.writeUBits(numLineBits, 4);
        out.flushBits(); // nothing.
        
        for (i = 0; i < _records.length; i++) {
            var record : SWFShapeRecord = _records[i];
            if (record is SWFStyleChangeRecord) {
                SWFStyleChangeRecord(record).numFillBits = numFillBits;
                SWFStyleChangeRecord(record).numLineBits = numLineBits;
            }
            out.writeObject(record);
        }
        out.flushBits();
    }
    
    private function writeCount(out : SWFOutputStream, count : int) : void {
        if (count < 255) {
            out.writeUI8(count);
        } else {
            out.writeUI8(0xff);
            out.writeUI16(count);
        }
    }
}

class SWFTag implements ISWFObject {
    
    private var _tagType : int;
    private var _writeRecord : Function;
    
    public function SWFTag(tagType : int, writeRecord : Function = null) {
        _tagType = tagType;
        _writeRecord = writeRecord;
    }
    
    public function writeSWF(out : SWFOutputStream) : void {
        
        var b : ByteArray = new ByteArray();
        var fout : SWFOutputStream = new SWFOutputStream(b);
        
        try {
            if (_writeRecord != null) {
                _writeRecord(fout);
            }
        } finally {
            fout.close();
        }
        
        if (b.length > 62) {
            out.writeUI16( (_tagType << 6) | 0x3f);
            out.writeUI32(b.length);
        } else {
            out.writeUI16( (_tagType << 6) | b.length);
        }
        
        out.writeBytes(b);
    }
}

class SWFTags implements ISWFObject, ISWFSpriteTags {
    
    private static const END : int = 0;
    private static const SHOW_FRAME : int = 1;
    private static const SET_BACKGROUND_COLOR : int = 9;
    private static const PLACE_OBJECT2 : int = 26;
    private static const REMOVE_OBJECT2 : int = 28;
    private static const DEFINE_SHAPE3 : int = 32;
    private static const DEFINE_SPRITE : int = 39;
    
    private var _bout : ByteArray;
    private var _fout : SWFOutputStream;
    private var _closed : Boolean;
    private var _characterId : int;
    
    public function SWFTags() {
        _bout = new ByteArray();
        _fout = new SWFOutputStream(_bout);
        _closed = false;
        _characterId = 0;
    }
    
    public function writeObject(tag : SWFTag) : void {
        if (_closed) {
            throw new Error('stream closed.');
        }
        _fout.writeObject(tag);
    }
    
    public function writeSWF(out : SWFOutputStream) : void {
        if (!_closed) {
            _fout.close();
            _closed = true;
        }
        out.writeBytes(_bout);
    }
    
    private function nextCharacterId() : int {
        _characterId++;
        return _characterId;
    }
    
    public function end() : void {
        writeObject(new SWFTag(END) );
    }
    
    public function showFrame() : void {
        writeObject(new SWFTag(SHOW_FRAME) );
    }
    
    public function setBackgroundColor(color : int) : void {
        writeObject(new SWFTag(SET_BACKGROUND_COLOR,
            function(out : SWFOutputStream) : void {
                out.writeRGBColor(color);
            }            
        ) );
    }
    
    public function defineShape3(
        shapeBounds : Rectangle,
        shapeWithStyle : SWFShapeWithStyle
    ) : int {
        var  characterId : int = nextCharacterId();
        writeObject(new SWFTag(DEFINE_SHAPE3,
            function(out : SWFOutputStream) : void {
                out.writeUI16(characterId); 
                out.writeRect(shapeBounds);
                out.writeObject(shapeWithStyle);
            }            
        ) );
        return characterId;
    }
    
    public function defineSprite(frameCount : int, controlTags : ISWFSpriteTags) : int {
        var  characterId : int = nextCharacterId();
        writeObject(new SWFTag(DEFINE_SPRITE,
            function(out : SWFOutputStream) : void {
                out.writeUI16(characterId); 
                out.writeUI16(frameCount);
                out.writeObject(controlTags);
            }
        ) );
        return characterId;
    }
    
    public function placeObject2(
        depth : int,
        characterId : int,
        replace : Boolean = false,
        matrix : Matrix = null,
        colorTransform : ColorTransform = null,
        ratio : int = -1,
        name : String = null,
        clipDepth : int    = 0
    ) : void {
        writeObject(new SWFTag(PLACE_OBJECT2,
            function(out : SWFOutputStream) : void {
                var placeFlagHasClipActions : Boolean = false;
                var placeFlagHasClipDepth : Boolean = (clipDepth != 0);
                var placeFlagHasName : Boolean = (name != null);
                var placeFlagHasRatio : Boolean = (ratio >= 0);
                var placeFlagHasColorTransform : Boolean = (colorTransform != null);
                var placeFlagHasMatrix : Boolean = (matrix != null);
                var placeFlagHasCharacter : Boolean = (characterId != 0);
                var placeFlagMove : Boolean = replace || !placeFlagHasCharacter;
                
                out.writeUBit(placeFlagHasClipActions);
                out.writeUBit(placeFlagHasClipDepth);
                out.writeUBit(placeFlagHasName);
                out.writeUBit(placeFlagHasRatio);
                out.writeUBit(placeFlagHasColorTransform);
                out.writeUBit(placeFlagHasMatrix);
                out.writeUBit(placeFlagHasCharacter);
                out.writeUBit(placeFlagMove);
                out.flushBits(); // nothing.
                
                out.writeUI16(depth);
                
                if (placeFlagHasCharacter) {
                    out.writeUI16(characterId);
                }
                
                if (placeFlagHasMatrix) {
                    out.writeMatrix(matrix);
                }
                
                if (placeFlagHasColorTransform) {
                    out.writeRGBAColorTransform(colorTransform);
                }
                
                if (placeFlagHasRatio) {
                    out.writeUI16(ratio);
                }
                
                if (placeFlagHasName) {
                    out.writeString(name);
                }
                
                if (placeFlagHasClipDepth) {
                    out.writeUI16(clipDepth);
                }
            }            
        ) );
    }
    
    public function removeObject2(depth : int) : void {
        writeObject(new SWFTag(REMOVE_OBJECT2,
            function(out : SWFOutputStream) : void {
                out.writeUI16(depth);
            }            
        ) );
    }
}

class SWFDocument extends SWFTags {
    
    public static const SIGNATURE_SWF : String = 'FWS';
    public static const SIGNATURE_SWC : String = 'CWS';
    
    private var _signature : String = SIGNATURE_SWC;
    private var _version : int = 9;
    private var _fileLength : int = 0;
    private var _frameSize : Rectangle = new Rectangle(0, 0, 11000, 8000);
    private var _frameRate : Number = 24;
    private var _frameCount : int = 1;

    public function SWFDocument() {
    }

    public function defineShape(shapeBounds : Rectangle, shapeWithStyle : *) : * {

        if (shapeWithStyle is Function) {
            var handler : Function = shapeWithStyle as Function;
            shapeWithStyle = new SWFShapeWithStyle();
            var g : SWFGraphics = new SWFGraphics(shapeWithStyle);
            handler(g);
            shapeWithStyle.endShapeRecord();
            if (shapeBounds == null) {
                shapeBounds = g.getBounds();
            }
        }
        
        var shapeId : int = defineShape3(shapeBounds, shapeWithStyle);
        return {
            shapeId : shapeId,
            x : shapeBounds.x,
            y : shapeBounds.y,
            width : shapeBounds.width,
            height : shapeBounds.height
        };
    }
    
    public function setSignature(signature : String) : void {
        _signature = signature;
    }
    public function getSignature() : String {
        return _signature;
    }

    public function setVersion(version : int) : void {
        _version = version;
    }
    public function getVersion() : int {
        return _version;
    }

    private function setFileLength(fileLength : int) : void {
        _fileLength = fileLength;
    }
    public function getFileLength() : int {
        return _fileLength;
    }

    public function setFrameRate(frameRate : Number) : void {
        _frameRate = frameRate;
    }
    public function getFrameRate() : Number {
        return _frameRate;
    }
    
    public function setFrameCount(frameCount : int) : void {
        _frameCount = frameCount;
    }
    public function getFrameCount() : int {
        return _frameCount;
    }
    
    public function setFrameSize(width : int, height : int) : void {
        _frameSize = new Rectangle(0, 0, width, height);
    }
    public function getFrameSize() : Rectangle {
        return _frameSize;
    }
    
    public function save(out : IDataOutput) : void {
    
        validateSpec();
        
        var contents : ByteArray = getContents();
        
        // fileLength: sizeof(UI8 * 4 + UI32) + contents.length
        setFileLength(8 + contents.length);
        
        var sout : SWFOutputStream = new SWFOutputStream(out);
        
        //----------------
        // writeBytes header
        
        var signature : String = getSignature();
        for (var i : int = 0; i < signature.length; i++) {
            sout.writeUI8(signature.charCodeAt(i) );
        }
        sout.writeUI8(_version);
        sout.writeUI32(_fileLength);
        
        //----------------
        // writeBytes body
        
        if (signature == SIGNATURE_SWC) {
            contents.compress();
        }
        
        sout.writeBytes(contents);
        
        sout.flush();
    }
    
    private function validateSpec() : void {
        var maxFrameCount : int = 16000;
        if (getFrameCount() > maxFrameCount) {
            throw new Error('frameCount over:' +
                getFrameCount() + ' > ' + maxFrameCount);
        }
        if (getFrameSize().x != 0 ||
            getFrameSize().y != 0 ||
            getFrameSize().width < 1 ||
            getFrameSize().height < 1) {
            throw new Error('bad frameSize:' + getFrameSize() );
        }
    }
    
    private function getContents() : ByteArray {
        
        var bout : ByteArray = new ByteArray();
        var fout : SWFOutputStream = new SWFOutputStream(bout);
        
        try {
            
            // stage bounds.
            fout.writeRect(getFrameSize() );
            
            // frame rate (FIXED8).
            var rate : int = int(getFrameRate() * 256);
            fout.writeUI8(rate % 256);
            fout.writeUI8(rate / 256);
            
            // frame count.
            fout.writeUI16(getFrameCount() );
            
            // tags
            fout.writeObject(this);
            
        } finally {
            fout.close();
        }
        return bout;
    }
}

class SWFGraphics {
    
    private var _target : SWFShapeWithStyle;
    private var _bounds : Rectangle;
    
    private var _startX : int;
    private var _startY : int;
    private var _lastX : int;
    private var _lastY : int;
    
    public function SWFGraphics(target : SWFShapeWithStyle) {
        
        _target = target;
        _bounds = new Rectangle();
        
        _startX = 0;
        _startY = 0;
        _lastX = 0;
        _lastY = 0;
    }
    
    public function getBounds() : Rectangle {
        return _bounds;
    }
    
    public function lineStyle(thickness : int = 0, color : int = 0x000000, alpha : Number = 1.0) : void {
        var lineStyle : int = (thickness > 0) ? _target.lineStyle(thickness, color, alpha) : 0;
        _target.styleChangeRecord(-1, -1, lineStyle, false, 0, 0);
    }
    
    public function beginFill(color : int, alpha : Number = 1.0) : void {
        var fillStyle : int = _target.solidFill(color, alpha);
        _target.styleChangeRecord(-1, fillStyle, -1, false, 0, 0);
    }
    
    public function endFill() : void {
        
        if (_lastX != _startX || _lastY != _startY) {
            // force close.
            lineTo(_startX, _startY);
        }
        
        _target.styleChangeRecord(-1, 0, -1, false, 0, 0);
    }
    
    public function moveTo(x : int, y : int) : void {
        
        _target.styleChangeRecord(-1, -1, -1, true, x, y);
        
        _lastX = x;
        _lastY = y;
        
        _startX = x;
        _startY = y;
        
        addToBounds(x, y);
    }
    
    public function lineTo(x : int, y : int) : void {
        
        var dx : int = x - _lastX;
        var dy : int = y - _lastY;
        
        _target.straightEdgeRecord(dx, dy);
        
        _lastX = x;
        _lastY = y;
        
        addToBounds(x, y);
    }
    
    public function curveTo(controlX : int, controlY : int, anchorX : int, anchorY : int) : void {
        
        var cx : int = controlX - _lastX;
        var cy : int = controlY - _lastY;
        var ax : int = anchorX - controlX;
        var ay : int = anchorY - controlY;
        
        _target.curvedEdgeRecord(cx, cy, ax, ay);
        
        _lastX = anchorX;
        _lastY = anchorY;
        
        addToBounds(controlX, controlY);
        addToBounds(anchorX, anchorY);
    }
    
    private function addToBounds(x : int, y : int) : void {
        var bounds : Rectangle = getBounds();
        var xMin : int = bounds.x;
        var xMax : int = bounds.x + bounds.width;
        var yMin : int = bounds.y;
        var yMax : int = bounds.y + bounds.height;
        xMin = Math.min(x, xMin);
        xMax = Math.max(x, xMax);
        yMin = Math.min(y, yMin);
        yMax = Math.max(y, yMax);
        bounds.x = xMin;
        bounds.width = xMax - xMin;
        bounds.y = yMin;
        bounds.height = yMax - yMin;
    }
    
    public function drawRect(x : int, y : int, width : int, height : int) : void {
        moveTo(x, y);
        lineTo(x + width, y);
        lineTo(x + width, y + height);
        lineTo(x, y + height);
        lineTo(x, y);
    }
    
    public function drawCircle(x : int, y : int, r : int) : void {
        drawPie(x, y, r, 0, Math.PI * 2, false);
    }
    
    private function drawPie(x : int, y : int, r : int, t1 : Number, t2 : Number, cont : Boolean) : void {
        
        var ra : Number = Math.PI / 2;
        
        var div : int = Math.max(1, (int)(Math.abs(t1 - t2) / 0.4) );
        
        var lx : Number = 0;
        var ly : Number = 0;
        var lt : Number = 0;
        
        for (var i : int = 0; i <= div; i++) {
            
            var ct : Number = t1 + (t2 - t1) * i / div;
            var cx : Number = Math.cos(ct) * r + x;    
            var cy : Number = Math.sin(ct) * r + y;    
            
            if (i == 0) {
                if (!cont) {
                    moveTo(cx, cy);
                }
            } else {
                var cp : Point = getPieControlPoint(
                    lx, ly, lt + ra, cx, cy, ct + ra); 
                curveTo(cp.x, cp.y, cx, cy);            
            }
            
            lx = cx;
            ly = cy;
            lt = ct;
        }
    }
    
    private function getPieControlPoint(x1 : Number, y1 : Number, t1 : Number, x2 : Number, y2 : Number, t2 : Number) : Point {
        
        var x12 : Number = x2 - x1;
        var y12 : Number = y2 - y1;
        
        var l12 : Number = Math.sqrt(x12 * x12 + y12 * y12);
        var t12 : Number = Math.atan2(y12, x12);
        
        var l13 : Number = l12 * Math.sin(t2 - t12) / Math.sin(t2 - t1);
        
        var x3 : Number = x1 + l13 * Math.cos(t1);
        var y3 : Number = y1 + l13 * Math.sin(t1);
        
        return new Point(x3,  y3);
    }
}