diff --git a/AnimatedSpriteGenerator.as b/AnimatedSpriteGenerator.as new file mode 100755 index 0000000..92590f8 --- /dev/null +++ b/AnimatedSpriteGenerator.as @@ -0,0 +1,92 @@ +package +{ + import flash.display.*; + import flash.geom.*; + + public class AnimatedSpriteGenerator implements IFramesSource + { + private var src:MovieClip; + private var def:GraphicExportDef; + private var sequences:Array = new Array(); // [[AnimatedSpriteFrame]] + private var callback; + public function GetFrames():Array + { + var ret:Array = []; + for (var i in sequences) + { + ret = ret.concat(sequences[i].map(function(frame:AnimatedSpriteFrame, i, arr) { return frame.info; })); + } + return ret; + } + + public function GetData() + { + return {}; + } + + public function AnimatedSpriteGenerator(src:MovieClip, def:GraphicExportDef) + { + this.src = src; + this.def = def; + } + + public function Go(callback) + { + this.callback = callback; + for (var i = 0; i < src.totalFrames; i++) + { + Utils.WeirdGotoFrame(src, i + 1); + Utils.RecursivelyStop(src); + + for (var j = 0; j < src.numChildren; j++) + { + var c = src.getChildAt(j); + if (c is MovieClip) + { + sequences.push(GetAnimation(c, i)); + } + } + } + + callback(true); + } + + private function GetAnimation(m:MovieClip, seq:int):Array + { + var ret:Array = new Array(); + for (var i = 0; i < m.totalFrames; i++) + { + Utils.WeirdGotoFrame(m, i + 1); + Utils.RecursivelyStop(m); + + var bounds:Rectangle = Utils.GetAccurateBounds(m); + + var b:BitmapData = new BitmapData(bounds.width + def.padding * 2, bounds.height + def.padding * 2, true, 0x0); + b.drawWithQuality(m,new Matrix(1, 0, 0, 1, -bounds.left + def.padding, -bounds.top + def.padding), null, null, bounds, false, StageQuality.BEST); + + var info:FrameInfo = new FrameInfo(b); + + var frame:AnimatedSpriteFrame = new AnimatedSpriteFrame(); + frame.info = info; + frame.sequence = seq; + frame.frame = i; + frame.offset = new Point(-bounds.left + def.padding, -bounds.right + def.padding); + + ret.push(frame); + } + + return ret; + } + } +} + +class AnimatedSpriteFrame +{ + import flash.display.*; + import flash.geom.*; + + public var info:FrameInfo; + public var sequence:int = 0; + public var frame:int = 0; + public var offset:Point = new Point(); +} \ No newline at end of file diff --git a/AnimatedSpriteGenerator.as b/AnimatedSpriteGenerator.as new file mode 100755 index 0000000..92590f8 --- /dev/null +++ b/AnimatedSpriteGenerator.as @@ -0,0 +1,92 @@ +package +{ + import flash.display.*; + import flash.geom.*; + + public class AnimatedSpriteGenerator implements IFramesSource + { + private var src:MovieClip; + private var def:GraphicExportDef; + private var sequences:Array = new Array(); // [[AnimatedSpriteFrame]] + private var callback; + public function GetFrames():Array + { + var ret:Array = []; + for (var i in sequences) + { + ret = ret.concat(sequences[i].map(function(frame:AnimatedSpriteFrame, i, arr) { return frame.info; })); + } + return ret; + } + + public function GetData() + { + return {}; + } + + public function AnimatedSpriteGenerator(src:MovieClip, def:GraphicExportDef) + { + this.src = src; + this.def = def; + } + + public function Go(callback) + { + this.callback = callback; + for (var i = 0; i < src.totalFrames; i++) + { + Utils.WeirdGotoFrame(src, i + 1); + Utils.RecursivelyStop(src); + + for (var j = 0; j < src.numChildren; j++) + { + var c = src.getChildAt(j); + if (c is MovieClip) + { + sequences.push(GetAnimation(c, i)); + } + } + } + + callback(true); + } + + private function GetAnimation(m:MovieClip, seq:int):Array + { + var ret:Array = new Array(); + for (var i = 0; i < m.totalFrames; i++) + { + Utils.WeirdGotoFrame(m, i + 1); + Utils.RecursivelyStop(m); + + var bounds:Rectangle = Utils.GetAccurateBounds(m); + + var b:BitmapData = new BitmapData(bounds.width + def.padding * 2, bounds.height + def.padding * 2, true, 0x0); + b.drawWithQuality(m,new Matrix(1, 0, 0, 1, -bounds.left + def.padding, -bounds.top + def.padding), null, null, bounds, false, StageQuality.BEST); + + var info:FrameInfo = new FrameInfo(b); + + var frame:AnimatedSpriteFrame = new AnimatedSpriteFrame(); + frame.info = info; + frame.sequence = seq; + frame.frame = i; + frame.offset = new Point(-bounds.left + def.padding, -bounds.right + def.padding); + + ret.push(frame); + } + + return ret; + } + } +} + +class AnimatedSpriteFrame +{ + import flash.display.*; + import flash.geom.*; + + public var info:FrameInfo; + public var sequence:int = 0; + public var frame:int = 0; + public var offset:Point = new Point(); +} \ No newline at end of file diff --git a/DebugExporter.swf b/DebugExporter.swf index 3a08579..eb90984 100755 --- a/DebugExporter.swf +++ b/DebugExporter.swf Binary files differ diff --git a/AnimatedSpriteGenerator.as b/AnimatedSpriteGenerator.as new file mode 100755 index 0000000..92590f8 --- /dev/null +++ b/AnimatedSpriteGenerator.as @@ -0,0 +1,92 @@ +package +{ + import flash.display.*; + import flash.geom.*; + + public class AnimatedSpriteGenerator implements IFramesSource + { + private var src:MovieClip; + private var def:GraphicExportDef; + private var sequences:Array = new Array(); // [[AnimatedSpriteFrame]] + private var callback; + public function GetFrames():Array + { + var ret:Array = []; + for (var i in sequences) + { + ret = ret.concat(sequences[i].map(function(frame:AnimatedSpriteFrame, i, arr) { return frame.info; })); + } + return ret; + } + + public function GetData() + { + return {}; + } + + public function AnimatedSpriteGenerator(src:MovieClip, def:GraphicExportDef) + { + this.src = src; + this.def = def; + } + + public function Go(callback) + { + this.callback = callback; + for (var i = 0; i < src.totalFrames; i++) + { + Utils.WeirdGotoFrame(src, i + 1); + Utils.RecursivelyStop(src); + + for (var j = 0; j < src.numChildren; j++) + { + var c = src.getChildAt(j); + if (c is MovieClip) + { + sequences.push(GetAnimation(c, i)); + } + } + } + + callback(true); + } + + private function GetAnimation(m:MovieClip, seq:int):Array + { + var ret:Array = new Array(); + for (var i = 0; i < m.totalFrames; i++) + { + Utils.WeirdGotoFrame(m, i + 1); + Utils.RecursivelyStop(m); + + var bounds:Rectangle = Utils.GetAccurateBounds(m); + + var b:BitmapData = new BitmapData(bounds.width + def.padding * 2, bounds.height + def.padding * 2, true, 0x0); + b.drawWithQuality(m,new Matrix(1, 0, 0, 1, -bounds.left + def.padding, -bounds.top + def.padding), null, null, bounds, false, StageQuality.BEST); + + var info:FrameInfo = new FrameInfo(b); + + var frame:AnimatedSpriteFrame = new AnimatedSpriteFrame(); + frame.info = info; + frame.sequence = seq; + frame.frame = i; + frame.offset = new Point(-bounds.left + def.padding, -bounds.right + def.padding); + + ret.push(frame); + } + + return ret; + } + } +} + +class AnimatedSpriteFrame +{ + import flash.display.*; + import flash.geom.*; + + public var info:FrameInfo; + public var sequence:int = 0; + public var frame:int = 0; + public var offset:Point = new Point(); +} \ No newline at end of file diff --git a/DebugExporter.swf b/DebugExporter.swf index 3a08579..eb90984 100755 --- a/DebugExporter.swf +++ b/DebugExporter.swf Binary files differ diff --git a/Exporter.as b/Exporter.as index 98b1198..e754de5 100644 --- a/Exporter.as +++ b/Exporter.as @@ -249,7 +249,6 @@ { if (driver != null) { - Trace(str); driver.writeUTF(str); driver.flush(); } diff --git a/AnimatedSpriteGenerator.as b/AnimatedSpriteGenerator.as new file mode 100755 index 0000000..92590f8 --- /dev/null +++ b/AnimatedSpriteGenerator.as @@ -0,0 +1,92 @@ +package +{ + import flash.display.*; + import flash.geom.*; + + public class AnimatedSpriteGenerator implements IFramesSource + { + private var src:MovieClip; + private var def:GraphicExportDef; + private var sequences:Array = new Array(); // [[AnimatedSpriteFrame]] + private var callback; + public function GetFrames():Array + { + var ret:Array = []; + for (var i in sequences) + { + ret = ret.concat(sequences[i].map(function(frame:AnimatedSpriteFrame, i, arr) { return frame.info; })); + } + return ret; + } + + public function GetData() + { + return {}; + } + + public function AnimatedSpriteGenerator(src:MovieClip, def:GraphicExportDef) + { + this.src = src; + this.def = def; + } + + public function Go(callback) + { + this.callback = callback; + for (var i = 0; i < src.totalFrames; i++) + { + Utils.WeirdGotoFrame(src, i + 1); + Utils.RecursivelyStop(src); + + for (var j = 0; j < src.numChildren; j++) + { + var c = src.getChildAt(j); + if (c is MovieClip) + { + sequences.push(GetAnimation(c, i)); + } + } + } + + callback(true); + } + + private function GetAnimation(m:MovieClip, seq:int):Array + { + var ret:Array = new Array(); + for (var i = 0; i < m.totalFrames; i++) + { + Utils.WeirdGotoFrame(m, i + 1); + Utils.RecursivelyStop(m); + + var bounds:Rectangle = Utils.GetAccurateBounds(m); + + var b:BitmapData = new BitmapData(bounds.width + def.padding * 2, bounds.height + def.padding * 2, true, 0x0); + b.drawWithQuality(m,new Matrix(1, 0, 0, 1, -bounds.left + def.padding, -bounds.top + def.padding), null, null, bounds, false, StageQuality.BEST); + + var info:FrameInfo = new FrameInfo(b); + + var frame:AnimatedSpriteFrame = new AnimatedSpriteFrame(); + frame.info = info; + frame.sequence = seq; + frame.frame = i; + frame.offset = new Point(-bounds.left + def.padding, -bounds.right + def.padding); + + ret.push(frame); + } + + return ret; + } + } +} + +class AnimatedSpriteFrame +{ + import flash.display.*; + import flash.geom.*; + + public var info:FrameInfo; + public var sequence:int = 0; + public var frame:int = 0; + public var offset:Point = new Point(); +} \ No newline at end of file diff --git a/DebugExporter.swf b/DebugExporter.swf index 3a08579..eb90984 100755 --- a/DebugExporter.swf +++ b/DebugExporter.swf Binary files differ diff --git a/Exporter.as b/Exporter.as index 98b1198..e754de5 100644 --- a/Exporter.as +++ b/Exporter.as @@ -249,7 +249,6 @@ { if (driver != null) { - Trace(str); driver.writeUTF(str); driver.flush(); } diff --git a/FrameInfo.as b/FrameInfo.as index 69227a2..9e2c773 100755 --- a/FrameInfo.as +++ b/FrameInfo.as @@ -1,10 +1,15 @@ package { import flash.display.*; + import flash.geom.*; public class FrameInfo { public var frame:BitmapData; public var rect:Rectangle = new Rectangle(); public var sheetIndex:int = 0; + public function FrameInfo(b:BitmapData) + { + frame = b; + } } } \ No newline at end of file diff --git a/AnimatedSpriteGenerator.as b/AnimatedSpriteGenerator.as new file mode 100755 index 0000000..92590f8 --- /dev/null +++ b/AnimatedSpriteGenerator.as @@ -0,0 +1,92 @@ +package +{ + import flash.display.*; + import flash.geom.*; + + public class AnimatedSpriteGenerator implements IFramesSource + { + private var src:MovieClip; + private var def:GraphicExportDef; + private var sequences:Array = new Array(); // [[AnimatedSpriteFrame]] + private var callback; + public function GetFrames():Array + { + var ret:Array = []; + for (var i in sequences) + { + ret = ret.concat(sequences[i].map(function(frame:AnimatedSpriteFrame, i, arr) { return frame.info; })); + } + return ret; + } + + public function GetData() + { + return {}; + } + + public function AnimatedSpriteGenerator(src:MovieClip, def:GraphicExportDef) + { + this.src = src; + this.def = def; + } + + public function Go(callback) + { + this.callback = callback; + for (var i = 0; i < src.totalFrames; i++) + { + Utils.WeirdGotoFrame(src, i + 1); + Utils.RecursivelyStop(src); + + for (var j = 0; j < src.numChildren; j++) + { + var c = src.getChildAt(j); + if (c is MovieClip) + { + sequences.push(GetAnimation(c, i)); + } + } + } + + callback(true); + } + + private function GetAnimation(m:MovieClip, seq:int):Array + { + var ret:Array = new Array(); + for (var i = 0; i < m.totalFrames; i++) + { + Utils.WeirdGotoFrame(m, i + 1); + Utils.RecursivelyStop(m); + + var bounds:Rectangle = Utils.GetAccurateBounds(m); + + var b:BitmapData = new BitmapData(bounds.width + def.padding * 2, bounds.height + def.padding * 2, true, 0x0); + b.drawWithQuality(m,new Matrix(1, 0, 0, 1, -bounds.left + def.padding, -bounds.top + def.padding), null, null, bounds, false, StageQuality.BEST); + + var info:FrameInfo = new FrameInfo(b); + + var frame:AnimatedSpriteFrame = new AnimatedSpriteFrame(); + frame.info = info; + frame.sequence = seq; + frame.frame = i; + frame.offset = new Point(-bounds.left + def.padding, -bounds.right + def.padding); + + ret.push(frame); + } + + return ret; + } + } +} + +class AnimatedSpriteFrame +{ + import flash.display.*; + import flash.geom.*; + + public var info:FrameInfo; + public var sequence:int = 0; + public var frame:int = 0; + public var offset:Point = new Point(); +} \ No newline at end of file diff --git a/DebugExporter.swf b/DebugExporter.swf index 3a08579..eb90984 100755 --- a/DebugExporter.swf +++ b/DebugExporter.swf Binary files differ diff --git a/Exporter.as b/Exporter.as index 98b1198..e754de5 100644 --- a/Exporter.as +++ b/Exporter.as @@ -249,7 +249,6 @@ { if (driver != null) { - Trace(str); driver.writeUTF(str); driver.flush(); } diff --git a/FrameInfo.as b/FrameInfo.as index 69227a2..9e2c773 100755 --- a/FrameInfo.as +++ b/FrameInfo.as @@ -1,10 +1,15 @@ package { import flash.display.*; + import flash.geom.*; public class FrameInfo { public var frame:BitmapData; public var rect:Rectangle = new Rectangle(); public var sheetIndex:int = 0; + public function FrameInfo(b:BitmapData) + { + frame = b; + } } } \ No newline at end of file diff --git a/FramePacker.as b/FramePacker.as index 8f6f97b..0e5e8a9 100755 --- a/FramePacker.as +++ b/FramePacker.as @@ -4,20 +4,25 @@ import flash.display.*; public class FramePacker { + public const MaxSize:int = 2048; private var sheets:Array; // [RectanglePacker] private var frames:Array; // [FrameInfo] - public void FramePacker(frames:Array) + + public function GetSheets():Array { - this.frames = frames.filter(function(i:FrameInfo) { return i.frame != null; }); + return sheets; } -import Packer.RectanglePacker; -import Packer.RectangleNode; + + public function FramePacker(frames:Array) + { + this.frames = frames.filter(function(i:FrameInfo, index, arr) { return i.frame != null; }, this); + } private function GetMaxSize(frames:Array):Point { if (frames.length == 0) return new Point(); - return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo) { return i.frame.width; })), - Math.max.apply(Math, frames.map(function(i:FrameInfo) { return i.frame.width; }))); + return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frame.width; }, this)), + Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frame.height; }, this))); } private function GetArea(frames:Array):int @@ -25,11 +30,11 @@ var area = 0; for (var i in frames) { - if (frames[i] instanceof FrameInfo) + if (frames[i] is FrameInfo) { area += frames[i].frame.width * frames[i].frame.height; } - if (frames[i] instanceof BitmapData || frames[i] instanceof Rectangle) + if (frames[i] is BitmapData || frames[i] is Rectangle) { area += frames[i].width * frames[i].height; } @@ -39,8 +44,10 @@ public const AreaUsedMultiplier = 1.3; // guess how much more sheet space we use than actual space taken up by frames - public function Pack():Boolean + private var callback; + public function Pack(callback) { + this.callback = callback; sheets = new Array(); var index = 0; while (index < frames.length) @@ -50,35 +57,29 @@ var max:Point = GetMaxSize(now); var area:int = GetArea(now); - var exponent = Math.floor(Math.log(area * AreaUsedMultiplier) / Math.log(2)); - var sizeX:int = Math.pow(2, exponent); - var sizeY:int = startSizeX; + var exponent = Math.floor(Math.log(Math.sqrt(area * AreaUsedMultiplier)) / Math.log(2)); + var sizeX:int = Math.min(MaxSize, Math.pow(2, exponent)); + var sizeY:int = sizeX; - var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY); + var i; var startIndex = index; - while (index < frames.length) + var nodes:Array = new Array(); + while (startIndex == index) { - var done = false; - var nodes:Array = new Array(); - var sorted:Array = now.sortOn(now, Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:FrameInfo, b:FrameInfo) { return (a.frame.width * a.frame.height) - (b.frame.width - b.frame.height); }); + nodes.length = 0; + var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY); + var sorted:Array = now.sortOn("frame", Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:BitmapData, b:BitmapData) { return (a.width * a.height) - (b.width - b.height); }); + for (var sortedI = 0; sortedI < now.length; sortedI++) { - var i = nodes[sorted[sortedI]]; + i = sorted[sortedI]; var node:RectangleNode = sheet.Insert(now[i].frame.width, now[i].frame.height); if (node == null) { - if (sortedI == 0) - { - Exporter.Instance.Print("Failure: item too large to pack on sheet"); - return false; - } break; } - else - { - nodes.push(node); - } + nodes.push(node); } if (nodes.length == now.length) { @@ -95,30 +96,28 @@ { sizeY *= 2; } - if (sizeX > 2048 || sizeY > 2048) + if (sizeX > MaxSize || sizeY > MaxSize) { - index += now.length; + if (nodes.length == 0) + { + Exporter.Instance.Print("Failure: item too large to pack on sheet"); + callback(false); + return; + } + index += nodes.length; } } } - if (index > startIndex) + for (i = startIndex; i < index; i++) { - for (var i = startIndex; i < index; i++) - { - frames[i].sheetIndex = sheets.length; - frames[i].rect = nodes[i - startIndex].Rect; - } - sheets.push(sheet); - } + frames[i].sheetIndex = sheets.length; + frames[i].rect = nodes[i - startIndex].Rect; + } + sheets.push(sheet); } - return output; - } - - private function PackFrames(toPack:Array, sizeX:int, sizeY:int):Array - { - + callback(true); } } } \ No newline at end of file diff --git a/AnimatedSpriteGenerator.as b/AnimatedSpriteGenerator.as new file mode 100755 index 0000000..92590f8 --- /dev/null +++ b/AnimatedSpriteGenerator.as @@ -0,0 +1,92 @@ +package +{ + import flash.display.*; + import flash.geom.*; + + public class AnimatedSpriteGenerator implements IFramesSource + { + private var src:MovieClip; + private var def:GraphicExportDef; + private var sequences:Array = new Array(); // [[AnimatedSpriteFrame]] + private var callback; + public function GetFrames():Array + { + var ret:Array = []; + for (var i in sequences) + { + ret = ret.concat(sequences[i].map(function(frame:AnimatedSpriteFrame, i, arr) { return frame.info; })); + } + return ret; + } + + public function GetData() + { + return {}; + } + + public function AnimatedSpriteGenerator(src:MovieClip, def:GraphicExportDef) + { + this.src = src; + this.def = def; + } + + public function Go(callback) + { + this.callback = callback; + for (var i = 0; i < src.totalFrames; i++) + { + Utils.WeirdGotoFrame(src, i + 1); + Utils.RecursivelyStop(src); + + for (var j = 0; j < src.numChildren; j++) + { + var c = src.getChildAt(j); + if (c is MovieClip) + { + sequences.push(GetAnimation(c, i)); + } + } + } + + callback(true); + } + + private function GetAnimation(m:MovieClip, seq:int):Array + { + var ret:Array = new Array(); + for (var i = 0; i < m.totalFrames; i++) + { + Utils.WeirdGotoFrame(m, i + 1); + Utils.RecursivelyStop(m); + + var bounds:Rectangle = Utils.GetAccurateBounds(m); + + var b:BitmapData = new BitmapData(bounds.width + def.padding * 2, bounds.height + def.padding * 2, true, 0x0); + b.drawWithQuality(m,new Matrix(1, 0, 0, 1, -bounds.left + def.padding, -bounds.top + def.padding), null, null, bounds, false, StageQuality.BEST); + + var info:FrameInfo = new FrameInfo(b); + + var frame:AnimatedSpriteFrame = new AnimatedSpriteFrame(); + frame.info = info; + frame.sequence = seq; + frame.frame = i; + frame.offset = new Point(-bounds.left + def.padding, -bounds.right + def.padding); + + ret.push(frame); + } + + return ret; + } + } +} + +class AnimatedSpriteFrame +{ + import flash.display.*; + import flash.geom.*; + + public var info:FrameInfo; + public var sequence:int = 0; + public var frame:int = 0; + public var offset:Point = new Point(); +} \ No newline at end of file diff --git a/DebugExporter.swf b/DebugExporter.swf index 3a08579..eb90984 100755 --- a/DebugExporter.swf +++ b/DebugExporter.swf Binary files differ diff --git a/Exporter.as b/Exporter.as index 98b1198..e754de5 100644 --- a/Exporter.as +++ b/Exporter.as @@ -249,7 +249,6 @@ { if (driver != null) { - Trace(str); driver.writeUTF(str); driver.flush(); } diff --git a/FrameInfo.as b/FrameInfo.as index 69227a2..9e2c773 100755 --- a/FrameInfo.as +++ b/FrameInfo.as @@ -1,10 +1,15 @@ package { import flash.display.*; + import flash.geom.*; public class FrameInfo { public var frame:BitmapData; public var rect:Rectangle = new Rectangle(); public var sheetIndex:int = 0; + public function FrameInfo(b:BitmapData) + { + frame = b; + } } } \ No newline at end of file diff --git a/FramePacker.as b/FramePacker.as index 8f6f97b..0e5e8a9 100755 --- a/FramePacker.as +++ b/FramePacker.as @@ -4,20 +4,25 @@ import flash.display.*; public class FramePacker { + public const MaxSize:int = 2048; private var sheets:Array; // [RectanglePacker] private var frames:Array; // [FrameInfo] - public void FramePacker(frames:Array) + + public function GetSheets():Array { - this.frames = frames.filter(function(i:FrameInfo) { return i.frame != null; }); + return sheets; } -import Packer.RectanglePacker; -import Packer.RectangleNode; + + public function FramePacker(frames:Array) + { + this.frames = frames.filter(function(i:FrameInfo, index, arr) { return i.frame != null; }, this); + } private function GetMaxSize(frames:Array):Point { if (frames.length == 0) return new Point(); - return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo) { return i.frame.width; })), - Math.max.apply(Math, frames.map(function(i:FrameInfo) { return i.frame.width; }))); + return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frame.width; }, this)), + Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frame.height; }, this))); } private function GetArea(frames:Array):int @@ -25,11 +30,11 @@ var area = 0; for (var i in frames) { - if (frames[i] instanceof FrameInfo) + if (frames[i] is FrameInfo) { area += frames[i].frame.width * frames[i].frame.height; } - if (frames[i] instanceof BitmapData || frames[i] instanceof Rectangle) + if (frames[i] is BitmapData || frames[i] is Rectangle) { area += frames[i].width * frames[i].height; } @@ -39,8 +44,10 @@ public const AreaUsedMultiplier = 1.3; // guess how much more sheet space we use than actual space taken up by frames - public function Pack():Boolean + private var callback; + public function Pack(callback) { + this.callback = callback; sheets = new Array(); var index = 0; while (index < frames.length) @@ -50,35 +57,29 @@ var max:Point = GetMaxSize(now); var area:int = GetArea(now); - var exponent = Math.floor(Math.log(area * AreaUsedMultiplier) / Math.log(2)); - var sizeX:int = Math.pow(2, exponent); - var sizeY:int = startSizeX; + var exponent = Math.floor(Math.log(Math.sqrt(area * AreaUsedMultiplier)) / Math.log(2)); + var sizeX:int = Math.min(MaxSize, Math.pow(2, exponent)); + var sizeY:int = sizeX; - var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY); + var i; var startIndex = index; - while (index < frames.length) + var nodes:Array = new Array(); + while (startIndex == index) { - var done = false; - var nodes:Array = new Array(); - var sorted:Array = now.sortOn(now, Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:FrameInfo, b:FrameInfo) { return (a.frame.width * a.frame.height) - (b.frame.width - b.frame.height); }); + nodes.length = 0; + var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY); + var sorted:Array = now.sortOn("frame", Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:BitmapData, b:BitmapData) { return (a.width * a.height) - (b.width - b.height); }); + for (var sortedI = 0; sortedI < now.length; sortedI++) { - var i = nodes[sorted[sortedI]]; + i = sorted[sortedI]; var node:RectangleNode = sheet.Insert(now[i].frame.width, now[i].frame.height); if (node == null) { - if (sortedI == 0) - { - Exporter.Instance.Print("Failure: item too large to pack on sheet"); - return false; - } break; } - else - { - nodes.push(node); - } + nodes.push(node); } if (nodes.length == now.length) { @@ -95,30 +96,28 @@ { sizeY *= 2; } - if (sizeX > 2048 || sizeY > 2048) + if (sizeX > MaxSize || sizeY > MaxSize) { - index += now.length; + if (nodes.length == 0) + { + Exporter.Instance.Print("Failure: item too large to pack on sheet"); + callback(false); + return; + } + index += nodes.length; } } } - if (index > startIndex) + for (i = startIndex; i < index; i++) { - for (var i = startIndex; i < index; i++) - { - frames[i].sheetIndex = sheets.length; - frames[i].rect = nodes[i - startIndex].Rect; - } - sheets.push(sheet); - } + frames[i].sheetIndex = sheets.length; + frames[i].rect = nodes[i - startIndex].Rect; + } + sheets.push(sheet); } - return output; - } - - private function PackFrames(toPack:Array, sizeX:int, sizeY:int):Array - { - + callback(true); } } } \ No newline at end of file diff --git a/GraphicExportDef.as b/GraphicExportDef.as index fb60e89..12ca505 100755 --- a/GraphicExportDef.as +++ b/GraphicExportDef.as @@ -9,6 +9,9 @@ public class GraphicExportDef { + public const TYPE_SPRITES = 1; + public const TYPE_SKELETAL = 2; + private var _exportType:int = 1; public function get exportType():int { return _exportType; } @@ -18,6 +21,9 @@ private var _valid:Boolean = false; public function get valid():Boolean { return _valid; } + private var _padding:int = 0; + public function get padding():int { return _padding; } + private var input:ByteArray; private var inputLoader:Loader = null; @@ -60,6 +66,8 @@ inputLoader.loadBytes(input, context); } + private var frames:IFramesSource = null; + private function LoaderComplete(e) { try @@ -79,12 +87,52 @@ Utils.RecursivelyStop(clip); - Done(); + frames = null; + + switch (exportType) + { + case TYPE_SPRITES: + frames = new AnimatedSpriteGenerator(clip, this); + break; + } + + if (frames == null) + { + Fail("invalid export type: " + exportType); + return; + } + + frames.Go(FramesDone); + } catch (e) { Exporter.Instance.Print(e.getStackTrace()); } } + + private var packer:FramePacker; + private function FramesDone(success) + { + if (!success) + { + Fail("couldn't get source frames"); + return; + } + packer = new FramePacker(frames.GetFrames()); + packer.Pack(PackDone); + } + + private function PackDone(success) + { + if (!success) + { + Fail("couldn't pack frames"); + return; + } + + Done(); + } + private function LoaderError(e) { Fail("couldn't load input: " + e.toString()); diff --git a/AnimatedSpriteGenerator.as b/AnimatedSpriteGenerator.as new file mode 100755 index 0000000..92590f8 --- /dev/null +++ b/AnimatedSpriteGenerator.as @@ -0,0 +1,92 @@ +package +{ + import flash.display.*; + import flash.geom.*; + + public class AnimatedSpriteGenerator implements IFramesSource + { + private var src:MovieClip; + private var def:GraphicExportDef; + private var sequences:Array = new Array(); // [[AnimatedSpriteFrame]] + private var callback; + public function GetFrames():Array + { + var ret:Array = []; + for (var i in sequences) + { + ret = ret.concat(sequences[i].map(function(frame:AnimatedSpriteFrame, i, arr) { return frame.info; })); + } + return ret; + } + + public function GetData() + { + return {}; + } + + public function AnimatedSpriteGenerator(src:MovieClip, def:GraphicExportDef) + { + this.src = src; + this.def = def; + } + + public function Go(callback) + { + this.callback = callback; + for (var i = 0; i < src.totalFrames; i++) + { + Utils.WeirdGotoFrame(src, i + 1); + Utils.RecursivelyStop(src); + + for (var j = 0; j < src.numChildren; j++) + { + var c = src.getChildAt(j); + if (c is MovieClip) + { + sequences.push(GetAnimation(c, i)); + } + } + } + + callback(true); + } + + private function GetAnimation(m:MovieClip, seq:int):Array + { + var ret:Array = new Array(); + for (var i = 0; i < m.totalFrames; i++) + { + Utils.WeirdGotoFrame(m, i + 1); + Utils.RecursivelyStop(m); + + var bounds:Rectangle = Utils.GetAccurateBounds(m); + + var b:BitmapData = new BitmapData(bounds.width + def.padding * 2, bounds.height + def.padding * 2, true, 0x0); + b.drawWithQuality(m,new Matrix(1, 0, 0, 1, -bounds.left + def.padding, -bounds.top + def.padding), null, null, bounds, false, StageQuality.BEST); + + var info:FrameInfo = new FrameInfo(b); + + var frame:AnimatedSpriteFrame = new AnimatedSpriteFrame(); + frame.info = info; + frame.sequence = seq; + frame.frame = i; + frame.offset = new Point(-bounds.left + def.padding, -bounds.right + def.padding); + + ret.push(frame); + } + + return ret; + } + } +} + +class AnimatedSpriteFrame +{ + import flash.display.*; + import flash.geom.*; + + public var info:FrameInfo; + public var sequence:int = 0; + public var frame:int = 0; + public var offset:Point = new Point(); +} \ No newline at end of file diff --git a/DebugExporter.swf b/DebugExporter.swf index 3a08579..eb90984 100755 --- a/DebugExporter.swf +++ b/DebugExporter.swf Binary files differ diff --git a/Exporter.as b/Exporter.as index 98b1198..e754de5 100644 --- a/Exporter.as +++ b/Exporter.as @@ -249,7 +249,6 @@ { if (driver != null) { - Trace(str); driver.writeUTF(str); driver.flush(); } diff --git a/FrameInfo.as b/FrameInfo.as index 69227a2..9e2c773 100755 --- a/FrameInfo.as +++ b/FrameInfo.as @@ -1,10 +1,15 @@ package { import flash.display.*; + import flash.geom.*; public class FrameInfo { public var frame:BitmapData; public var rect:Rectangle = new Rectangle(); public var sheetIndex:int = 0; + public function FrameInfo(b:BitmapData) + { + frame = b; + } } } \ No newline at end of file diff --git a/FramePacker.as b/FramePacker.as index 8f6f97b..0e5e8a9 100755 --- a/FramePacker.as +++ b/FramePacker.as @@ -4,20 +4,25 @@ import flash.display.*; public class FramePacker { + public const MaxSize:int = 2048; private var sheets:Array; // [RectanglePacker] private var frames:Array; // [FrameInfo] - public void FramePacker(frames:Array) + + public function GetSheets():Array { - this.frames = frames.filter(function(i:FrameInfo) { return i.frame != null; }); + return sheets; } -import Packer.RectanglePacker; -import Packer.RectangleNode; + + public function FramePacker(frames:Array) + { + this.frames = frames.filter(function(i:FrameInfo, index, arr) { return i.frame != null; }, this); + } private function GetMaxSize(frames:Array):Point { if (frames.length == 0) return new Point(); - return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo) { return i.frame.width; })), - Math.max.apply(Math, frames.map(function(i:FrameInfo) { return i.frame.width; }))); + return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frame.width; }, this)), + Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frame.height; }, this))); } private function GetArea(frames:Array):int @@ -25,11 +30,11 @@ var area = 0; for (var i in frames) { - if (frames[i] instanceof FrameInfo) + if (frames[i] is FrameInfo) { area += frames[i].frame.width * frames[i].frame.height; } - if (frames[i] instanceof BitmapData || frames[i] instanceof Rectangle) + if (frames[i] is BitmapData || frames[i] is Rectangle) { area += frames[i].width * frames[i].height; } @@ -39,8 +44,10 @@ public const AreaUsedMultiplier = 1.3; // guess how much more sheet space we use than actual space taken up by frames - public function Pack():Boolean + private var callback; + public function Pack(callback) { + this.callback = callback; sheets = new Array(); var index = 0; while (index < frames.length) @@ -50,35 +57,29 @@ var max:Point = GetMaxSize(now); var area:int = GetArea(now); - var exponent = Math.floor(Math.log(area * AreaUsedMultiplier) / Math.log(2)); - var sizeX:int = Math.pow(2, exponent); - var sizeY:int = startSizeX; + var exponent = Math.floor(Math.log(Math.sqrt(area * AreaUsedMultiplier)) / Math.log(2)); + var sizeX:int = Math.min(MaxSize, Math.pow(2, exponent)); + var sizeY:int = sizeX; - var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY); + var i; var startIndex = index; - while (index < frames.length) + var nodes:Array = new Array(); + while (startIndex == index) { - var done = false; - var nodes:Array = new Array(); - var sorted:Array = now.sortOn(now, Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:FrameInfo, b:FrameInfo) { return (a.frame.width * a.frame.height) - (b.frame.width - b.frame.height); }); + nodes.length = 0; + var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY); + var sorted:Array = now.sortOn("frame", Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:BitmapData, b:BitmapData) { return (a.width * a.height) - (b.width - b.height); }); + for (var sortedI = 0; sortedI < now.length; sortedI++) { - var i = nodes[sorted[sortedI]]; + i = sorted[sortedI]; var node:RectangleNode = sheet.Insert(now[i].frame.width, now[i].frame.height); if (node == null) { - if (sortedI == 0) - { - Exporter.Instance.Print("Failure: item too large to pack on sheet"); - return false; - } break; } - else - { - nodes.push(node); - } + nodes.push(node); } if (nodes.length == now.length) { @@ -95,30 +96,28 @@ { sizeY *= 2; } - if (sizeX > 2048 || sizeY > 2048) + if (sizeX > MaxSize || sizeY > MaxSize) { - index += now.length; + if (nodes.length == 0) + { + Exporter.Instance.Print("Failure: item too large to pack on sheet"); + callback(false); + return; + } + index += nodes.length; } } } - if (index > startIndex) + for (i = startIndex; i < index; i++) { - for (var i = startIndex; i < index; i++) - { - frames[i].sheetIndex = sheets.length; - frames[i].rect = nodes[i - startIndex].Rect; - } - sheets.push(sheet); - } + frames[i].sheetIndex = sheets.length; + frames[i].rect = nodes[i - startIndex].Rect; + } + sheets.push(sheet); } - return output; - } - - private function PackFrames(toPack:Array, sizeX:int, sizeY:int):Array - { - + callback(true); } } } \ No newline at end of file diff --git a/GraphicExportDef.as b/GraphicExportDef.as index fb60e89..12ca505 100755 --- a/GraphicExportDef.as +++ b/GraphicExportDef.as @@ -9,6 +9,9 @@ public class GraphicExportDef { + public const TYPE_SPRITES = 1; + public const TYPE_SKELETAL = 2; + private var _exportType:int = 1; public function get exportType():int { return _exportType; } @@ -18,6 +21,9 @@ private var _valid:Boolean = false; public function get valid():Boolean { return _valid; } + private var _padding:int = 0; + public function get padding():int { return _padding; } + private var input:ByteArray; private var inputLoader:Loader = null; @@ -60,6 +66,8 @@ inputLoader.loadBytes(input, context); } + private var frames:IFramesSource = null; + private function LoaderComplete(e) { try @@ -79,12 +87,52 @@ Utils.RecursivelyStop(clip); - Done(); + frames = null; + + switch (exportType) + { + case TYPE_SPRITES: + frames = new AnimatedSpriteGenerator(clip, this); + break; + } + + if (frames == null) + { + Fail("invalid export type: " + exportType); + return; + } + + frames.Go(FramesDone); + } catch (e) { Exporter.Instance.Print(e.getStackTrace()); } } + + private var packer:FramePacker; + private function FramesDone(success) + { + if (!success) + { + Fail("couldn't get source frames"); + return; + } + packer = new FramePacker(frames.GetFrames()); + packer.Pack(PackDone); + } + + private function PackDone(success) + { + if (!success) + { + Fail("couldn't pack frames"); + return; + } + + Done(); + } + private function LoaderError(e) { Fail("couldn't load input: " + e.toString()); diff --git a/IFramesSource.as b/IFramesSource.as new file mode 100755 index 0000000..a55f231 --- /dev/null +++ b/IFramesSource.as @@ -0,0 +1,9 @@ +package +{ + public interface IFramesSource + { + function Go(callback); + function GetFrames():Array; + function GetData(); + } +} \ No newline at end of file diff --git a/AnimatedSpriteGenerator.as b/AnimatedSpriteGenerator.as new file mode 100755 index 0000000..92590f8 --- /dev/null +++ b/AnimatedSpriteGenerator.as @@ -0,0 +1,92 @@ +package +{ + import flash.display.*; + import flash.geom.*; + + public class AnimatedSpriteGenerator implements IFramesSource + { + private var src:MovieClip; + private var def:GraphicExportDef; + private var sequences:Array = new Array(); // [[AnimatedSpriteFrame]] + private var callback; + public function GetFrames():Array + { + var ret:Array = []; + for (var i in sequences) + { + ret = ret.concat(sequences[i].map(function(frame:AnimatedSpriteFrame, i, arr) { return frame.info; })); + } + return ret; + } + + public function GetData() + { + return {}; + } + + public function AnimatedSpriteGenerator(src:MovieClip, def:GraphicExportDef) + { + this.src = src; + this.def = def; + } + + public function Go(callback) + { + this.callback = callback; + for (var i = 0; i < src.totalFrames; i++) + { + Utils.WeirdGotoFrame(src, i + 1); + Utils.RecursivelyStop(src); + + for (var j = 0; j < src.numChildren; j++) + { + var c = src.getChildAt(j); + if (c is MovieClip) + { + sequences.push(GetAnimation(c, i)); + } + } + } + + callback(true); + } + + private function GetAnimation(m:MovieClip, seq:int):Array + { + var ret:Array = new Array(); + for (var i = 0; i < m.totalFrames; i++) + { + Utils.WeirdGotoFrame(m, i + 1); + Utils.RecursivelyStop(m); + + var bounds:Rectangle = Utils.GetAccurateBounds(m); + + var b:BitmapData = new BitmapData(bounds.width + def.padding * 2, bounds.height + def.padding * 2, true, 0x0); + b.drawWithQuality(m,new Matrix(1, 0, 0, 1, -bounds.left + def.padding, -bounds.top + def.padding), null, null, bounds, false, StageQuality.BEST); + + var info:FrameInfo = new FrameInfo(b); + + var frame:AnimatedSpriteFrame = new AnimatedSpriteFrame(); + frame.info = info; + frame.sequence = seq; + frame.frame = i; + frame.offset = new Point(-bounds.left + def.padding, -bounds.right + def.padding); + + ret.push(frame); + } + + return ret; + } + } +} + +class AnimatedSpriteFrame +{ + import flash.display.*; + import flash.geom.*; + + public var info:FrameInfo; + public var sequence:int = 0; + public var frame:int = 0; + public var offset:Point = new Point(); +} \ No newline at end of file diff --git a/DebugExporter.swf b/DebugExporter.swf index 3a08579..eb90984 100755 --- a/DebugExporter.swf +++ b/DebugExporter.swf Binary files differ diff --git a/Exporter.as b/Exporter.as index 98b1198..e754de5 100644 --- a/Exporter.as +++ b/Exporter.as @@ -249,7 +249,6 @@ { if (driver != null) { - Trace(str); driver.writeUTF(str); driver.flush(); } diff --git a/FrameInfo.as b/FrameInfo.as index 69227a2..9e2c773 100755 --- a/FrameInfo.as +++ b/FrameInfo.as @@ -1,10 +1,15 @@ package { import flash.display.*; + import flash.geom.*; public class FrameInfo { public var frame:BitmapData; public var rect:Rectangle = new Rectangle(); public var sheetIndex:int = 0; + public function FrameInfo(b:BitmapData) + { + frame = b; + } } } \ No newline at end of file diff --git a/FramePacker.as b/FramePacker.as index 8f6f97b..0e5e8a9 100755 --- a/FramePacker.as +++ b/FramePacker.as @@ -4,20 +4,25 @@ import flash.display.*; public class FramePacker { + public const MaxSize:int = 2048; private var sheets:Array; // [RectanglePacker] private var frames:Array; // [FrameInfo] - public void FramePacker(frames:Array) + + public function GetSheets():Array { - this.frames = frames.filter(function(i:FrameInfo) { return i.frame != null; }); + return sheets; } -import Packer.RectanglePacker; -import Packer.RectangleNode; + + public function FramePacker(frames:Array) + { + this.frames = frames.filter(function(i:FrameInfo, index, arr) { return i.frame != null; }, this); + } private function GetMaxSize(frames:Array):Point { if (frames.length == 0) return new Point(); - return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo) { return i.frame.width; })), - Math.max.apply(Math, frames.map(function(i:FrameInfo) { return i.frame.width; }))); + return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frame.width; }, this)), + Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frame.height; }, this))); } private function GetArea(frames:Array):int @@ -25,11 +30,11 @@ var area = 0; for (var i in frames) { - if (frames[i] instanceof FrameInfo) + if (frames[i] is FrameInfo) { area += frames[i].frame.width * frames[i].frame.height; } - if (frames[i] instanceof BitmapData || frames[i] instanceof Rectangle) + if (frames[i] is BitmapData || frames[i] is Rectangle) { area += frames[i].width * frames[i].height; } @@ -39,8 +44,10 @@ public const AreaUsedMultiplier = 1.3; // guess how much more sheet space we use than actual space taken up by frames - public function Pack():Boolean + private var callback; + public function Pack(callback) { + this.callback = callback; sheets = new Array(); var index = 0; while (index < frames.length) @@ -50,35 +57,29 @@ var max:Point = GetMaxSize(now); var area:int = GetArea(now); - var exponent = Math.floor(Math.log(area * AreaUsedMultiplier) / Math.log(2)); - var sizeX:int = Math.pow(2, exponent); - var sizeY:int = startSizeX; + var exponent = Math.floor(Math.log(Math.sqrt(area * AreaUsedMultiplier)) / Math.log(2)); + var sizeX:int = Math.min(MaxSize, Math.pow(2, exponent)); + var sizeY:int = sizeX; - var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY); + var i; var startIndex = index; - while (index < frames.length) + var nodes:Array = new Array(); + while (startIndex == index) { - var done = false; - var nodes:Array = new Array(); - var sorted:Array = now.sortOn(now, Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:FrameInfo, b:FrameInfo) { return (a.frame.width * a.frame.height) - (b.frame.width - b.frame.height); }); + nodes.length = 0; + var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY); + var sorted:Array = now.sortOn("frame", Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:BitmapData, b:BitmapData) { return (a.width * a.height) - (b.width - b.height); }); + for (var sortedI = 0; sortedI < now.length; sortedI++) { - var i = nodes[sorted[sortedI]]; + i = sorted[sortedI]; var node:RectangleNode = sheet.Insert(now[i].frame.width, now[i].frame.height); if (node == null) { - if (sortedI == 0) - { - Exporter.Instance.Print("Failure: item too large to pack on sheet"); - return false; - } break; } - else - { - nodes.push(node); - } + nodes.push(node); } if (nodes.length == now.length) { @@ -95,30 +96,28 @@ { sizeY *= 2; } - if (sizeX > 2048 || sizeY > 2048) + if (sizeX > MaxSize || sizeY > MaxSize) { - index += now.length; + if (nodes.length == 0) + { + Exporter.Instance.Print("Failure: item too large to pack on sheet"); + callback(false); + return; + } + index += nodes.length; } } } - if (index > startIndex) + for (i = startIndex; i < index; i++) { - for (var i = startIndex; i < index; i++) - { - frames[i].sheetIndex = sheets.length; - frames[i].rect = nodes[i - startIndex].Rect; - } - sheets.push(sheet); - } + frames[i].sheetIndex = sheets.length; + frames[i].rect = nodes[i - startIndex].Rect; + } + sheets.push(sheet); } - return output; - } - - private function PackFrames(toPack:Array, sizeX:int, sizeY:int):Array - { - + callback(true); } } } \ No newline at end of file diff --git a/GraphicExportDef.as b/GraphicExportDef.as index fb60e89..12ca505 100755 --- a/GraphicExportDef.as +++ b/GraphicExportDef.as @@ -9,6 +9,9 @@ public class GraphicExportDef { + public const TYPE_SPRITES = 1; + public const TYPE_SKELETAL = 2; + private var _exportType:int = 1; public function get exportType():int { return _exportType; } @@ -18,6 +21,9 @@ private var _valid:Boolean = false; public function get valid():Boolean { return _valid; } + private var _padding:int = 0; + public function get padding():int { return _padding; } + private var input:ByteArray; private var inputLoader:Loader = null; @@ -60,6 +66,8 @@ inputLoader.loadBytes(input, context); } + private var frames:IFramesSource = null; + private function LoaderComplete(e) { try @@ -79,12 +87,52 @@ Utils.RecursivelyStop(clip); - Done(); + frames = null; + + switch (exportType) + { + case TYPE_SPRITES: + frames = new AnimatedSpriteGenerator(clip, this); + break; + } + + if (frames == null) + { + Fail("invalid export type: " + exportType); + return; + } + + frames.Go(FramesDone); + } catch (e) { Exporter.Instance.Print(e.getStackTrace()); } } + + private var packer:FramePacker; + private function FramesDone(success) + { + if (!success) + { + Fail("couldn't get source frames"); + return; + } + packer = new FramePacker(frames.GetFrames()); + packer.Pack(PackDone); + } + + private function PackDone(success) + { + if (!success) + { + Fail("couldn't pack frames"); + return; + } + + Done(); + } + private function LoaderError(e) { Fail("couldn't load input: " + e.toString()); diff --git a/IFramesSource.as b/IFramesSource.as new file mode 100755 index 0000000..a55f231 --- /dev/null +++ b/IFramesSource.as @@ -0,0 +1,9 @@ +package +{ + public interface IFramesSource + { + function Go(callback); + function GetFrames():Array; + function GetData(); + } +} \ No newline at end of file diff --git a/RectanglePacker.as b/RectanglePacker.as index be87439..1a5f673 100755 --- a/RectanglePacker.as +++ b/RectanglePacker.as @@ -105,12 +105,14 @@ */ /* END DEBUG CHECKS */ + var i, n:RectangleNode, p:RectangleNode; + // setup the Removed flag on every node that should be removed // we also check at this time to see if any more nodes need to be removed, such as a parent of // 2 removed nodes (we want to optimize the tree to get rid of completely empty subtrees) - for (var i = 0; i < nodes.length; i++) + for (i = 0; i < nodes.length; i++) { - var n:RectangleNode = nodes[i]; + n = nodes[i]; if (n.Host == this) { n.Removed = true; @@ -123,7 +125,7 @@ if (n.Parent != null) { - var p:RectangleNode = n.Parent; + p = n.Parent; // check and see if the current node's parent still has any valid children if ((p.Left.IsFreeSpace || p.Left.Removed) && (p.Right.IsFreeSpace || p.Right.Removed)) { @@ -150,23 +152,24 @@ } } } - + // now we have an expanded list of all nodes that will actually be cut out to return an optimized tree, // so go ahead and remove them - for (var i = 0; i < nodes.length; i++) + for (i = 0; i < nodes.length; i++) { - var n:RectangleNode = nodes[i]; + n = nodes[i]; if (n.Parent != null) { // n is getting removed, so n.Parent may have some changes to its pointers (provided n.Parent is not // also getting removed!) - var p:RectangleNode = n.Parent; + p = n.Parent; if (!p.Removed) { if (p.Left && p.Right) { + var newNode:RectangleNode; if (p.Left.Removed && p.Right.Removed) { // both children were removed, but the parent wasn't, so the parent must be root @@ -188,7 +191,7 @@ { // duplicate the node representing empty space so the original node // remains untouched - var newNode:RectangleNode = new RectangleNode(0, 0, 0, 0, this); + newNode = new RectangleNode(0, 0, 0, 0, this); newNode.MakeFromNode(p.Right); newNode.UpdateLinksFromNode(p.Right); } @@ -208,7 +211,7 @@ { // duplicate the node representing empty space so the original node // remains untouched - var newNode:RectangleNode = new RectangleNode(0, 0, 0, 0, this); + newNode = new RectangleNode(0, 0, 0, 0, this); newNode.MakeFromNode(p.Left); newNode.UpdateLinksFromNode(p.Left); } @@ -231,7 +234,6 @@ } //strs.push(EnumerateTree()); } - RenderDetailsBox.EndTrack("PackRemoveTime"); } public function Insert(w, h, useInstead:RectangleNode = null):RectangleNode @@ -247,7 +249,6 @@ area += w * h; node.InUse = true; - RenderDetailsBox.StartTrack("PackInsertTime"); var splitVertFirstCost = 0; if (w < node.Rect.width) { @@ -312,8 +313,6 @@ node.Flags.push("UseInstead"); } - RenderDetailsBox.EndTrack("PackInsertTime"); - //if (!CheckOKNode(node)) node.Flags.push("BadNode"); return node; @@ -441,7 +440,7 @@ { if (n.InUse) { - _trace(Math.round(n.LastAccessTime / 1000)); + //_trace(Math.round(n.LastAccessTime / 1000)); } var p:RectangleNode = n.Parent; if (p) diff --git a/AnimatedSpriteGenerator.as b/AnimatedSpriteGenerator.as new file mode 100755 index 0000000..92590f8 --- /dev/null +++ b/AnimatedSpriteGenerator.as @@ -0,0 +1,92 @@ +package +{ + import flash.display.*; + import flash.geom.*; + + public class AnimatedSpriteGenerator implements IFramesSource + { + private var src:MovieClip; + private var def:GraphicExportDef; + private var sequences:Array = new Array(); // [[AnimatedSpriteFrame]] + private var callback; + public function GetFrames():Array + { + var ret:Array = []; + for (var i in sequences) + { + ret = ret.concat(sequences[i].map(function(frame:AnimatedSpriteFrame, i, arr) { return frame.info; })); + } + return ret; + } + + public function GetData() + { + return {}; + } + + public function AnimatedSpriteGenerator(src:MovieClip, def:GraphicExportDef) + { + this.src = src; + this.def = def; + } + + public function Go(callback) + { + this.callback = callback; + for (var i = 0; i < src.totalFrames; i++) + { + Utils.WeirdGotoFrame(src, i + 1); + Utils.RecursivelyStop(src); + + for (var j = 0; j < src.numChildren; j++) + { + var c = src.getChildAt(j); + if (c is MovieClip) + { + sequences.push(GetAnimation(c, i)); + } + } + } + + callback(true); + } + + private function GetAnimation(m:MovieClip, seq:int):Array + { + var ret:Array = new Array(); + for (var i = 0; i < m.totalFrames; i++) + { + Utils.WeirdGotoFrame(m, i + 1); + Utils.RecursivelyStop(m); + + var bounds:Rectangle = Utils.GetAccurateBounds(m); + + var b:BitmapData = new BitmapData(bounds.width + def.padding * 2, bounds.height + def.padding * 2, true, 0x0); + b.drawWithQuality(m,new Matrix(1, 0, 0, 1, -bounds.left + def.padding, -bounds.top + def.padding), null, null, bounds, false, StageQuality.BEST); + + var info:FrameInfo = new FrameInfo(b); + + var frame:AnimatedSpriteFrame = new AnimatedSpriteFrame(); + frame.info = info; + frame.sequence = seq; + frame.frame = i; + frame.offset = new Point(-bounds.left + def.padding, -bounds.right + def.padding); + + ret.push(frame); + } + + return ret; + } + } +} + +class AnimatedSpriteFrame +{ + import flash.display.*; + import flash.geom.*; + + public var info:FrameInfo; + public var sequence:int = 0; + public var frame:int = 0; + public var offset:Point = new Point(); +} \ No newline at end of file diff --git a/DebugExporter.swf b/DebugExporter.swf index 3a08579..eb90984 100755 --- a/DebugExporter.swf +++ b/DebugExporter.swf Binary files differ diff --git a/Exporter.as b/Exporter.as index 98b1198..e754de5 100644 --- a/Exporter.as +++ b/Exporter.as @@ -249,7 +249,6 @@ { if (driver != null) { - Trace(str); driver.writeUTF(str); driver.flush(); } diff --git a/FrameInfo.as b/FrameInfo.as index 69227a2..9e2c773 100755 --- a/FrameInfo.as +++ b/FrameInfo.as @@ -1,10 +1,15 @@ package { import flash.display.*; + import flash.geom.*; public class FrameInfo { public var frame:BitmapData; public var rect:Rectangle = new Rectangle(); public var sheetIndex:int = 0; + public function FrameInfo(b:BitmapData) + { + frame = b; + } } } \ No newline at end of file diff --git a/FramePacker.as b/FramePacker.as index 8f6f97b..0e5e8a9 100755 --- a/FramePacker.as +++ b/FramePacker.as @@ -4,20 +4,25 @@ import flash.display.*; public class FramePacker { + public const MaxSize:int = 2048; private var sheets:Array; // [RectanglePacker] private var frames:Array; // [FrameInfo] - public void FramePacker(frames:Array) + + public function GetSheets():Array { - this.frames = frames.filter(function(i:FrameInfo) { return i.frame != null; }); + return sheets; } -import Packer.RectanglePacker; -import Packer.RectangleNode; + + public function FramePacker(frames:Array) + { + this.frames = frames.filter(function(i:FrameInfo, index, arr) { return i.frame != null; }, this); + } private function GetMaxSize(frames:Array):Point { if (frames.length == 0) return new Point(); - return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo) { return i.frame.width; })), - Math.max.apply(Math, frames.map(function(i:FrameInfo) { return i.frame.width; }))); + return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frame.width; }, this)), + Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frame.height; }, this))); } private function GetArea(frames:Array):int @@ -25,11 +30,11 @@ var area = 0; for (var i in frames) { - if (frames[i] instanceof FrameInfo) + if (frames[i] is FrameInfo) { area += frames[i].frame.width * frames[i].frame.height; } - if (frames[i] instanceof BitmapData || frames[i] instanceof Rectangle) + if (frames[i] is BitmapData || frames[i] is Rectangle) { area += frames[i].width * frames[i].height; } @@ -39,8 +44,10 @@ public const AreaUsedMultiplier = 1.3; // guess how much more sheet space we use than actual space taken up by frames - public function Pack():Boolean + private var callback; + public function Pack(callback) { + this.callback = callback; sheets = new Array(); var index = 0; while (index < frames.length) @@ -50,35 +57,29 @@ var max:Point = GetMaxSize(now); var area:int = GetArea(now); - var exponent = Math.floor(Math.log(area * AreaUsedMultiplier) / Math.log(2)); - var sizeX:int = Math.pow(2, exponent); - var sizeY:int = startSizeX; + var exponent = Math.floor(Math.log(Math.sqrt(area * AreaUsedMultiplier)) / Math.log(2)); + var sizeX:int = Math.min(MaxSize, Math.pow(2, exponent)); + var sizeY:int = sizeX; - var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY); + var i; var startIndex = index; - while (index < frames.length) + var nodes:Array = new Array(); + while (startIndex == index) { - var done = false; - var nodes:Array = new Array(); - var sorted:Array = now.sortOn(now, Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:FrameInfo, b:FrameInfo) { return (a.frame.width * a.frame.height) - (b.frame.width - b.frame.height); }); + nodes.length = 0; + var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY); + var sorted:Array = now.sortOn("frame", Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:BitmapData, b:BitmapData) { return (a.width * a.height) - (b.width - b.height); }); + for (var sortedI = 0; sortedI < now.length; sortedI++) { - var i = nodes[sorted[sortedI]]; + i = sorted[sortedI]; var node:RectangleNode = sheet.Insert(now[i].frame.width, now[i].frame.height); if (node == null) { - if (sortedI == 0) - { - Exporter.Instance.Print("Failure: item too large to pack on sheet"); - return false; - } break; } - else - { - nodes.push(node); - } + nodes.push(node); } if (nodes.length == now.length) { @@ -95,30 +96,28 @@ { sizeY *= 2; } - if (sizeX > 2048 || sizeY > 2048) + if (sizeX > MaxSize || sizeY > MaxSize) { - index += now.length; + if (nodes.length == 0) + { + Exporter.Instance.Print("Failure: item too large to pack on sheet"); + callback(false); + return; + } + index += nodes.length; } } } - if (index > startIndex) + for (i = startIndex; i < index; i++) { - for (var i = startIndex; i < index; i++) - { - frames[i].sheetIndex = sheets.length; - frames[i].rect = nodes[i - startIndex].Rect; - } - sheets.push(sheet); - } + frames[i].sheetIndex = sheets.length; + frames[i].rect = nodes[i - startIndex].Rect; + } + sheets.push(sheet); } - return output; - } - - private function PackFrames(toPack:Array, sizeX:int, sizeY:int):Array - { - + callback(true); } } } \ No newline at end of file diff --git a/GraphicExportDef.as b/GraphicExportDef.as index fb60e89..12ca505 100755 --- a/GraphicExportDef.as +++ b/GraphicExportDef.as @@ -9,6 +9,9 @@ public class GraphicExportDef { + public const TYPE_SPRITES = 1; + public const TYPE_SKELETAL = 2; + private var _exportType:int = 1; public function get exportType():int { return _exportType; } @@ -18,6 +21,9 @@ private var _valid:Boolean = false; public function get valid():Boolean { return _valid; } + private var _padding:int = 0; + public function get padding():int { return _padding; } + private var input:ByteArray; private var inputLoader:Loader = null; @@ -60,6 +66,8 @@ inputLoader.loadBytes(input, context); } + private var frames:IFramesSource = null; + private function LoaderComplete(e) { try @@ -79,12 +87,52 @@ Utils.RecursivelyStop(clip); - Done(); + frames = null; + + switch (exportType) + { + case TYPE_SPRITES: + frames = new AnimatedSpriteGenerator(clip, this); + break; + } + + if (frames == null) + { + Fail("invalid export type: " + exportType); + return; + } + + frames.Go(FramesDone); + } catch (e) { Exporter.Instance.Print(e.getStackTrace()); } } + + private var packer:FramePacker; + private function FramesDone(success) + { + if (!success) + { + Fail("couldn't get source frames"); + return; + } + packer = new FramePacker(frames.GetFrames()); + packer.Pack(PackDone); + } + + private function PackDone(success) + { + if (!success) + { + Fail("couldn't pack frames"); + return; + } + + Done(); + } + private function LoaderError(e) { Fail("couldn't load input: " + e.toString()); diff --git a/IFramesSource.as b/IFramesSource.as new file mode 100755 index 0000000..a55f231 --- /dev/null +++ b/IFramesSource.as @@ -0,0 +1,9 @@ +package +{ + public interface IFramesSource + { + function Go(callback); + function GetFrames():Array; + function GetData(); + } +} \ No newline at end of file diff --git a/RectanglePacker.as b/RectanglePacker.as index be87439..1a5f673 100755 --- a/RectanglePacker.as +++ b/RectanglePacker.as @@ -105,12 +105,14 @@ */ /* END DEBUG CHECKS */ + var i, n:RectangleNode, p:RectangleNode; + // setup the Removed flag on every node that should be removed // we also check at this time to see if any more nodes need to be removed, such as a parent of // 2 removed nodes (we want to optimize the tree to get rid of completely empty subtrees) - for (var i = 0; i < nodes.length; i++) + for (i = 0; i < nodes.length; i++) { - var n:RectangleNode = nodes[i]; + n = nodes[i]; if (n.Host == this) { n.Removed = true; @@ -123,7 +125,7 @@ if (n.Parent != null) { - var p:RectangleNode = n.Parent; + p = n.Parent; // check and see if the current node's parent still has any valid children if ((p.Left.IsFreeSpace || p.Left.Removed) && (p.Right.IsFreeSpace || p.Right.Removed)) { @@ -150,23 +152,24 @@ } } } - + // now we have an expanded list of all nodes that will actually be cut out to return an optimized tree, // so go ahead and remove them - for (var i = 0; i < nodes.length; i++) + for (i = 0; i < nodes.length; i++) { - var n:RectangleNode = nodes[i]; + n = nodes[i]; if (n.Parent != null) { // n is getting removed, so n.Parent may have some changes to its pointers (provided n.Parent is not // also getting removed!) - var p:RectangleNode = n.Parent; + p = n.Parent; if (!p.Removed) { if (p.Left && p.Right) { + var newNode:RectangleNode; if (p.Left.Removed && p.Right.Removed) { // both children were removed, but the parent wasn't, so the parent must be root @@ -188,7 +191,7 @@ { // duplicate the node representing empty space so the original node // remains untouched - var newNode:RectangleNode = new RectangleNode(0, 0, 0, 0, this); + newNode = new RectangleNode(0, 0, 0, 0, this); newNode.MakeFromNode(p.Right); newNode.UpdateLinksFromNode(p.Right); } @@ -208,7 +211,7 @@ { // duplicate the node representing empty space so the original node // remains untouched - var newNode:RectangleNode = new RectangleNode(0, 0, 0, 0, this); + newNode = new RectangleNode(0, 0, 0, 0, this); newNode.MakeFromNode(p.Left); newNode.UpdateLinksFromNode(p.Left); } @@ -231,7 +234,6 @@ } //strs.push(EnumerateTree()); } - RenderDetailsBox.EndTrack("PackRemoveTime"); } public function Insert(w, h, useInstead:RectangleNode = null):RectangleNode @@ -247,7 +249,6 @@ area += w * h; node.InUse = true; - RenderDetailsBox.StartTrack("PackInsertTime"); var splitVertFirstCost = 0; if (w < node.Rect.width) { @@ -312,8 +313,6 @@ node.Flags.push("UseInstead"); } - RenderDetailsBox.EndTrack("PackInsertTime"); - //if (!CheckOKNode(node)) node.Flags.push("BadNode"); return node; @@ -441,7 +440,7 @@ { if (n.InUse) { - _trace(Math.round(n.LastAccessTime / 1000)); + //_trace(Math.round(n.LastAccessTime / 1000)); } var p:RectangleNode = n.Parent; if (p) diff --git a/Utils.as b/Utils.as index 83c797c..c76edbe 100644 --- a/Utils.as +++ b/Utils.as @@ -1,6 +1,7 @@ package { import flash.display.*; + import flash.geom.*; public class Utils { public static function RecursivelyStop(clip) @@ -17,5 +18,242 @@ } } } + + public static function WeirdGotoFrame(clip:MovieClip, frame) + { + var dir = clip.currentFrame > frame ? -1 : 1; + while (clip.currentFrame != frame) + { + RecurseDir(clip, dir); + } + } + + protected static function RecurseDir(clip:MovieClip, dir) + { + for (var i = 0; i < clip.numChildren; i++) + { + var child = clip.getChildAt(i); + if (child is MovieClip) + { + RecurseDir(child, dir); + } + } + if (dir == 1) + { + clip.nextFrame(); + } else { + clip.prevFrame(); + } + } + + protected static var tempSpace:BitmapData = null; + protected static var tempSpaceRect:Rectangle = new Rectangle(); + protected static var tempSpaceMatrix:Matrix = new Matrix(); + protected static var tempSpaceZero:Point = new Point(); + protected static var tempSpaceColorTransform:ColorTransform = new ColorTransform(0,0,0,0,0,0,0,255); + protected static var lastDrawOffset:Point = new Point(); + public static function GetAccurateBounds(clip:DisplayObject, useMatrix:Matrix = null):Rectangle + { + if (!tempSpace) tempSpace = new BitmapData(2048, 2048, true, 0x0); + + var oldPad = 0; + var padAmount = 5; + + var ret:Rectangle = new Rectangle(); + + var baseBounds:Rectangle = clip.getBounds(clip); + + var boundsClip:DisplayObject = null; + if (clip is DisplayObjectContainer) + { + boundsClip = (clip as DisplayObjectContainer).getChildByName("__bounds__"); + } + + if (boundsClip != null) + { + baseBounds = boundsClip.getBounds(clip); + } + + if (useMatrix) + { + TransformAABBRect(baseBounds, useMatrix); + } + + if (boundsClip != null) + { + return baseBounds; + } + + var minW = Math.pow(2, Math.ceil(Math.log(baseBounds.width + padAmount * 2) / Math.log(2))); + var minH = Math.pow(2, Math.ceil(Math.log(baseBounds.height + padAmount * 2) / Math.log(2))); + if (tempSpace == null) + { + tempSpace = new BitmapData(minW, minH, true, 0x0); + } + if (tempSpace.width < minW || tempSpace.height < minH) + { + tempSpace = new BitmapData(Math.max(tempSpace.width, minW), Math.max(tempSpace.height, minH), true, 0x0); + } + + var r:Rectangle = new Rectangle(); + + baseBounds.left = Math.floor(baseBounds.left); + baseBounds.right = Math.ceil(baseBounds.right); + + baseBounds.top = Math.floor(baseBounds.top); + baseBounds.bottom = Math.ceil(baseBounds.bottom); + + var i; + + var needsChecking = true; + while (needsChecking) + { + needsChecking = false; + r.left = baseBounds.left - padAmount; + r.right = baseBounds.right + padAmount; + + r.top = baseBounds.top - padAmount; + r.bottom = baseBounds.bottom + padAmount; + + ret = r.clone(); + + tempSpaceRect.x = tempSpaceRect.y = 0; + tempSpaceRect.width = tempSpace.width; + tempSpaceRect.height = tempSpace.height; + tempSpace.fillRect(tempSpaceRect, 0x0); + + tempSpaceMatrix.identity(); + if (useMatrix) + { + tempSpaceMatrix.a = useMatrix.a; + tempSpaceMatrix.b = useMatrix.b; + tempSpaceMatrix.c = useMatrix.c; + tempSpaceMatrix.d = useMatrix.d; + tempSpaceMatrix.tx = useMatrix.tx; + tempSpaceMatrix.ty = useMatrix.ty; + } + tempSpaceMatrix.translate(-r.left, -r.top); + + lastDrawOffset.x = -r.left; + lastDrawOffset.y = -r.top; + tempSpace.draw(clip, tempSpaceMatrix); + + tempSpaceRect.width = 1; + tempSpaceRect.height = r.height; + + var sideMovedIn = 0; + for (i = 0; i < r.width; i++) + { + tempSpaceRect.x = i; + if (tempSpace.hitTest(tempSpaceZero, 1, tempSpaceRect)) + { + break; + } else { + ret.left++; + sideMovedIn++; + } + } + if (ret.left < baseBounds.left - oldPad - 1) + { + oldPad = padAmount; + padAmount += 20; + needsChecking = true; + continue; + } + + for (i = r.width - 1; i >= sideMovedIn; i--) + { + tempSpaceRect.x = i; + if (tempSpace.hitTest(tempSpaceZero, 1, tempSpaceRect)) + { + break; + } else { + ret.right--; + } + } + if (ret.right > baseBounds.right + oldPad + 1 && sideMovedIn <= baseBounds.right + oldPad + 1) + { + oldPad = padAmount; + padAmount += 20; + needsChecking = true; + continue; + } + + sideMovedIn = 0; + tempSpaceRect.width = r.width; + tempSpaceRect.height = 1; + tempSpaceRect.x = 0; + for (i = 0; i < r.height; i++) + { + tempSpaceRect.y = i; + if (tempSpace.hitTest(tempSpaceZero, 1, tempSpaceRect)) + { + break; + } else { + ret.top++; + sideMovedIn++; + } + } + if (ret.top < baseBounds.top - oldPad - 1) + { + oldPad = padAmount; + padAmount += 20; + needsChecking = true; + continue; + } + + for (i = r.height - 1; i >= sideMovedIn; i--) + { + tempSpaceRect.y = i; + if (tempSpace.hitTest(tempSpaceZero, 1, tempSpaceRect)) + { + break; + } else { + ret.bottom--; + } + } + if (ret.bottom > baseBounds.bottom + oldPad + 1 && sideMovedIn <= baseBounds.bottom + oldPad + 1) + { + oldPad = padAmount; + padAmount += 20; + needsChecking = true; + continue; + } + } + return ret; + } + + public static function TransformAABBRect(r:Rectangle, m:Matrix) + { + var o1X = r.left; + var o1Y = r.top; + + var o2X = r.right; + var o2Y = r.top; + + var o3X = r.left; + var o3Y = r.bottom; + + var o4X = r.right; + var o4Y = r.bottom; + + var n1X = o1X * m.a + o1Y * m.c + m.tx; + var n1Y = o1X * m.b + o1Y * m.d + m.ty; + + var n2X = o2X * m.a + o2Y * m.c + m.tx; + var n2Y = o2X * m.b + o2Y * m.d + m.ty; + + var n3X = o3X * m.a + o3Y * m.c + m.tx; + var n3Y = o3X * m.b + o3Y * m.d + m.ty; + + var n4X = o4X * m.a + o4Y * m.c + m.tx; + var n4Y = o4X * m.b + o4Y * m.d + m.ty; + + r.left = Math.min(n1X, n2X, n3X, n4X); + r.right = Math.max(n1X, n2X, n3X, n4X); + + r.top = Math.min(n1Y, n2Y, n3Y, n4Y); + r.bottom = Math.max(n1Y, n2Y, n3Y, n4Y); + } } } \ No newline at end of file diff --git a/AnimatedSpriteGenerator.as b/AnimatedSpriteGenerator.as new file mode 100755 index 0000000..92590f8 --- /dev/null +++ b/AnimatedSpriteGenerator.as @@ -0,0 +1,92 @@ +package +{ + import flash.display.*; + import flash.geom.*; + + public class AnimatedSpriteGenerator implements IFramesSource + { + private var src:MovieClip; + private var def:GraphicExportDef; + private var sequences:Array = new Array(); // [[AnimatedSpriteFrame]] + private var callback; + public function GetFrames():Array + { + var ret:Array = []; + for (var i in sequences) + { + ret = ret.concat(sequences[i].map(function(frame:AnimatedSpriteFrame, i, arr) { return frame.info; })); + } + return ret; + } + + public function GetData() + { + return {}; + } + + public function AnimatedSpriteGenerator(src:MovieClip, def:GraphicExportDef) + { + this.src = src; + this.def = def; + } + + public function Go(callback) + { + this.callback = callback; + for (var i = 0; i < src.totalFrames; i++) + { + Utils.WeirdGotoFrame(src, i + 1); + Utils.RecursivelyStop(src); + + for (var j = 0; j < src.numChildren; j++) + { + var c = src.getChildAt(j); + if (c is MovieClip) + { + sequences.push(GetAnimation(c, i)); + } + } + } + + callback(true); + } + + private function GetAnimation(m:MovieClip, seq:int):Array + { + var ret:Array = new Array(); + for (var i = 0; i < m.totalFrames; i++) + { + Utils.WeirdGotoFrame(m, i + 1); + Utils.RecursivelyStop(m); + + var bounds:Rectangle = Utils.GetAccurateBounds(m); + + var b:BitmapData = new BitmapData(bounds.width + def.padding * 2, bounds.height + def.padding * 2, true, 0x0); + b.drawWithQuality(m,new Matrix(1, 0, 0, 1, -bounds.left + def.padding, -bounds.top + def.padding), null, null, bounds, false, StageQuality.BEST); + + var info:FrameInfo = new FrameInfo(b); + + var frame:AnimatedSpriteFrame = new AnimatedSpriteFrame(); + frame.info = info; + frame.sequence = seq; + frame.frame = i; + frame.offset = new Point(-bounds.left + def.padding, -bounds.right + def.padding); + + ret.push(frame); + } + + return ret; + } + } +} + +class AnimatedSpriteFrame +{ + import flash.display.*; + import flash.geom.*; + + public var info:FrameInfo; + public var sequence:int = 0; + public var frame:int = 0; + public var offset:Point = new Point(); +} \ No newline at end of file diff --git a/DebugExporter.swf b/DebugExporter.swf index 3a08579..eb90984 100755 --- a/DebugExporter.swf +++ b/DebugExporter.swf Binary files differ diff --git a/Exporter.as b/Exporter.as index 98b1198..e754de5 100644 --- a/Exporter.as +++ b/Exporter.as @@ -249,7 +249,6 @@ { if (driver != null) { - Trace(str); driver.writeUTF(str); driver.flush(); } diff --git a/FrameInfo.as b/FrameInfo.as index 69227a2..9e2c773 100755 --- a/FrameInfo.as +++ b/FrameInfo.as @@ -1,10 +1,15 @@ package { import flash.display.*; + import flash.geom.*; public class FrameInfo { public var frame:BitmapData; public var rect:Rectangle = new Rectangle(); public var sheetIndex:int = 0; + public function FrameInfo(b:BitmapData) + { + frame = b; + } } } \ No newline at end of file diff --git a/FramePacker.as b/FramePacker.as index 8f6f97b..0e5e8a9 100755 --- a/FramePacker.as +++ b/FramePacker.as @@ -4,20 +4,25 @@ import flash.display.*; public class FramePacker { + public const MaxSize:int = 2048; private var sheets:Array; // [RectanglePacker] private var frames:Array; // [FrameInfo] - public void FramePacker(frames:Array) + + public function GetSheets():Array { - this.frames = frames.filter(function(i:FrameInfo) { return i.frame != null; }); + return sheets; } -import Packer.RectanglePacker; -import Packer.RectangleNode; + + public function FramePacker(frames:Array) + { + this.frames = frames.filter(function(i:FrameInfo, index, arr) { return i.frame != null; }, this); + } private function GetMaxSize(frames:Array):Point { if (frames.length == 0) return new Point(); - return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo) { return i.frame.width; })), - Math.max.apply(Math, frames.map(function(i:FrameInfo) { return i.frame.width; }))); + return new Point(Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frame.width; }, this)), + Math.max.apply(Math, frames.map(function(i:FrameInfo, index, arr) { return i.frame.height; }, this))); } private function GetArea(frames:Array):int @@ -25,11 +30,11 @@ var area = 0; for (var i in frames) { - if (frames[i] instanceof FrameInfo) + if (frames[i] is FrameInfo) { area += frames[i].frame.width * frames[i].frame.height; } - if (frames[i] instanceof BitmapData || frames[i] instanceof Rectangle) + if (frames[i] is BitmapData || frames[i] is Rectangle) { area += frames[i].width * frames[i].height; } @@ -39,8 +44,10 @@ public const AreaUsedMultiplier = 1.3; // guess how much more sheet space we use than actual space taken up by frames - public function Pack():Boolean + private var callback; + public function Pack(callback) { + this.callback = callback; sheets = new Array(); var index = 0; while (index < frames.length) @@ -50,35 +57,29 @@ var max:Point = GetMaxSize(now); var area:int = GetArea(now); - var exponent = Math.floor(Math.log(area * AreaUsedMultiplier) / Math.log(2)); - var sizeX:int = Math.pow(2, exponent); - var sizeY:int = startSizeX; + var exponent = Math.floor(Math.log(Math.sqrt(area * AreaUsedMultiplier)) / Math.log(2)); + var sizeX:int = Math.min(MaxSize, Math.pow(2, exponent)); + var sizeY:int = sizeX; - var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY); + var i; var startIndex = index; - while (index < frames.length) + var nodes:Array = new Array(); + while (startIndex == index) { - var done = false; - var nodes:Array = new Array(); - var sorted:Array = now.sortOn(now, Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:FrameInfo, b:FrameInfo) { return (a.frame.width * a.frame.height) - (b.frame.width - b.frame.height); }); + nodes.length = 0; + var sheet:RectanglePacker = new RectanglePacker(sizeX, sizeY); + var sorted:Array = now.sortOn("frame", Array.RETURNINDEXEDARRAY | Array.DESCENDING, function(a:BitmapData, b:BitmapData) { return (a.width * a.height) - (b.width - b.height); }); + for (var sortedI = 0; sortedI < now.length; sortedI++) { - var i = nodes[sorted[sortedI]]; + i = sorted[sortedI]; var node:RectangleNode = sheet.Insert(now[i].frame.width, now[i].frame.height); if (node == null) { - if (sortedI == 0) - { - Exporter.Instance.Print("Failure: item too large to pack on sheet"); - return false; - } break; } - else - { - nodes.push(node); - } + nodes.push(node); } if (nodes.length == now.length) { @@ -95,30 +96,28 @@ { sizeY *= 2; } - if (sizeX > 2048 || sizeY > 2048) + if (sizeX > MaxSize || sizeY > MaxSize) { - index += now.length; + if (nodes.length == 0) + { + Exporter.Instance.Print("Failure: item too large to pack on sheet"); + callback(false); + return; + } + index += nodes.length; } } } - if (index > startIndex) + for (i = startIndex; i < index; i++) { - for (var i = startIndex; i < index; i++) - { - frames[i].sheetIndex = sheets.length; - frames[i].rect = nodes[i - startIndex].Rect; - } - sheets.push(sheet); - } + frames[i].sheetIndex = sheets.length; + frames[i].rect = nodes[i - startIndex].Rect; + } + sheets.push(sheet); } - return output; - } - - private function PackFrames(toPack:Array, sizeX:int, sizeY:int):Array - { - + callback(true); } } } \ No newline at end of file diff --git a/GraphicExportDef.as b/GraphicExportDef.as index fb60e89..12ca505 100755 --- a/GraphicExportDef.as +++ b/GraphicExportDef.as @@ -9,6 +9,9 @@ public class GraphicExportDef { + public const TYPE_SPRITES = 1; + public const TYPE_SKELETAL = 2; + private var _exportType:int = 1; public function get exportType():int { return _exportType; } @@ -18,6 +21,9 @@ private var _valid:Boolean = false; public function get valid():Boolean { return _valid; } + private var _padding:int = 0; + public function get padding():int { return _padding; } + private var input:ByteArray; private var inputLoader:Loader = null; @@ -60,6 +66,8 @@ inputLoader.loadBytes(input, context); } + private var frames:IFramesSource = null; + private function LoaderComplete(e) { try @@ -79,12 +87,52 @@ Utils.RecursivelyStop(clip); - Done(); + frames = null; + + switch (exportType) + { + case TYPE_SPRITES: + frames = new AnimatedSpriteGenerator(clip, this); + break; + } + + if (frames == null) + { + Fail("invalid export type: " + exportType); + return; + } + + frames.Go(FramesDone); + } catch (e) { Exporter.Instance.Print(e.getStackTrace()); } } + + private var packer:FramePacker; + private function FramesDone(success) + { + if (!success) + { + Fail("couldn't get source frames"); + return; + } + packer = new FramePacker(frames.GetFrames()); + packer.Pack(PackDone); + } + + private function PackDone(success) + { + if (!success) + { + Fail("couldn't pack frames"); + return; + } + + Done(); + } + private function LoaderError(e) { Fail("couldn't load input: " + e.toString()); diff --git a/IFramesSource.as b/IFramesSource.as new file mode 100755 index 0000000..a55f231 --- /dev/null +++ b/IFramesSource.as @@ -0,0 +1,9 @@ +package +{ + public interface IFramesSource + { + function Go(callback); + function GetFrames():Array; + function GetData(); + } +} \ No newline at end of file diff --git a/RectanglePacker.as b/RectanglePacker.as index be87439..1a5f673 100755 --- a/RectanglePacker.as +++ b/RectanglePacker.as @@ -105,12 +105,14 @@ */ /* END DEBUG CHECKS */ + var i, n:RectangleNode, p:RectangleNode; + // setup the Removed flag on every node that should be removed // we also check at this time to see if any more nodes need to be removed, such as a parent of // 2 removed nodes (we want to optimize the tree to get rid of completely empty subtrees) - for (var i = 0; i < nodes.length; i++) + for (i = 0; i < nodes.length; i++) { - var n:RectangleNode = nodes[i]; + n = nodes[i]; if (n.Host == this) { n.Removed = true; @@ -123,7 +125,7 @@ if (n.Parent != null) { - var p:RectangleNode = n.Parent; + p = n.Parent; // check and see if the current node's parent still has any valid children if ((p.Left.IsFreeSpace || p.Left.Removed) && (p.Right.IsFreeSpace || p.Right.Removed)) { @@ -150,23 +152,24 @@ } } } - + // now we have an expanded list of all nodes that will actually be cut out to return an optimized tree, // so go ahead and remove them - for (var i = 0; i < nodes.length; i++) + for (i = 0; i < nodes.length; i++) { - var n:RectangleNode = nodes[i]; + n = nodes[i]; if (n.Parent != null) { // n is getting removed, so n.Parent may have some changes to its pointers (provided n.Parent is not // also getting removed!) - var p:RectangleNode = n.Parent; + p = n.Parent; if (!p.Removed) { if (p.Left && p.Right) { + var newNode:RectangleNode; if (p.Left.Removed && p.Right.Removed) { // both children were removed, but the parent wasn't, so the parent must be root @@ -188,7 +191,7 @@ { // duplicate the node representing empty space so the original node // remains untouched - var newNode:RectangleNode = new RectangleNode(0, 0, 0, 0, this); + newNode = new RectangleNode(0, 0, 0, 0, this); newNode.MakeFromNode(p.Right); newNode.UpdateLinksFromNode(p.Right); } @@ -208,7 +211,7 @@ { // duplicate the node representing empty space so the original node // remains untouched - var newNode:RectangleNode = new RectangleNode(0, 0, 0, 0, this); + newNode = new RectangleNode(0, 0, 0, 0, this); newNode.MakeFromNode(p.Left); newNode.UpdateLinksFromNode(p.Left); } @@ -231,7 +234,6 @@ } //strs.push(EnumerateTree()); } - RenderDetailsBox.EndTrack("PackRemoveTime"); } public function Insert(w, h, useInstead:RectangleNode = null):RectangleNode @@ -247,7 +249,6 @@ area += w * h; node.InUse = true; - RenderDetailsBox.StartTrack("PackInsertTime"); var splitVertFirstCost = 0; if (w < node.Rect.width) { @@ -312,8 +313,6 @@ node.Flags.push("UseInstead"); } - RenderDetailsBox.EndTrack("PackInsertTime"); - //if (!CheckOKNode(node)) node.Flags.push("BadNode"); return node; @@ -441,7 +440,7 @@ { if (n.InUse) { - _trace(Math.round(n.LastAccessTime / 1000)); + //_trace(Math.round(n.LastAccessTime / 1000)); } var p:RectangleNode = n.Parent; if (p) diff --git a/Utils.as b/Utils.as index 83c797c..c76edbe 100644 --- a/Utils.as +++ b/Utils.as @@ -1,6 +1,7 @@ package { import flash.display.*; + import flash.geom.*; public class Utils { public static function RecursivelyStop(clip) @@ -17,5 +18,242 @@ } } } + + public static function WeirdGotoFrame(clip:MovieClip, frame) + { + var dir = clip.currentFrame > frame ? -1 : 1; + while (clip.currentFrame != frame) + { + RecurseDir(clip, dir); + } + } + + protected static function RecurseDir(clip:MovieClip, dir) + { + for (var i = 0; i < clip.numChildren; i++) + { + var child = clip.getChildAt(i); + if (child is MovieClip) + { + RecurseDir(child, dir); + } + } + if (dir == 1) + { + clip.nextFrame(); + } else { + clip.prevFrame(); + } + } + + protected static var tempSpace:BitmapData = null; + protected static var tempSpaceRect:Rectangle = new Rectangle(); + protected static var tempSpaceMatrix:Matrix = new Matrix(); + protected static var tempSpaceZero:Point = new Point(); + protected static var tempSpaceColorTransform:ColorTransform = new ColorTransform(0,0,0,0,0,0,0,255); + protected static var lastDrawOffset:Point = new Point(); + public static function GetAccurateBounds(clip:DisplayObject, useMatrix:Matrix = null):Rectangle + { + if (!tempSpace) tempSpace = new BitmapData(2048, 2048, true, 0x0); + + var oldPad = 0; + var padAmount = 5; + + var ret:Rectangle = new Rectangle(); + + var baseBounds:Rectangle = clip.getBounds(clip); + + var boundsClip:DisplayObject = null; + if (clip is DisplayObjectContainer) + { + boundsClip = (clip as DisplayObjectContainer).getChildByName("__bounds__"); + } + + if (boundsClip != null) + { + baseBounds = boundsClip.getBounds(clip); + } + + if (useMatrix) + { + TransformAABBRect(baseBounds, useMatrix); + } + + if (boundsClip != null) + { + return baseBounds; + } + + var minW = Math.pow(2, Math.ceil(Math.log(baseBounds.width + padAmount * 2) / Math.log(2))); + var minH = Math.pow(2, Math.ceil(Math.log(baseBounds.height + padAmount * 2) / Math.log(2))); + if (tempSpace == null) + { + tempSpace = new BitmapData(minW, minH, true, 0x0); + } + if (tempSpace.width < minW || tempSpace.height < minH) + { + tempSpace = new BitmapData(Math.max(tempSpace.width, minW), Math.max(tempSpace.height, minH), true, 0x0); + } + + var r:Rectangle = new Rectangle(); + + baseBounds.left = Math.floor(baseBounds.left); + baseBounds.right = Math.ceil(baseBounds.right); + + baseBounds.top = Math.floor(baseBounds.top); + baseBounds.bottom = Math.ceil(baseBounds.bottom); + + var i; + + var needsChecking = true; + while (needsChecking) + { + needsChecking = false; + r.left = baseBounds.left - padAmount; + r.right = baseBounds.right + padAmount; + + r.top = baseBounds.top - padAmount; + r.bottom = baseBounds.bottom + padAmount; + + ret = r.clone(); + + tempSpaceRect.x = tempSpaceRect.y = 0; + tempSpaceRect.width = tempSpace.width; + tempSpaceRect.height = tempSpace.height; + tempSpace.fillRect(tempSpaceRect, 0x0); + + tempSpaceMatrix.identity(); + if (useMatrix) + { + tempSpaceMatrix.a = useMatrix.a; + tempSpaceMatrix.b = useMatrix.b; + tempSpaceMatrix.c = useMatrix.c; + tempSpaceMatrix.d = useMatrix.d; + tempSpaceMatrix.tx = useMatrix.tx; + tempSpaceMatrix.ty = useMatrix.ty; + } + tempSpaceMatrix.translate(-r.left, -r.top); + + lastDrawOffset.x = -r.left; + lastDrawOffset.y = -r.top; + tempSpace.draw(clip, tempSpaceMatrix); + + tempSpaceRect.width = 1; + tempSpaceRect.height = r.height; + + var sideMovedIn = 0; + for (i = 0; i < r.width; i++) + { + tempSpaceRect.x = i; + if (tempSpace.hitTest(tempSpaceZero, 1, tempSpaceRect)) + { + break; + } else { + ret.left++; + sideMovedIn++; + } + } + if (ret.left < baseBounds.left - oldPad - 1) + { + oldPad = padAmount; + padAmount += 20; + needsChecking = true; + continue; + } + + for (i = r.width - 1; i >= sideMovedIn; i--) + { + tempSpaceRect.x = i; + if (tempSpace.hitTest(tempSpaceZero, 1, tempSpaceRect)) + { + break; + } else { + ret.right--; + } + } + if (ret.right > baseBounds.right + oldPad + 1 && sideMovedIn <= baseBounds.right + oldPad + 1) + { + oldPad = padAmount; + padAmount += 20; + needsChecking = true; + continue; + } + + sideMovedIn = 0; + tempSpaceRect.width = r.width; + tempSpaceRect.height = 1; + tempSpaceRect.x = 0; + for (i = 0; i < r.height; i++) + { + tempSpaceRect.y = i; + if (tempSpace.hitTest(tempSpaceZero, 1, tempSpaceRect)) + { + break; + } else { + ret.top++; + sideMovedIn++; + } + } + if (ret.top < baseBounds.top - oldPad - 1) + { + oldPad = padAmount; + padAmount += 20; + needsChecking = true; + continue; + } + + for (i = r.height - 1; i >= sideMovedIn; i--) + { + tempSpaceRect.y = i; + if (tempSpace.hitTest(tempSpaceZero, 1, tempSpaceRect)) + { + break; + } else { + ret.bottom--; + } + } + if (ret.bottom > baseBounds.bottom + oldPad + 1 && sideMovedIn <= baseBounds.bottom + oldPad + 1) + { + oldPad = padAmount; + padAmount += 20; + needsChecking = true; + continue; + } + } + return ret; + } + + public static function TransformAABBRect(r:Rectangle, m:Matrix) + { + var o1X = r.left; + var o1Y = r.top; + + var o2X = r.right; + var o2Y = r.top; + + var o3X = r.left; + var o3Y = r.bottom; + + var o4X = r.right; + var o4Y = r.bottom; + + var n1X = o1X * m.a + o1Y * m.c + m.tx; + var n1Y = o1X * m.b + o1Y * m.d + m.ty; + + var n2X = o2X * m.a + o2Y * m.c + m.tx; + var n2Y = o2X * m.b + o2Y * m.d + m.ty; + + var n3X = o3X * m.a + o3Y * m.c + m.tx; + var n3Y = o3X * m.b + o3Y * m.d + m.ty; + + var n4X = o4X * m.a + o4Y * m.c + m.tx; + var n4Y = o4X * m.b + o4Y * m.d + m.ty; + + r.left = Math.min(n1X, n2X, n3X, n4X); + r.right = Math.max(n1X, n2X, n3X, n4X); + + r.top = Math.min(n1Y, n2Y, n3Y, n4Y); + r.bottom = Math.max(n1Y, n2Y, n3Y, n4Y); + } } } \ No newline at end of file diff --git a/driver.py b/driver.py index 0a045d6..70f82e6 100644 --- a/driver.py +++ b/driver.py @@ -95,7 +95,7 @@ if not stdin_callback(line): quit = True if quit: - if not closed: + if not closed and not self.exporter_already_running: self.send_json({"command":"exit"}) self.conn.close() self.sock.close()