package org.cdmckay.utils
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.net.URLRequest;
import flash.utils.Dictionary;
/**
* Creates Sprites from previously loaded in Bitmaps.
* You may create any number of Sprites from a single Bitmap.
*
* @example The following code shows basic usage:
*
* var factory:SpriteFactory = new SpriteFactory();
* factory.loadBitmap("tree", "tile-tree.png");
*
* // Create a tree sprite with its origin at the top-left corner.
* var tree:Sprite = factory.newInstance("tree");
*
*
* @example The following code shows advanced usage:
*
* var factory:SpriteFactory = new SpriteFactory("assets/sprites");
* factory.loadBitmap("tree", "tile-tree.png");
*
* // Create a tree sprite with its origin at the middle.
* var tree:Sprite = factory.newInstance("tree",
* SpriteFactory.HALIGN_CENTER | SpriteFactory.VALIGN_MIDDLE);
*
*
* Copyright (c) 2010, Cameron McKay
* 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 the 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 CAMERON MCKAY 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.
*
* @author Cameron McKay
*/
public class SpriteFactory
{
public static const HALIGN_LEFT:uint = 1;
public static const HALIGN_CENTER:uint = 2;
public static const HALIGN_RIGHT:uint = 4;
public static const VALIGN_TOP:uint = 8;
public static const VALIGN_MIDDLE:uint = 16;
public static const VALIGN_BOTTOM:uint = 32;
private var _path:String;
private var _bitmaps:Dictionary = new Dictionary();
private var _queues:Dictionary = new Dictionary();
/**
* Create a new sprite factory that reads bitmaps
* from an optional path.
*
* @param path The path to look for bitmaps.
*/
public function SpriteFactory(path:String = "") {
_path = path;
}
/**
* Create a new sprite using the given bitmap id.
*
* @param id The id used to load the bitmap.
* @param alignment The alignment of the bitmap relative to sprite origin.
* @return A new sprite with bitmap in it.
*/
public function newSprite(id:String, alignment:uint = 0):Sprite {
if (!_bitmaps.hasOwnProperty(id)) {
throw new ArgumentError("Bitmap id not found: " + id);
}
var sprite:Sprite = new Sprite();
var bitmap:Bitmap = _bitmaps[id];
if (bitmap === null) {
_queues[id] = _queues[id] || [];
_queues[id].push({
sprite: sprite,
alignment: alignment
});
}
else {
var clone:Bitmap = new Bitmap(bitmap.bitmapData);
var offset:Point = calculateOffset(alignment, bitmap);
clone.x = offset.x;
clone.y = offset.y;
sprite.addChild(clone);
}
return sprite;
}
/**
* Load a bitmap from a file.
*
* @param id The unique identifier to be used when creating a sprite.
* @param filename The name of the file.
*/
public function loadBitmap(id:String, filename:String):void {
if (_bitmaps.hasOwnProperty(id)) {
throw new ArgumentError("Bitmap id must be unique: " + id);
}
_bitmaps[id] = null;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void {
var bitmap:Bitmap = e.target.content;
var bitmapData:BitmapData = bitmap.bitmapData;
trace("Finished loading " + id);
var queue:Array = _queues[id];
if (queue && queue.length > 0) {
for (var i:int = 0; i < queue.length; i++) {
var o:Object = queue[i];
var clone:Bitmap = new Bitmap(bitmapData);
var offset:Point = calculateOffset(o.alignment, clone);
clone.x = offset.x;
clone.y = offset.y;
o.sprite.addChild(clone);
}
delete _queues[id];
}
});
trace("Started loading " + id);
var url:String = _path + "/" + filename;
loader.load(new URLRequest(url));
}
/**
* Calculate the Cartesian offsets needed to align
* a bitmap according to a given alignment.
*
* @param alignment An alignment mask.
* @param bitmap The bitmap that is being aligned.
* @return The point that the top-left corner of the bitmap should be.
*/
private function calculateOffset(alignment:uint, bitmap:Bitmap):Point {
var offset:Point = new Point(0, 0);
if ((alignment & HALIGN_CENTER) === HALIGN_CENTER) {
offset.x = -bitmap.width / 2;
}
else if ((alignment & HALIGN_RIGHT) === HALIGN_RIGHT) {
offset.x = -bitmap.width;
}
if ((alignment & VALIGN_MIDDLE) === VALIGN_MIDDLE) {
offset.y = -bitmap.height / 2;
}
else if ((alignment & VALIGN_BOTTOM) === VALIGN_BOTTOM) {
offset.y = -bitmap.height;
}
return offset;
}
}
}