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