Adobe has released a great little framework called the Starling Framework. This framework is based on the iOS game engine Sparrow Framework.
The Sparrow framework replicated the flash environment pretty close in naming conventions and structure. Basically the Sparrow framework is a port of flash to iOS. Now the iOS flash port has been ported to flash, funnily enough…
The Starling framework uses the molehill API to give a 2D engine that is powered by the GPU by placing the assets on planes in 3D and simulating a 2D screen, thus gaining from the GPU rendering of molehill.
Both Sparrow and Starling uses sprite sheets and bitmaps, and these are mapped using a texture XML. This data format is specific to Sparrow, and used again in Starling.
Lee Brimelow has a great video course on how to use Texture Packer to create a png and texure XML here:
http://gotoandlearn.com/play.php?id=147
To decrease the file size of the packages I wipped up a little library that can convert an XML and PNG file to a binary file and then back again. By deflating the XML and the PNG together to binary form you significally decrease the file size as well as you get one less file to load (one file holds both data).
One of the best advantages with this is that the XML in binary form doesn’t take up us much data as it does in it’s verbose string form.
The package consists of 2 files, BytesSprite and SparrowData. SparrowData is only used when inflating a byte array back to XML and PNG.
It should be pretty straightforward to use: The ByteSprite class has 2 functions, inflate and deflate.
This example will convert a PNG and XML to binary form, and then back again using this class:
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.utils.ByteArray;
import mx.core.ByteArrayAsset;
import se.mowday.utils.ByteSprite;
import se.mowday.utils.SparrowData;
public class Main extends Sprite
{
// The texture XML data
[Embed(source='../asset/sp.xml', mimeType='application/octet-stream')]
private var data:Class;
// The PNG sprite image
[Embed(source='../asset/sp.png')]
private var png:Class
public function Main():void
{
var requestHeaderSize:int = 1200;
var responseHeaderSize:int = 300;
var perFile:int = (requestHeaderSize + responseHeaderSize) * 0.0078125;
perFile = 0;
var data:ByteArray = ByteSprite.deflate(Bitmap(new png()).bitmapData, XML(new data()));
var oldSum:int = (perFile * 2 +105);
var newSum:int = (perFile + convert(data.length));
trace("Original file: 105kb");
trace("Original header size: " + (perFile * 2) + "kb");
trace("Sum original = " + oldSum + "kb");
trace("------------------------");
trace("New file: " + convert(data.length) + "kb");
trace("New header size: " + (perFile) + "kb");
trace("Sum new = " + newSum + "kb");
trace("------------------------");
trace("Gain = " + (oldSum - newSum) + "kb - " + (100 - Math.round(newSum / oldSum * 100)) + " %");
var newData:SparrowData = ByteSprite.inflate(data);
addChild(newData.png);
}
private function convert(bytes:Number):Number
{
return bytes * 0.0009765625;
}
}
}
That will output a trace that looks like this:
Original file: 105kb
Original header size: 0kb
Sum original = 105kb
————————
New file: 88.7607421875kb
New header size: 0kb
Sum new = 88kb
————————
Gain = 17kb – 16 %
When inflating a binary byte array, get the XML and PNG from the SparrowData and simply pass this on to the StarlingFramework as would usually do (like in Lee’s example).
To celebrate this I have also created a little helper app. This is a real simple AIR app that uses command line arguments to convert files back and forth.
The command line arguments are:
- -xml {path} defines the path to XML file
- -bin {path} defines the path to binary file
- -png {path} defines the path to PNG file
- -log {path} defines the path to the log file (OPTIONAL)
- -inflate inflates the binary file to an XML and a PNG file (overwrites previous files!)
- -deflate deflates the XML and PNG files into a binary file and saves it to disk
- -x exits the application on successful completion(OPTIONAL)
- -e exits the application on failing to convert(OPTIONAL)
Now to use this application is also pretty straight forward, just invoked the .exe file with the arguments and settings you want. In my case I have installed to air app to:
C:\Program Files\StarlingHelper
So to run the application on my machine (using Windows 7) I would run a command line prompt in the folder where the files are and run (for example):
“C:\Program Files\StarlingHelper\StarlingHelper.exe” -x -e -xml myXML.xml -png myPNG.png -bin myBin.ms -log log.txt -deflate
That command will deflate the XML and PNG files into a binary file, exit on completion or error and log everything.
To inflate the files back to XML and PNG again, all you need to do is:
“C:\Program Files\StarlingHelper\StarlingHelper.exe” -x -e -xml backXML.xml -png backPNG.png -bin myBin.ms -log log.txt -inflate
And 2 new files, backXML.xml and backPNG.png will be created for you.
Here is the ByteSprite.as class:
Show ▼
/*
Copyright (c) 2011, Carl Mowday
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Carl Mowday nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package se.mowday.utils
{
import flash.display.BitmapData;
import flash.utils.ByteArray;
/**
* This class converts "sparrow data formatted" files
* into one single binary file. This will reduce file size
* by concatinating the information from the two files into one file
* in binary form. This itself reduces alot of the file size as a
* result of moving to binary from the verbose XML syntax. <br />
* <br />
* The 2 functions in this class, <code>deflate</code> and <code>inflate</code>
* are eachothers opposites.<br />
* <code>deflate</code> will take an image file in the form of BitmapData as well
* as the texture XML with it and convert and return a bytearray containing the
* information of the two files in binary form. <br />
* <code>inflate</code> does the opposite. It converts a bytearray into a <code>SparrowData</code>
* instance that holds both the png (as a bitmap) and the texture XML.<br />
* <br />
* The data is stored like this<br />
* <code>
* NUMBER_OF_ITEMS_IN_XML
*
* LOOP_NUMBER_OF_ITEMS_IN_XML
* {
* NAME_LENGTH | NAME | X | Y | WIDTH | HEIGHT | [ FRAME_X | FRAME_Y | FRAME_WIDTH | FRAME_HEIGHT | ]
* }
*
* PNG_WIDTH | PNG_HEIGHT | PNG_DATA
* </code>
* @author Carl Mowday
*/
public class ByteSprite
{
/**
* Converts an image file and it's texture XML into a
* binary representation of the 2 files.
* @param png The bitmapdata of the image file, ususally a PNG file
* @param data The texture XML that comes with the image data
* @return A binary representation of the 2 files (compressed!)
*/
static public function deflate(png:BitmapData, data:XML):ByteArray
{
var ba:ByteArray = new ByteArray();
var tempBA:ByteArray = new ByteArray();
var subTexture:XMLList = data.SubTexture;
ba.writeShort(subTexture.length());
for each(var subtxt:XML in subTexture)
{
tempBA.clear();
tempBA.writeUTFBytes(subtxt.@name);
ba.writeShort(tempBA.length);
ba.writeBytes(tempBA);
ba.writeDouble(subtxt.@x);
ba.writeDouble(subtxt.@y);
ba.writeDouble(subtxt.@width);
ba.writeDouble(subtxt.@height);
if (subtxt.@frameX != undefined && subtxt.@frameY != undefined)
{
ba.writeBoolean(true);
ba.writeDouble(subtxt.@frameX);
ba.writeDouble(subtxt.@frameY);
ba.writeDouble(subtxt.@frameWidth);
ba.writeDouble(subtxt.@frameHeight);
}
else
{
ba.writeBoolean(false);
}
}
ba.writeDouble(png.width);
ba.writeDouble(png.height);
for (var x:int = 0; x < png.width; x++)
{
for (var y:int = 0; y < png.height; y++)
{
ba.writeUnsignedInt(png.getPixel32(x, y));
}
}
ba.compress();
return ba;
}
/**
* Converts a binary bytearray into a png and a texture XML
* ala the "sparrow data format".
* @param data The binary representation of the png containing
* the sprites and the texture XML
* @return The texture XML and the png contained in a SparrowData
* instance.
*/
static public function inflate(data:ByteArray):SparrowData
{
data.uncompress();
data.position = 0;
var xml:String = new String();
xml = "<TextureAtlas>\n";
var itemsLength:int = data.readShort();
for (var i:int = 0; i < itemsLength; i++)
{
var nameLength:int = data.readShort();
var name:String = data.readUTFBytes(nameLength);
xml += "\t<SubTexture name='" + name + "' ";
xml += "x='" + data.readDouble() + "' y='" + data.readDouble() +"' ";
xml += "width='" + data.readDouble() + "' height='" + data.readDouble() + "' ";
if (data.readBoolean())
{
xml += "frameX='" + data.readDouble() + "' ";
xml += "frameY='" + data.readDouble() + "' ";
xml += "frameWidth='" + data.readDouble() + "' ";
xml += "frameHeight='" + data.readDouble() + "' ";
}
xml += "/>\n";
}
xml += "</TextureAtlas>";
var pngWidth:Number = data.readDouble();
var pngHeight:Number = data.readDouble();
var pngData:ByteArray = new ByteArray();
data.readBytes(pngData);
var sd:SparrowData = new SparrowData(
pngData,
pngWidth,
pngHeight,
new XML(xml));
return sd;
}
}
}
And the SparrowData class:
Show ▼
/*
Copyright (c) 2011, Carl Mowday
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Carl Mowday nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package se.mowday.utils
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.utils.ByteArray;
/**
* This class holds the output of the ByteSprite class. This includes
* the png bitmap that holds the sprites as well as the texture XML
* that comes with it.<br />
* <br />
* There are 2 ways of accessing the png image. <br />
* 1. Simply get the <code>png</code> member. If the png member
* is empty (null) the class will generate a bitmap data object by looping
* all of the pixels in the byte array, generate a bitmapdata and then
* creating a bitmap object based on the bitmapdata.<br />
* 2. Invoked the <code>initiate</code> function. This acts as an
* asynchronicle start of loading the png. This method will create
* a Loader object, and then pass the bytes directly to that object.
* When the loader has finished loading the bitmap is retrieved from
* the content and then used. When this is done a <code>Event.COMPLETE</code>
* event will be dispatched, and the png member is ready to be used.
* @see se.mowday.utils.ByteSprite
* @author Carl Mowday
*/
public class SparrowData extends EventDispatcher
{
private var _png:Bitmap;
private var _pngData:ByteArray;
private var _data:XML;
private var _height:Number;
private var _width:Number;
private var _loader:Loader;
public function SparrowData(pngData:ByteArray, width:Number, height:Number, data:XML)
{
_data = data;
_width = width;
_height = height;
_pngData = pngData;
}
/**
* Starts an asynchronicle loading of the png file. When completed
* the instance will dispatch an <code>Event.COMPLETE</code> event.
*/
public function initiate():void
{
_loader = new Loader();
_loader.contentLoaderInfo.addEventListener(Event.INIT, onInit);
_loader.loadBytes(_pngData);
}
/**
* Invoked when the loader has completed loading
* the bytes and the content can be retrieved
*/
private function onInit(e:Event):void
{
_loader.contentLoaderInfo.removeEventListener(Event.INIT, onInit);
_png = Bitmap(_loader.content);
dispatchEvent(new Event(Event.COMPLETE));
_loader = null;
}
/**
* Checks if the png image is ready to be retrieved
* or not
*/
public function get ready():Boolean
{
return png != null;
}
/**
* The texture XML that comes with the sprite map
*/
public function get data():XML
{
return _data;
}
/**
* The png image (the sprite map)
*/
public function get png():Bitmap
{
if (_png == null)
{
var bmd:BitmapData = new BitmapData(_width, _height);
for (var x:int = 0; x < _width; x++)
{
for (var y:int = 0; y < _height; y++)
{
bmd.setPixel32(x, y, _pngData.readUnsignedInt());
}
}
}
_png = new Bitmap(bmd);
return _png;
}
/**
* The raw png data
*/
public function get pngData():ByteArray
{
return _pngData;
}
}
}
These are all the files you need, the first is the project that contains the ByteSprite and SparrowData class, and the second is the AIR application. Have fun!
Download: starling_lib (113.12KB)
Added: 26/09/2011
Description:
The Flashdevelop project for the "Starling lib"
Download: StarlingHelper (278.92KB)
Added: 26/09/2011
Description:
The AIR application for the Starling helper project