'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
/**
* 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);
}
}