package Packer
{
import flash.display.MovieClip;
import flash.display.Stage;
import flash.geom.*;
import flash.display.*;
import flash.utils.ByteArray;
import flash.utils.getQualifiedClassName;
import flash.net.FileReference;
import flash.utils.Endian;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.utils.*;
import djarts.core.CacheManager;
import com.adobe.serialization.json.JSON;
import flash.filters.BitmapFilter;
import flash.filters.BlurFilter;
import flash.filters.ColorMatrixFilter;
import fl.motion.AdjustColor;
import djarts.utils.SimpleTimer;
import flash.events.*;
import djarts.core.*;
import djarts.display.*;
public class AsyncGraphicPacker
{
protected var clips:Array = new Array();
protected var clipSizing:Array = new Array();
protected var fonts:Array = new Array();
protected var fontCopies:Array = new Array();
protected var sheetWidth = 1024;
protected var sheetHeight = 1024;
protected var stage;
public var ExpandSpriteRegionEdgeAlphas = true;
public var outputFormat = "binary";
public var outputAssetName = "";
protected var additionalData = {};
public var regionsOutput = new MovieClip();
public function Init(sheetWidth, sheetHeight, stage)
{
this.sheetWidth = sheetWidth;
this.sheetHeight = sheetHeight;
this.stage = stage;
}
protected var doneCallback = null;
protected var callbackParam;
public function ExportDefs(graphicDefs:Array, callback, callbackParam, outputFormat = "binary")
{
this.doneCallback = callback;
this.callbackParam = callbackParam;
this.outputFormat = outputFormat;
var clipCollection:GraphicDefClipCollection = new GraphicDefClipCollection(graphicDefs, GraphicDefsLoaded);
}
public function ExportClips(clips:Array, callback)
{
this.doneCallback = callback;
this.clips = clips;
var sheetSize:Point = this.DetermineMinimumSheetSizeForClips();
this.sheetWidth = sheetSize.x;
this.sheetHeight = sheetSize.y;
Export(false);
}
public function GraphicDefsLoaded(clips:Array)
{
this.clips = clips;
var sheetSize:Point = this.DetermineMinimumSheetSizeForClips();
this.sheetWidth = sheetSize.x;
this.sheetHeight = sheetSize.y;
this.Export(false);
}
public function AddClip(clip:MovieClip, scale=1, sizing:Point = null)
{
clip.scaleX = clip.scaleX * scale;
clip.scaleY = clip.scaleY * scale;
clips.push(clip);
clipSizing.push(sizing);
}
public function AddBitmapFont(font:BitmapFont)
{
fonts.push(font);
}
public function AddBitmapFontCopy(font:BitmapFont, toName:String)
{
fontCopies.push({from:font,to:toName});
}
protected var sheet = 0;
public function ExportFrom(clip, scale=1, save=true, callback=null)
{
this.doneCallback = callback;
for (var i = 0; i < clip.numChildren; i++)
{
if (clip.getChildAt(i) is MovieClip)
{
AddClip(clip.getChildAt(i), scale);
}
}
Export(save);
}
public static const CACHESEQUENCES = 0;
public static const PAD = 2;
public function GetSheets(){return sheets;}
public function GetOutput(){return output;}
var jsonPngSet:JSONPNGSet;
public function GetJSONPNGSet(){ return jsonPngSet; }
public var sheets:Array = new Array();
protected var sprites:Array = new Array();
protected var spriteOriginalClips:Array = new Array();
var output:ByteArray = new ByteArray();
var sorted:Array;
var saveExport = false;
public function Export(save = true)
{
sheetIndex = 0;
clipIndex = 0;
this.saveExport = save;
additionalData = {};
sheets = new Array();
sprites = new Array();
spriteOriginalClips = new Array();
var areas:Array = new Array();
for (var i in clips)
{
areas[i] = clips[i].width * clips[i].height;
}
sorted = areas.sort(Array.RETURNINDEXEDARRAY | Array.NUMERIC | Array.DESCENDING);
sheets.push(new SpriteSheet(sheetWidth, sheetHeight));
output = new ByteArray();
output.endian = Endian.LITTLE_ENDIAN;
DrawClipsToSheets();
}
var clipIndex = 0;
function DrawClipsToSheets()
{
var perPass = 5;
var start = clipIndex;
for (var i = start; i < clips.length && i <= start + perPass; i++)
{
var sprite:TextureSprite = new TextureSprite(clips[sorted[i]], CACHESEQUENCES);
CacheClip(clips[sorted[i]], sprite);
sprites.push(sprite);
spriteOriginalClips.push(clips[sorted[i]]);
_strace("cached clip "+i+" of "+clips.length);
clipIndex++;
}
if (clipIndex == clips.length)
{
DrawFontsToSheets();
EncodeToPNGAndWriteSheetsToOutput();
} else {
var t:SimpleTimer = new SimpleTimer(0.001, DrawClipsToSheets);
}
}
function DrawFontsToSheets()
{
/* Do all the Fonts at once, they are not slow.. */
var fontSpritesByName = {};
for (var i in fonts)
{
var sprite:TextureSprite = new TextureSprite(fonts[i], CACHESEQUENCES);
CopyBitmapFontToSingleSheet(fonts[i], sprite);
sprites.push(sprite);
spriteOriginalClips.push(fonts[i]);
additionalData[fonts[i].Name] = fonts[i].GetAdditionalData();
fontSpritesByName[fonts[i].Name] = sprite;
}
for (var i in fontCopies)
{
var f:BitmapFontCopy = new BitmapFontCopy(fontCopies[i].to, fontCopies[i].from.Size, fontCopies[i].from.Bold, fontCopies[i].from);
sprites.push(fontSpritesByName[fontCopies[i].from.Name]);
spriteOriginalClips.push(f);
additionalData[f.Name] = f.GetAdditionalData();
}
}
var sheetIndex = 0;
var pngSheets:Array;
function EncodeToPNGAndWriteSheetsToOutput()
{
if (sheetIndex == 0)
{
output.writeUnsignedInt(sheets.length);
}
pngSheets = new Array();
var perPass = 5;
var start = sheetIndex;
for (var i = start; i < sheets.length && i <= start + perPass; i++)
{
var s:SpriteSheet = sheets[i];
output.writeUnsignedInt(s.width);
output.writeUnsignedInt(s.height);
if (ExpandSpriteRegionEdgeAlphas) ExpandSpriteSheetEdgeAlpha(s);
var png:ByteArray = PNGExportHelper.GetExportPNG(s.GetBitmap());
output.writeUnsignedInt(png.length);
output.writeBytes(png);
pngSheets.push(png);
_strace("PNG Processed Sheet "+i+" of " + sheets.length);
sheetIndex++;
}
if (sheetIndex == sheets.length)
{
WriteSpriteInfoAndCompleteOutput();
} else {
var t:SimpleTimer = new SimpleTimer(0.001, EncodeToPNGAndWriteSheetsToOutput);
}
}
var jsonOutput;
function WriteSpriteInfoAndCompleteOutput()
{
for (var i = 0; i < sprites.length; i++)
{
var orig = spriteOriginalClips[i];
if (orig is BitmapFont && orig.AsBackupOnly)
{
sprites.splice(i, 1);
spriteOriginalClips.splice(i, 1);
i--;
}
}
var spriteNames = [];
output.writeUnsignedInt(sprites.length);
for (var i = 0; i < sprites.length; i++)
{
var tex:TextureSprite = sprites[i];
var orig = spriteOriginalClips[i];
var clipName = "clip";
if (orig is BitmapFont || orig is BitmapFontCopy)
{
clipName = orig.Name;
} else {
clipName = getQualifiedClassName(orig);
if (clipName == "flash.display::MovieClip")
{
clipName = orig.name;
}
}
spriteNames.push(clipName);
_strace("Exporting "+clipName);
output.writeUnsignedInt(clipName.length);
output.writeMultiByte(clipName, "us-ascii");
output.writeUnsignedInt(tex.NumSequences());
for (var j = 0; j < tex.NumSequences(); j++)
{
var info:SequenceInfo = tex.GetSequenceInfo(j);
output.writeUnsignedInt(info.Length);
_strace("With "+info.Length+" Frames");
for (var k = 0; k < info.Length; k++)
{
output.writeUnsignedInt(sheets.indexOf(info.FrameTextures[k]));
var location:Rectangle = info.FrameNodes[k].Rect;
output.writeFloat(location.x + PAD);
output.writeFloat(location.y + PAD);
output.writeFloat(location.width - (PAD * 2));
output.writeFloat(location.height - (PAD * 2));
var offset:Point = info.FrameOffsets[k];
output.writeFloat(offset.x - PAD);
output.writeFloat(offset.y - PAD);
regionsOutput.graphics.beginFill(0x00CCFF, .2);
regionsOutput.graphics.lineStyle(1, 0x00CCFF);
regionsOutput.graphics.drawRect(location.x + PAD, location.y + PAD, location.width - (PAD * 2), location.height - (PAD * 2));
regionsOutput.graphics.endFill();
}
}
}
//Create JSON output for JSON Save
var jsonSpriteData = {};
jsonSpriteData.format = "spritesheet";
jsonSpriteData.graphics = [];
for (var i = 0; i < sprites.length; i++)
{
var spriteData = {};
spriteData.name = spriteNames[i];
var tex:TextureSprite = sprites[i];
spriteData.sequences = [];
for (var j = 0; j < tex.NumSequences(); j++)
{
var seq = [];
var info:SequenceInfo = tex.GetSequenceInfo(j);
for (var k = 0; k < info.Length; k++)
{
var frame = {};
frame.texture = sheets.indexOf(info.FrameTextures[k]);
var location:Rectangle = info.FrameNodes[k].Rect;
frame.tx = location.x + PAD;
frame.ty = location.y + PAD;
frame.tw = location.width - (PAD * 2);
frame.th = location.height - (PAD * 2);
var offset:Point = info.FrameOffsets[k];
frame.x = offset.x - PAD;
frame.y = offset.y - PAD;
seq.push(frame);
}
spriteData.sequences.push(seq);
}
jsonSpriteData.graphics.push(spriteData);
}
//Convert to Texture Packer format.
var texturePackerJSON = {};
var texturePackerFrames = {};
for(var i = 0; i < jsonSpriteData.length; i++)
{
var spriteData = jsonSpriteData[i];
var seqs = spriteData.sequences;
for (var j = 0; j < seqs.length; j++)
{
var seq = seqs[j];
for (var k = 0; k < seq.length; k++)
{
var frame = {};
frame.filename = (seqs.length == 1 && seq.length == 1) ? spriteData.name : spriteData.name+"_"+j+"_"+k;
frame.frame = {x:seq[k].tx,y:seq[k].ty,w:seq[k].tw,h:seq[k].th};
frame.rotated = false;
frame.trimmed = false;
frame.spriteSourceSize = {x:0,y:0,w:seq[k].tw,h:seq[k].th};
frame.sourceSize = {w:seq[k].tw,h:seq[k].th};
frame.pivot = {x:seq[k].x/seq[k].tw,y:seq[k].y/seq[k].th};
texturePackerFrames[frame.filename] = frame;//.push(frame);
}
}
}
texturePackerJSON.meta = {image:spriteNames[0]+".png"};
texturePackerJSON.frames = texturePackerFrames;
var additionalDataString = com.adobe.serialization.json.JSON.encode(additionalData);
output.writeUnsignedInt(additionalDataString.length);
output.writeMultiByte(additionalDataString, "us-ascii");
if (saveExport && outputFormat == "binary")
{
var fr:FileReference = new FileReference();
fr.save(output, "data.pak");
}
_strace("done. num sheets: " + sheets.length);
if (outputFormat == "json_png_set")
{
var assetName = (outputAssetName == "") ? spriteNames[0] : outputAssetName;
jsonPngSet = new JSONPNGSet(assetName, jsonSpriteData, pngSheets);
}
if (this.doneCallback != null && callbackParam == null) this.doneCallback(outputFormat == "binary" ? output : jsonPngSet);
if (this.doneCallback != null && callbackParam != null) this.doneCallback(outputFormat == "binary" ? output : jsonPngSet, callbackParam);
}
/*
public function SaveAsFileBundle()
{
var fr:FileReference = new FileReference();
fr.save(jsonOutput, "sheet.json");
fr.addEventListener(Event.COMPLETE, OutputNextFile);
}
public function OutputNextFile(e:Event)
{
if (pngSheets.length > 0)
{
var pngSheet = pngSheets.shift();
var fr:FileReference = new FileReference();
fr.save(pngSheet, "sheet_.png");
fr.addEventListener(Event.COMPLETE, OutputNextFile);
}
}*/
/* This function expands the edges of the sprite rects.
This is important to prevent alpha bleeding when half pixels are sampled in engine */
public function ExpandSpriteSheetEdgeAlpha(s:SpriteSheet)
{
var bitmap:BitmapData = s.GetBitmap();
bitmap.lock();
var nodes = s.GetAllocator().GetLeafNodes();
for (var n = 0; n < nodes.length; n++)
{
var rect:Rectangle = nodes[n].Rect;
var padding = PAD;
//top & bottom
for (var i = 0; i < 2; i++)
{
var y = rect.y + padding;
if (i == 1) y = rect.y + rect.height - padding - 1;
var ySetOffset = i == 0 ? -1 : 1;
for (var x = rect.x; x < rect.x + rect.width; x++)
{
var val:uint = bitmap.getPixel32(x, y);
var a:uint = (val & 0xFF000000) >>> 24;
var rgb:uint = (val & 0xFFFFFF);
bitmap.setPixel32(x, y + ySetOffset, val);
if (a == 255)
{
bitmap.setPixel32(x, y + ySetOffset, (0xFF << 24) | rgb);
}
}
}
//left & right
for (var i = 0; i < 2; i++)
{
var x = rect.x + padding;
if (i == 1) x = rect.x + rect.width - padding - 1;
var xSetOffset = i == 0 ? -1 : 1;
for (var y = rect.y; y < rect.y + rect.height; y++)
{
var val:uint = bitmap.getPixel32(x, y);
var a:uint = (val & 0xFF000000) >>> 24;
var rgb:uint = (val & 0xFFFFFF);
bitmap.setPixel32(x + xSetOffset, y, val);
if (a == 255)
{
bitmap.setPixel32(x + xSetOffset, y, (0xFF << 24) | rgb);
}
}
}
}
bitmap.unlock();
}
public function DetermineMinimumSheetSizeForClips():Point
{
/* TODO - FOR LARGE SIZED CHUNKS BETTER HANDLING FOR FINDING MINIMUM SIZE! */
sheets = new Array();
sprites = new Array();
var areas:Array = new Array();
var max = 0;
for (var i in clips)
{
if (clips[i].width > max) max = clips[i].width;
if (clips[i].height > max) max = clips[i].height;
areas[i] = clips[i].width * clips[i].height;
}
var sorted:Array = areas.sort(Array.RETURNINDEXEDARRAY | Array.NUMERIC | Array.DESCENDING);
var exponent = Math.ceil(Math.log(max) / Math.log(2));
max = Math.pow(2, exponent);
//rough heuristic to see if this process is worth doing.
//it can be slow if there are a large number of big clips.
if (clips.length > 100 && max >= 512)
{
var s = Math.max(max, 1024);
return new Point(s, s);
}
sheets.push(new SpriteSheet(2048, 2048));
for (var i = 0; i < clips.length; i++)
{
var sprite:TextureSprite = new TextureSprite(clips[sorted[i]], CACHESEQUENCES);
CacheClip(clips[sorted[i]], sprite);
sprites.push(sprite);
}
var allUsedRects:Array = new Array();
var nodesToVisit:Array = new Array();
for (var i = 0; i < sheets.length; i++)
{
var sheet:SpriteSheet = sheets[i];
var nodes = sheet.GetAllocator().GetLeafNodes();
for (var n = 0; n < nodes.length; n++)
{
allUsedRects.push(nodes[n].Rect);
}
}
var sizeFound = false;
var sheetSizeX = 64;
var sheetSizeY = 64;
while(!sizeFound)
{
var sheet:SpriteSheet = new SpriteSheet(sheetSizeX, sheetSizeY);
sizeFound = true;
for(var i = 0; i < allUsedRects.length; i++)
{
var node:RectangleNode = sheet.GetAllocator().Insert(allUsedRects[i].width, allUsedRects[i].height);
if (node == null)
{
sizeFound = false;
break;
}
}
if (sizeFound == false)
{
if (sheetSizeX == sheetSizeY)
{
sheetSizeX *= 2;
}
else
{
sheetSizeY *= 2;
}
}
}
return new Point(sheetSizeX, sheetSizeY);
}
protected function CopyBitmapFontToSingleSheet(font:BitmapFont, sprite:TextureSprite)
{
sprite.SetSequence(0, font.CodepointData.length);
// info to go into the sprite
var offsets:Array = new Array();
var bounds:Array = new Array();
var nodes:Array = new Array();
var localSheets:Array = new Array();
// track the nodes we have locally created, if we can't fit
// everything on 1 sheet we will remove them
var createdNodes:Array = new Array();
var createdNewSheet = false;
var useSheets:Array = new Array();
for (var s = 0; s < this.sheets.length; s++)
{
useSheets.push(s);
}
for (var sheetIndex = 0; sheetIndex < useSheets.length; sheetIndex++)
{
var s = useSheets[sheetIndex];
var sheet:SpriteSheet = sheets[s];
if (!sheet.sheetFull)
{
var ok = true;
for (var i = 0; i < font.CodepointData.length; i++)
{
var node:RectangleNode = null;
var data:FontCodepointData = font.CodepointData[i];
if (font.AsBackupOnly && data.References == 0)
{
// do nothing
}
else if (data.FromOtherFont)
{
// this is a copy node, from a backup font
node = data.FromFont.CodepointData[data.FromFontIndex].DestinationNode;
}
else
{
node = sheet.GetAllocator().Insert(data.Data.width, data.Data.height);
if (node == null)
{
// failed to fit the entire font on a sheet, so let's clear
// everything and start again
ok = false;
for (var j in createdNodes)
{
sheet.GetBitmap().fillRect(createdNodes[j].Rect, 0x0);
}
sheet.GetAllocator().RemoveNodes(createdNodes, false);
nodes = new Array();
createdNodes = new Array();
break;
}
createdNodes.push(node);
}
nodes.push(node);
}
if (ok)
{
// the nodes array is completed (length == font.CodepointData.length)
// now we have to setup the sheet indices, bounds, offsets, etc...
for (var i = 0; i < font.CodepointData.length; i++)
{
var data:FontCodepointData = font.CodepointData[i];
if (font.AsBackupOnly && data.References == 0)
{
// do nothing
offsets.push(new Point());
bounds.push(new Rectangle());
localSheets.push(-1);
}
else
{
var node:RectangleNode = nodes[i];
if (!data.FromOtherFont)
{
sheet.GetBitmap().copyPixels(data.Data, data.Data.rect, new Point(node.Rect.x, node.Rect.y), null, null, true);
}
offsets.push(data.Offset);
bounds.push(node.Rect.clone());
localSheets.push(node.Host.HostTexture);
data.DestinationNode = node;
}
}
break;
}
else
{
if (s == sheets.length - 1)
{
if (createdNewSheet)
{
// couldn't fit the font on 1 sheet
CopyBitmapFontToSheets(font, sprite);
return;
}
else
{
createdNewSheet = true;
var newSheet:SpriteSheet = new SpriteSheet(sheetWidth, sheetHeight);
newSheet.SheetIndex = sheets.length;
useSheets.push(sheets.length);
sheets.push(newSheet);
}
}
}
}
}
for (var j = 0; j < nodes.length; j++)
{
sprite.AddFrame(0, j, offsets[j], bounds[j], localSheets[j], nodes[j], null);
}
sprite.Loaded = true;
}
protected function CopyBitmapFontToSheets(font:BitmapFont, sprite:TextureSprite)
{
sprite.SetSequence(0, font.CodepointData.length);
// info to go into the sprite
var offsets:Array = new Array();
var bounds:Array = new Array();
var nodes:Array = new Array();
var localSheets:Array = new Array();
var i = 0;
var useSheets:Array = new Array();
for (var s = 0; s < this.sheets.length; s++)
{
useSheets.push(s);
}
var sheetIndex = useSheets.length - 1;
var searchBackwards = true;
var done = false;
while (!done)
{
var s = useSheets[sheetIndex];
var sheet:SpriteSheet = sheets[s];
if (!sheet.sheetFull)
{
var count = 0;
var spaceAvailable = true;
while (spaceAvailable && i < font.CodepointData.length)
{
var data:FontCodepointData = font.CodepointData[i];
var node:RectangleNode = null;
var offset:Point = new Point();
if (font.AsBackupOnly && data.References == 0)
{
// do nothing, this sprite won't be used ever
}
else if (data.FromOtherFont)
{
var copyData:FontCodepointData = data.FromFont.CodepointData[data.FromFontIndex];
node = copyData.DestinationNode;
offset = copyData.Offset;
}
else
{
node = sheet.GetAllocator().Insert(data.Data.width, data.Data.height);
if (node == null)
{
spaceAvailable = false;
break;
}
offset = data.Offset;
sheet.GetBitmap().copyPixels(data.Data, data.Data.rect, new Point(node.Rect.x, node.Rect.y), null, null, true);
}
nodes.push(node);
offsets.push(offset);
bounds.push(node.Rect.clone());
localSheets.push(node.Host.HostTexture);
data.DestinationNode = node;
i++;
count++;
}
if (i < font.CodepointData.length)
{
if (searchBackwards)
{
sheetIndex--;
if (sheetIndex < 0)
{
searchBackwards = false;
sheetIndex = sheets.length - 1;
}
}
if (!searchBackwards)
{
var newSheet:SpriteSheet = new SpriteSheet(sheetWidth, sheetHeight);
newSheet.SheetIndex = sheets.length;
useSheets.push(sheets.length);
sheets.push(newSheet);
sheetIndex++;
}
}
else
{
done = true;
}
}
}
for (var j = 0; j < offsets.length; j++)
{
sprite.AddFrame(0, j, offsets[j], bounds[j], localSheets[j], nodes[j], null);
}
sprite.Loaded = true;
}
public static var UseFlatHeuristic = false;
private function AnalyzeClipType(clip:MovieClip):MovieClip
{
var isFlat = false;
if (clip.totalFrames > 1 && UseFlatHeuristic)
{
var framesWithMultiChildren = 0;
var totalChildren = 0;
var childrenWithSingleFrame = 0;
for (var i = 0; i < clip.totalFrames; i++)
{
clip.gotoAndStop(i);
var children = clip.numChildren;
totalChildren += children;
if (children > 1) framesWithMultiChildren++;
for (var j = 0; j < clip.numChildren; j++)
{
var child = clip.getChildAt(j);
if (!(child is MovieClip) || child.totalFrames == 1)
{
childrenWithSingleFrame++;
}
}
}
if (framesWithMultiChildren / clip.totalFrames > 0.75)
{
if (childrenWithSingleFrame / totalChildren > 0.75)
{
isFlat = true;
}
}
}
if (isFlat)
{
var ret:MovieClip = new MovieClip();
ret.addChild(clip);
return ret;
}
else
{
return clip;
}
}
protected function CacheClip(clip:MovieClip, sprite:TextureSprite)
{
clip = AnalyzeClipType(clip);
var totalFrames = clip.totalFrames;
var startSeq = 1;
var endSeq = totalFrames;
for (var i = startSeq; i <= endSeq; i++)
{
var endFrame = 1;
RecursivelyStop(clip, i);
for (var j = 0; j < clip.numChildren; j++)
{
var child = clip.getChildAt(j);
if (child is MovieClip)
{
if (child.totalFrames > endFrame) endFrame = child.totalFrames;
}
}
var offsets:Array = new Array();
var bounds:Array = new Array();
var nodes:Array = new Array();
var localSheets:Array = new Array();
var completed = false;
var startFrame = 1;
sprite.SetSequence(i - 1, endFrame);
var origStart = startFrame;
var useSheets:Array = new Array();
for (var s = 0; s < this.sheets.length; s++)
{
useSheets.push(s);
}
for (var sheetIndex = 0; sheetIndex < useSheets.length; sheetIndex++)
{
var s = useSheets[sheetIndex];
var sheet:SpriteSheet = sheets[s];
if (!sheet.sheetFull)
{
var count = 0;
count = CacheFrames(clip, offsets, bounds, sheet, nodes, startFrame, endFrame);
if (count > 0)
{
for (var q = 0; q < count; q++)
{
localSheets.push(sheet);
}
}
startFrame += count;
if (startFrame <= endFrame && s == sheets.length - 1)
{
var newSheet:SpriteSheet = new SpriteSheet(sheetWidth, sheetHeight);
newSheet.SheetIndex = sheets.length;
useSheets.push(sheets.length);
sheets.push(newSheet);
}
}
}
if (localSheets.length < endFrame - startFrame + 1)
{
_strace("GraphicPacker: Problem caching");
}
for (var j = 0; j < offsets.length; j++)
{
sprite.AddFrame(i - 1, j + origStart - 1, offsets[j], bounds[j], localSheets[j], nodes[j], null);
}
}
sprite.Loaded = true;
}
protected function CacheFrames(clip:MovieClip, offsets:Array, bounds:Array, sheet:SpriteSheet, nodes:Array, startFrame = 1, lastFrame = -1)
{
if (lastFrame == -1) lastFrame = clip.totalFrames;
for (var i = startFrame; i <= lastFrame; i++)
{
SubclipsGotoFrame(clip, i, {}, clip);
RecursivelyStop(clip);
var currentMatrix = clip.transform.matrix.clone();
var flippedX = currentMatrix.a < 0;
var flippedY = currentMatrix.d < 0;
var m:Matrix = new Matrix(currentMatrix.a, 0, 0, currentMatrix.d);
var r:Rectangle = clip.getBounds(clip);
r = CacheManager.Instance().GetAccurateBounds(clip, 2048);
var usedCacheBounds = false;
if (clip.getChildByName("__cache_bounds__"))
{
usedCacheBounds = true;
r = clip.getChildByName("__cache_bounds__").getRect(clip);
clip.mask = clip.getChildByName("__cache_bounds__");
}
r.x *= Math.abs(m.a);
r.y *= Math.abs(m.d);
r.width *= Math.abs(m.a);
r.height *= Math.abs(m.d);
r.width += PAD * 2.0;
r.height += PAD * 2.0;
var w = Math.ceil(r.width);
var h = Math.ceil(r.height);
var node:RectangleNode = sheet.GetAllocator().Insert(w, h);
if (node == null)
{
return i - startFrame;
}
nodes.push(node);
var target:Point = new Point(node.Rect.x, node.Rect.y);
//flippedX = false;
var offsetX = flippedX ? Math.ceil(r.right - PAD * 2.0) : Math.ceil(-r.left);
var offsetY = flippedY ? Math.ceil(r.bottom - PAD * 2.0) : Math.ceil(-r.top);
m.translate(offsetX + target.x + PAD, offsetY + target.y + PAD);
var offset:Point = new Point(offsetX + PAD, offsetY + PAD);
RenderBitmap(sheet, clip, m, new ColorTransform(1.0, 1.0, 1.0, clip.alpha));
offsets.push(offset);
bounds.push(node.Rect.clone());
if (usedCacheBounds)
{
clip.mask = null;
}
}
return lastFrame - startFrame + 1;
}
protected function RenderBitmap(sheet:SpriteSheet, clip:MovieClip, m:Matrix, ct:ColorTransform)
{
sheet.GetBitmap().draw(clip, m, ct);
}
protected function SubclipsGotoFrame(clip:MovieClip, i, force, rootClip, exceptClip = null)
{
for (var j = 0; j < clip.numChildren; j++)
{
var child = clip.getChildAt(j);
if (child is MovieClip && child != exceptClip)
{
if (i != null) child.gotoAndStop(((i - 1) % child.totalFrames) + 1);
SubclipsGotoFrame(child, i, force, rootClip);
}
}
}
protected function RecursivelyStop(clip, frame = null)
{
if (clip is MovieClip)
{
if (frame == null)
{
clip.stop();
} else {
clip.gotoAndStop(frame);
}
}
if (clip is DisplayObjectContainer)
{
for (var i = 0; i < clip.numChildren; i++)
{
RecursivelyStop(clip.getChildAt(i));
}
}
}
}
}