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();
}
}
}