textureAtlas
{
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Rectangle;
import flash.text.AntiAliasType;
import flash.text.Font;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.getQualifiedClassName;
import starling.text.BitmapFont;
import starling.textures.Texture;
import starling.textures.TextureAtlas;
import starling.text.TextField;
import com.emibap.textureAtlas.TextureItem;
/**
* DynamicAtlas.as
* https://github.com/emibap/Dynamic-Texture-Atlas-Generator
* @author Emibap (Emiliano Angelini) - http://www.emibap.com
* Contribution by Thomas Haselwanter -
https://github.com/thomashaselwanter
* Most of this comes thanks to the inspiration (and code) of Thibault Imbert
(http://www.bytearray.org) and Nicolas Gans (http://www.flashxpress.net/)
*
* Dynamic Texture Atlas and Bitmap Font Generator (Starling framework
Extension)
* ========
*
* This tool will convert any MovieClip containing Other MovieClips, Sprites
or Graphics into a starling Texture Atlas, all in runtime.
* It can also register bitmap Fonts from system or embedded regular fonts.
* By using it, you won't have to statically create your spritesheets or
fonts. For instance, you can just take a regular MovieClip containing all the
display objects you wish to put into your Altas, and convert everything from
vectors to bitmap textures.
* Or you can select which font (specifying characters) you'd like to
register as a Bitmap Font, using a string or passing a Regular TextField as a
parameter.
* This extension could save you a lot of time specially if you'll be coding
mobile apps with the [starling framework](http://www.starling-framework.org/).
*
* # version 1.0 #
* - Added the checkBounds parameter to scan the clip prior the rasterization
in order to get the bounds of the entire MovieClip (prevent scaling in some cases).
Thank you Aymeric Lamboley.
* - Added the fontCustomID parameter to the Bitmap font creation. Thank you
Regan.
*
* ### Features ###
*
* * Dynamic creation of a Texture Atlas from a MovieClip
(flash.display.MovieClip) container that could act as a sprite sheet, or from a
Vector of Classes
* * Filters made to the objects are captured
* * Color transforms (tint, alpha) are optionally captured
* * Scales the objects (and also the filters) to a specified value
* * Automatically detects the objects bounds so you don't necessarily have
to set the registration points to TOP LEFT
* * Registers Bitmap Fonts based on system or embedded fonts from strings or
from good old Flash TextFields
*
* ### TODO List ###
*
* * Further code optimization
* * A better implementation of the Bitmap Font creation process
* * Documentation (?)
*
* ### Whish List ###
* * Optional division of the process into small intervals (for smooth
performance of the app)
*
* ### Usage ###
*
* You can use the following static methods (examples at the gitHub Repo):
*
* [Texture Atlas creation]
* - DynamicAtlas.fromMovieClipContainer(swf:flash.display.MovieClip,
scaleFactor:Number = 1, margin:uint=0, preserveColor:Boolean =
true):starling.textures.TextureAtlas
* - DynamicAtlas.fromClassVector(assets:Vector.<Class>,
scaleFactor:Number = 1, margin:uint=0, preserveColor:Boolean =
true):starling.textures.TextureAtlas
*
* [Bitmap Font registration]
* - DynamicAtlas.bitmapFontFromString(chars:String, fontFamily:String,
fontSize:Number = 12, bold:Boolean = false, italic:Boolean = false,
charMarginX:int=0):void
* - DynamicAtlas.bitmapFontFromTextField(tf:flash.text.TextField,
charMarginX:int=0):void
*
* Enclose inside a try/catch for error handling:
* try {
* var atlas:TextureAtlas =
DynamicAtlas.fromMovieClipContainer(mc);
* } catch (e:Error) {
* trace("There was an error in the creation of the
texture Atlas. Please check if the dimensions of your clip exceeded the maximun
allowed texture size. -", e.message);
* }
*
* History:
* -------
* # version 0.9.5 #
* - Added the fromClassVector static function. Thank you Thomas Haselwanter
*
* # version 0.9 #
* - Added Bitmap Font creation support
* - Scaling also applies to filters.
* - Added Margin and PreserveColor Properties
*
* # version 0.8 #
* - Added the scaleFactor constructor parameter. Now you can define a custom
scale to the final result.
* - Scaling also applies to filters.
* - Added Margin and PreserveColor Properties
*
* # version 0.7 #
* First Public version
**/
// Private methods
var itm:TextureItem;
/**
* isEmbedded
*
* @param fontFamily:Boolean - The name of the Font
* @return Boolean - True if the font is an embedded one
*/
static protected function isEmbedded(fontFamily:String):Boolean
{
var embeddedFonts:Vector.<Font> =
Vector.<Font>(Font.enumerateFonts());
realBounds.offset(bounds.x, bounds.y);
realBounds.width = Math.max(realBounds.width, 1);
realBounds.height = Math.max(realBounds.height, 1);
tmpBData = null;
return realBounds;
}
/**
* drawItem - This will actually rasterize the display object passed as
a parameter
* @param clip
* @param name
* @param baseName
* @param clipColorTransform
* @param frameBounds
* @return TextureItem
*/
static protected function drawItem(clip:DisplayObject, name:String =
"", baseName:String = "", clipColorTransform:ColorTransform = null,
frameBounds:Rectangle=null):TextureItem
{
var realBounds:Rectangle = getRealBounds(clip);
if (frameBounds) {
realBounds.x = frameBounds.x - realBounds.x;
realBounds.y = frameBounds.y - realBounds.y;
realBounds.width = frameBounds.width;
realBounds.height = frameBounds.height;
}
_items.push(item);
_canvas.addChild(item);
_bData = null;
return item;
}
// Public methods
/**
* This method takes a vector of DisplayObject class and converts it into a
Texture Atlas.
*
* @param assets:Vector.<Class> - The DisplayObject classes you wish to
convert into a TextureAtlas. Must contain classes whose instances are of type
DisplayObject that will be rasterized and become the subtextures of your Atlas.
* @param scaleFactor:Number - The scaling factor to apply to every object.
Default value is 1 (no scaling).
* @param margin:uint - The amount of pixels that should be used as the
resulting image margin (for each side of the image). Default value is 0 (no
margin).
* @param preserveColor:Boolean - A Flag which indicates if the color
transforms should be captured or not. Default value is true (capture color
transform).
* @param checkBounds:Boolean - A Flag used to scan the clip prior
the rasterization in order to get the bounds of the entire MovieClip. By default is
false because it adds overhead to the process.
* @return TextureAtlas - The dynamically generated Texture Atlas.
*/
static public function fromClassVector(assets:Vector.<Class>,
scaleFactor:Number = 1, margin:uint=0, preserveColor:Boolean = true,
checkBounds:Boolean=false):TextureAtlas
{
var container:MovieClip = new MovieClip();
for each (var assetClass:Class in assets) {
var assetInstance:DisplayObject = new assetClass();
assetInstance.name = getQualifiedClassName(assetClass);
container.addChild(assetInstance);
}
return fromMovieClipContainer(container, scaleFactor, margin,
preserveColor, checkBounds);
}
/**
* This method will take a MovieClip sprite sheet (containing other
display objects) and convert it into a Texture Atlas.
*
* @param swf:MovieClip - The MovieClip sprite sheet you wish to
convert into a TextureAtlas. I must contain named instances of every display object
that will be rasterized and become the subtextures of your Atlas.
* @param scaleFactor:Number - The scaling factor to apply to every
object. Default value is 1 (no scaling).
* @param margin:uint - The amount of pixels that should be used as
the resulting image margin (for each side of the image). Default value is 0 (no
margin).
* @param preserveColor:Boolean - A Flag which indicates if the color
transforms should be captured or not. Default value is true (capture color
transform).
* @param checkBounds:Boolean - A Flag used to scan the clip prior
the rasterization in order to get the bounds of the entire MovieClip. By default is
false because it adds overhead to the process.
* @return TextureAtlas - The dynamically generated Texture Atlas.
*/
static public function fromMovieClipContainer(swf:Sprite,
scaleFactor:Number = 1, margin:uint=0, preserveColor:Boolean = true,
checkBounds:Boolean=false):TextureAtlas
{
var parseFrame:Boolean = false;
var selected:DisplayObject;
var selectedTotalFrames:int;
var selectedColorTransform:ColorTransform;
var frameBounds:Rectangle = new Rectangle(0, 0, 0, 0);
var canvasData:BitmapData;
var texture:Texture;
var xml:XML;
var subText:XML;
var atlas:TextureAtlas;
var itemsLen:int;
var itm:TextureItem;
var m:uint;
_margin = margin;
_preserveColor = preserveColor;
_items = [];
if (!_canvas)
_canvas = new Sprite();
if(swf is MovieClip)
MovieClip(swf).gotoAndStop(1);
selected.scaleX *= scaleFactor;
selected.scaleY *= scaleFactor;
if (selected.filters.length > 0)
{
var filters:Array = selected.filters;
var filtersLen:int = selected.filters.length;
var filter:Object;
for (var j:uint = 0; j < filtersLen; j++)
{
filter = filters[j];
if (filter.hasOwnProperty("blurX"))
{
filter.blurX *= scaleFactor;
filter.blurY *= scaleFactor;
}
if (filter.hasOwnProperty("distance"))
{
filter.distance *= scaleFactor;
}
}
selected.filters = filters;
}
}
_currentLab = "";
layoutChildren();
itemsLen = _items.length;
itm.graphic.dispose();
// xml
subText = new XML(<SubTexture />);
subText.@name = itm.textureName;
subText.@x = itm.x;
subText.@y = itm.y;
subText.@width = itm.width;
subText.@height = itm.height;
subText.@frameX = itm.frameX;
subText.@frameY = itm.frameY;
subText.@frameWidth = itm.frameWidth;
subText.@frameHeight = itm.frameHeight;
if (itm.frameName != "")
subText.@frameLabel = itm.frameName;
xml.appendChild(subText);
}
texture = Texture.fromBitmapData(canvasData);
atlas = new TextureAtlas(texture, xml);
_items.length = 0;
_canvas.removeChildren();
_items = null;
xml = null;
_canvas = null;
_currentLab = null;
//_x = _y = _margin = null;
return atlas;
}
/**
* This method will register a Bitmap Font based on each char that
belongs to a String.
*
* @param chars:String - The collection of chars which will become
the Bitmap Font
* @param fontFamily:String - The name of the Font that will be
converted to a Bitmap Font
* @param fontSize:Number - The size in pixels of the font.
* @param bold:Boolean - A flag indicating if the font will be
rasterized as bold.
* @param italic:Boolean - A flag indicating if the font will be
rasterized as italic.
* @param charMarginX:int - The number of pixels that each character
should have as horizontal margin (negative values are allowed). Default value is 0.
* @param fontCustomID:String - A custom font family name indicated
by the user. Helpful when using differnt effects for the same font. [Optional]
*/
static public function bitmapFontFromString(chars:String,
fontFamily:String, fontSize:Number = 12, bold:Boolean = false, italic:Boolean =
false, charMarginX:int=0, fontCustomID:String=""):void {
var format:TextFormat = new TextFormat(fontFamily, fontSize,
0xFFFFFF, bold, italic);
var tf:flash.text.TextField = new flash.text.TextField();
tf.autoSize = TextFieldAutoSize.LEFT;
tf.defaultTextFormat = format;
tf.text = chars;
/**
* This method will register a Bitmap Font based on each char that
belongs to a regular flash TextField, rasterizing filters and color transforms as
well.
*
* @param tf:flash.text.TextField - The textfield that will be used
to rasterize every char of the text property
* @param charMarginX:int - The number of pixels that each character
should have as horizontal margin (negative values are allowed). Default value is 0.
* @param fontCustomID:String - A custom font family name indicated
by the user. Helpful when using differnt effects for the same font. [Optional]
*/
static public function bitmapFontFromTextField(tf:flash.text.TextField,
charMarginX:int=0, fontCustomID:String=""):void {
var charCol:Vector.<String> = Vector.<String>(tf.text.split(""));
var format:TextFormat = tf.defaultTextFormat;
var fontFamily:String = format.font;
var fontSize:Object = format.size;
var oldAutoSize:String = tf.autoSize;
tf.autoSize = TextFieldAutoSize.LEFT;
var canvasData:BitmapData;
var texture:Texture;
var xml:XML;
var myChar:String;
_margin = 0;
_preserveColor = true;
_items = [];
var itm:TextureItem;
var itemsLen:int;
_currentLab = "";
layoutChildren();
itemsLen = _items.length;
itm.graphic.dispose();
// xml
charNode = new XML(<char page="0" xoffset="0"
yoffset="0"/>);
charNode.@id = itm.textureName;
charNode.@x = itm.x;
charNode.@y = itm.y;
charNode.@width = itm.width;
charNode.@height = itm.height;
charNode.@xadvance = itm.width + 2*charMarginX;
charsNode.appendChild(charNode);
}
xml.appendChild(charsNode);
texture = Texture.fromBitmapData(canvasData);
TextField.registerBitmapFont(new BitmapFont(texture, xml));
_items.length = 0;
_canvas.removeChildren();
tf.autoSize = oldAutoSize;
tf.text = charCol.join();
_items = null;
xml = null;
_canvas = null;
_currentLab = null;
}