package GraphicExport { import Defs.GraphicDef; import djarts.utils.CountWaiter; import djarts.core.*; import djarts.display.*; import flash.geom.Rectangle; import flash.geom.Point; import Packer.SpriteSheet; import flash.geom.Matrix; import flash.utils.*; import flash.display.MovieClip; import flash.display.BitmapData; import flash.display.Bitmap; import djarts.utils.SimpleTimer; import flash.display.*; import flash.geom.ColorTransform; public class IconPacker { protected var doneCallback = null; protected var callbackParam; protected var holder:MovieClip; protected var jsonSpriteData = {}; protected var pngSheets = []; public var outputFormat = "binary"; public function IconPacker(callback, callbackParam, holder:MovieClip, outputFormat) { this.doneCallback = callback; this.callbackParam = callbackParam; this.holder = holder; this.outputFormat = outputFormat; jsonSpriteData = {}; jsonSpriteData.format = "spritesheet"; jsonSpriteData.graphics = []; output.endian = Endian.LITTLE_ENDIAN; } protected var pageWidth = 2048; protected var pageHeight = 2048; protected var defs:Array; protected var pad = 3; public function ExportDefs(defs:Array) { if (defs.length == 1) { ExportSingle(defs[0]) } else { this.defs = defs; Export(); } } public function ExportSingle(def:GraphicDef) { this.defs = [def]; var g = GraphicFactory.Instance().GetGraphicByID(def.ID, null, CheckSizesForSingle); if (GraphicFactory.Instance().LoadedOK(g)) { CheckSizesForSingle(null, g); } } protected function CheckSizesForSingle(caller, g) { var p:Point = Pow2SizeForGraphic(defs[0]); pageWidth = p.x; pageHeight = p.y; Export(); } protected function Pow2SizeForGraphic(def:GraphicDef):Point { var g = GraphicFactory.Instance().GetGraphicByID(def.ID); var bounds:Rectangle = CacheManager.Instance().GetAccurateBounds(g, 2048); if (def.ExportParams.hasOwnProperty("available_sizes")) { var totalSize = 0; for (var i in def.ExportParams.available_sizes) { if (def.ExportParams.available_sizes[i] == "default") { var largestSide = Math.max(bounds.width + pad * 2, bounds.height + pad * 2); totalSize += largestSide * largestSide; } else { var parts:Array = def.ExportParams.available_sizes[i].split("x"); if (parts.length == 2) { var w = Number(parts[0]) + pad * 2; var h = Number(parts[1]) + pad * 2; totalSize += w * h; } } } var size = Math.sqrt(totalSize); var exponent = Math.ceil(Math.log(size) / Math.log(2)); size = Math.pow(2, exponent); return new Point(size, size); } else { var exponentX = Math.ceil(Math.log(bounds.width + pad * 2) / Math.log(2)); var exponentY = Math.ceil(Math.log(bounds.height + pad * 2) / Math.log(2)); return new Point(Math.pow(2, exponentX), Math.pow(2, exponentY)); } } protected var sheets:Array = new Array(); protected var totalSprites = 0; protected var output:ByteArray = new ByteArray(); protected var lastGraphic = null; protected var currentDef:GraphicDef; protected function Export() { var sheet:SpriteSheet = new SpriteSheet(pageWidth, pageHeight); sheets.push(sheet); holder.AddClip(new Bitmap(sheet.GetBitmap())); ExportNext(); } protected function ExportNext() { _strace("Exporting " + defs.length); if (defs.length == 0) { Done(); return; } currentDef = defs.shift(); var g = GraphicFactory.Instance().GetGraphicByID(currentDef.ID, null, GraphicLoaded); if (GraphicFactory.Instance().LoadedOK(g)) { GraphicLoaded(null, g); } } protected function GraphicLoaded(caller, g) { _strace(currentDef.Name); var bounds:Rectangle = CacheManager.Instance().GetAccurateBounds(g, 2048); if (currentDef.ExportParams.hasOwnProperty("available_sizes")) { for (var j in currentDef.ExportParams.available_sizes) { if (currentDef.ExportParams.available_sizes[j] == "default") { AddGraphicAtSize(currentDef, g, 1, bounds, Math.ceil(bounds.width), Math.ceil(bounds.height), true); } else { var parts:Array = currentDef.ExportParams.available_sizes[j].split("x"); if (parts.length == 2) { var width = Number(parts[0]); var height = Number(parts[1]); var scale = Math.min(width / bounds.width, height / bounds.height); AddGraphicAtSize(currentDef, g, scale, bounds, width, height); } } } } else { AddGraphicAtSize(currentDef, g, 1, bounds); } if (holder) { if (lastGraphic) { if (holder.contains(lastGraphic)) holder.removeChild(lastGraphic); } holder.addChild(g); lastGraphic = g; g.x = 150; g.y = 150; bitmapPreview.x = g.x; bitmapPreview.y = g.y + 150; holder.addChild(bitmapPreview); } ExportNext(); } protected var bitmapPreview:MovieClip = new MovieClip(); public static const VERSION = 2; protected function Done() { var stream:ByteArray = new ByteArray(); stream.endian = Endian.LITTLE_ENDIAN; // -1 is an unlikely number of sheets to have, so // I use this as a marker that this file has a version number in it stream.writeInt(-1); stream.writeInt(VERSION); stream.writeInt(sheets.length); for (var i = 0; i < sheets.length; i++) { stream.writeInt(pageWidth); stream.writeInt(pageHeight); var data:ByteArray = PNGExportHelper.GetExportPNG(sheets[i].GetBitmap()); stream.writeInt(data.length); stream.writeBytes(data); pngSheets.push(data); } stream.writeInt(totalSprites); stream.writeBytes(output); var jsonPngSet = new JSONPNGSet(jsonSpriteData.graphics[0].Name, jsonSpriteData, pngSheets); //jsonSpriteData if (doneCallback) { doneCallback(outputFormat == "binary" ? stream : jsonPngSet, callbackParam); } var t:SimpleTimer = new SimpleTimer(2, ClearPreview); } protected function ClearPreview() { if (holder) { if (lastGraphic) { if (holder.contains(lastGraphic)) holder.removeChild(lastGraphic); } //while(holder.numChildren > 0) holder.removeChildAt(0); } } protected function AddGraphicAtSize(def:GraphicDef, g, scale, bounds:Rectangle, availWidth = null, availHeight = null, isDefault = false) { totalSprites++; output.writeUnsignedInt(def.Name.length); output.writeMultiByte(def.Name, "us-ascii"); var spriteData = {}; jsonSpriteData.graphics.push(spriteData); spriteData.name = def.Name; var hasSize = (availWidth != null && availHeight != null); output.writeBoolean(hasSize); if (hasSize) { output.writeBoolean(isDefault); output.writeInt(availWidth); output.writeInt(availHeight); spriteData.defaultSize = isDefault; spriteData.sizeX = availWidth; spriteData.sizeY = availHeight; } output.writeInt(1); // num sequences output.writeInt(1); // num frames spriteData.sequences = []; spriteData.sequences.push([]); var frame = {}; spriteData.sequences[0].push(frame); var b:Rectangle = bounds.clone(); b.left = Math.floor(bounds.left * scale); b.right = Math.ceil(bounds.right * scale); b.top = Math.floor(bounds.top * scale); b.bottom = Math.ceil(bounds.bottom * scale); var newSheet = false; var found = false; for (var s = 0; s < sheets.length; s++) { var node:RectangleNode = sheets[s].GetAllocator().Insert(b.width + pad * 2, b.height + pad * 2); if (node) { output.writeInt(s); // sheet index // rectangle output.writeFloat(node.Rect.left + pad); output.writeFloat(node.Rect.top + pad); output.writeFloat(node.Rect.width - (pad * 2)); output.writeFloat(node.Rect.height - (pad * 2)); output.writeFloat(-b.left); output.writeFloat(-b.top); frame.texture = s; frame.tx = node.Rect.left + pad; frame.ty = node.Rect.top + pad; frame.tw = node.Rect.width - (pad * 2); frame.th = node.Rect.height - (pad * 2); frame.x = -b.left; frame.y = -b.top; sheets[s].GetBitmap().draw(g, new Matrix(scale, 0, 0, scale, -b.left + pad + node.Rect.left, -b.top + pad + node.Rect.top), null, BlendMode.NORMAL, new Rectangle(node.Rect.left + pad, // note: bitmap space! node.Rect.top + pad, node.Rect.width - pad * 2, node.Rect.height - pad * 2)); found = true; break; } if (!found && s == sheets.length - 1) { if (newSheet) { throw new Error("Bad sprite!"); return; } newSheet = true; sheets.push(new SpriteSheet(pageWidth, pageHeight)); holder.AddClip(new Bitmap(sheets[sheets.length - 1].GetBitmap())); } } } public function RemoveSmallChildren(clip:MovieClip) { if (clip.height < 35 && clip.parent != null) { clip.visible = false; _strace("removed clip that was "+clip.height+ " tall"); } for(var i = 0; i < clip.numChildren; i++) { if (clip.getChildAt(i) is MovieClip) { RemoveSmallChildren(clip.getChildAt(i) as MovieClip); } } } public function RecursiveCacheAsBitmap(clip:MovieClip) { clip.cacheAsBitmap = true; for(var i = 0; i < clip.numChildren; i++){ if (clip.getChildAt(i) is MovieClip) { RecursiveCacheAsBitmap(clip.getChildAt(i) as MovieClip); } } } protected var cw:CountWaiter; protected var loadCallback; protected function LoadAll(defs:Array, callback) { loadCallback = callback; cw = new CountWaiter(LoadDone); for (var i in defs) { cw.Wait(); GraphicFactory.Instance().MakeLoadOrFailCallbackByID(defs[i].ID, cw.WaitDone); GraphicFactory.Instance().GetGraphicByID(defs[i].ID, null, BlankCallback); } cw.Go(); } protected function BlankCallback(...rest) { } protected function LoadDone(waiter) { if (loadCallback) loadCallback(); } } }