File:BitmapUtils.js

/**
 * @module EaselJS Display
 * @namespace springroll.easeljs
 * @requires Core
 */
(function()
{
	var Bitmap = include('createjs.Bitmap'),
		Container = include('createjs.Container'),
		Rectangle = include('createjs.Rectangle');

	/**
	 * Designed to provide utility related to Bitmaps.
	 * @class BitmapUtils
	 */
	var BitmapUtils = {};

	/**
	 * Replaces Bitmaps in the global lib dictionary with a faux Bitmap
	 * that pulls the image from a spritesheet. This function should be
	 * called after you have loaded up javascript assets exported from Flash,
	 * but before you have instantiated those assets.
	 *
	 * @method loadSpriteSheet
	 * @static
	 * @param {Object} spritesheetData The JSON object describing the frames in the atlas. This is
	 *                                 expected to fit the JSON Hash format as exported from
	 *                                 TexturePacker.
	 * @param {Image|HTMLCanvasElement} spritesheetImage The spritesheet image that contains all of
	 *                                                   the frames.
	 * @param {Number} [scale=1] The scale to apply to all sprites from the spritesheet. For
	 *                           example, a half sized spritesheet should have a scale of 2.
	 * @param {String} [libName='lib'] The global dictionary to add items to.
	 */
	BitmapUtils.loadSpriteSheet = function(spritesheetData, spritesheetImage, scale, libName)
	{
		if (scale > 0)
		{
			// Do nothing
		}
		else if (spritesheetData.meta && parseFloat(spritesheetData.meta.scale))
		{
			// look for scale in spritesheet data
			scale = 1 / parseFloat(spritesheetData.meta.scale);
		}
		else
		{
			// scale should default to 1
			scale = 1;
		}
		if (!libName)
			libName = "lib";

		var frameDict = spritesheetData.frames || spritesheetData;
		// TexturePacker outputs frames with (not) swapped width & height when rotated, so we need to
		// swap them ourselves
		var swapFrameSize = spritesheetData.meta &&
			spritesheetData.meta.app == "http://www.codeandweb.com/texturepacker";

		var lib = window[libName];
		for (var key in frameDict)
		{
			var frame = frameDict[key];
			var index = key.indexOf(".");
			if (index > 0)
			{
				// remove any file extension from the frame id
				key = key.substring(0, index);
			}
			var bitmap = lib[key];

			/* jshint ignore:start */
			var newBitmap = lib[key] = function()
			{
				Container.call(this);
				var child = new Bitmap(this._image);
				this.addChild(child);
				child.sourceRect = this._frameRect;
				var s = this._scale;
				child.setTransform(this._frameOffsetX * s, this._frameOffsetY * s, s, s);
				if (this._rotated)
				{
					child.rotation = -90;
					//scale should not be included with regX
					child.regX = child.sourceRect.width;
				}
			};
			/* jshint ignore:end */

			var p = newBitmap.prototype = new Container();

			//give it a reference to the spritesheet
			p._image = spritesheetImage;

			//tell it what scale to use on the Bitmap to bring it to normal size
			p._scale = scale;

			var rotated = frame.rotated;
			if (rotated)
			{
				p._rotated = true;
			}
			var frameRect = frame.frame;

			//save the source rectangle of the sprite
			p._frameRect = new Rectangle(
				frameRect.x,
				frameRect.y, (rotated && swapFrameSize) ? frameRect.h : frameRect.w, (rotated && swapFrameSize) ? frameRect.w : frameRect.h
			);

			//if the sprite is trimmed, then save the amount that was trimmed
			//off the left and top sides
			if (frame.trimmed)
			{
				p._frameOffsetX = frame.spriteSourceSize.x;
				p._frameOffsetY = frame.spriteSourceSize.y;
			}
			else
			{
				p._frameOffsetX = p._frameOffsetY = 0;
			}

			if (bitmap && bitmap.prototype.nominalBounds)
			{
				//keep the nominal bounds from the original bitmap, if it existed
				p.nominalBounds = bitmap.prototype.nominalBounds;
			}
			else
			{
				p.nominalBounds = new Rectangle(0, 0,
					frame.sourceSize.w * scale,
					frame.sourceSize.h * scale
				);
			}
		}
	};

	/**
	 * Creates a faux Bitmap from a TextureAtlas entry.
	 * @method bitmapFromTexture
	 * @static
	 * @param {Texture} texture The texture from a TextureAtlas to create the Bitmap analogue from.
	 * @param {Number} scale A scale for the spritesheet to undo, e.g. a half sized spritesheet
	 *                     gets a scale of 2 to restore it to normal size.
	 */
	BitmapUtils.bitmapFromTexture = function(texture, scale)
	{
		if (scale > 0)
		{
			// Do nothing
		}
		else
		{
			// scale should default to 1
			scale = 1;
		}
		var output = new Container();
		var bitmap = new Bitmap(texture.image);
		output.addChild(bitmap);
		bitmap.sourceRect = texture.frame;
		bitmap.setTransform(
			texture.offset.x * scale,
			texture.offset.y * scale,
			scale,
			scale
		);

		if (texture.rotated)
		{
			bitmap.rotation = -90;
			bitmap.regX = bitmap.sourceRect.width;
		}
		//set up a nominal bounds to be kind
		output.nominalBounds = new Rectangle(0, 0,
			texture.width * scale,
			texture.height * scale
		);
		return output;
	};

	/**
	 * Replaces Bitmaps in the global lib dictionary with a faux Bitmap
	 * that uses a scaled bitmap, so you can load up smaller bitmaps behind
	 * the scenes that are scaled back up to normal size, or high res bitmaps
	 * that are scaled down.
	 *
	 * @method replaceWithScaledBitmap
	 * @static
	 * @param {String|Object} idOrDict A dictionary of Bitmap ids to replace, or a single id.
	 * @param {Number} [scale=1] The scale to apply to the image(s).
	 * @param {String} [libName='lib'] The global dictionary to add items to.
	 */
	BitmapUtils.replaceWithScaledBitmap = function(idOrDict, scale, libName)
	{
		//scale is required, but it doesn't hurt to check - also, don't bother for a scale of 1
		if (scale != 1 && scale > 0)
		{
			// Do nothing
		}
		else
		{
			return;
		}
		if (!libName)
			libName = "lib";

		var key, bitmap, newBitmap, p;
		var lib = window[libName];
		if (typeof idOrDict == "string")
		{
			key = idOrDict;
			bitmap = lib[key];
			if (bitmap)
			{
				/* jshint ignore:start */
				newBitmap = lib[key] = function()
				{
					Container.call(this);
					var child = new this._oldBM();
					this.addChild(child);
					child.setTransform(0, 0, this._scale, this._scale);
				};
				/* jshint ignore:end */
				p = newBitmap.prototype = new Container();
				p._oldBM = bitmap; //give it a reference to the Bitmap
				p._scale = scale; //tell it what scale to use on the Bitmap to bring it to normal size
				p.nominalBounds = bitmap.prototype.nominalBounds; //keep the nominal bounds
			}
		}
		else
		{
			for (key in idOrDict)
			{
				bitmap = lib[key];
				if (bitmap)
				{
					/* jshint ignore:start */
					newBitmap = lib[key] = function()
					{
						Container.call(this);
						var child = new this._oldBM();
						this.addChild(child);
						child.setTransform(0, 0, this._scale, this._scale);
					};
					/* jshint ignore:end */
					p = newBitmap.prototype = new Container();
					p._oldBM = bitmap; //give it a reference to the Bitmap
					p._scale = scale; //tell it what scale to use on the Bitmap to bring it to normal size
					p.nominalBounds = bitmap.prototype.nominalBounds; //keep the nominal bounds
				}
			}
		}
	};

	namespace("createjs").BitmapUtils = BitmapUtils;
	namespace("springroll.easeljs").BitmapUtils = BitmapUtils;
}());