File:SpineAtlas.js

/**
 * @module PIXI Spine
 * @namespace springroll.pixi
 * @requires  Core, PIXI Display, Animation
 */
(function(undefined)
{
	var AtlasReader = include('PIXI.spine.SpineRuntime.AtlasReader', false),
		AtlasPage = include('PIXI.spine.SpineRuntime.AtlasPage', false),
		AtlasRegion = include('PIXI.spine.SpineRuntime.AtlasRegion', false),
		Atlas = include('PIXI.spine.SpineRuntime.Atlas', false);

	if (!AtlasReader) return;

	/**
	 * Handles an atlas exported from Spine. This class is created during Spine loading, and
	 * should probably never be used on its own. Code in this class is pulled from
	 * https://github.com/pixijs/pixi-spine/blob/master/src/SpineRuntime/Atlas.js
	 *
	 * @class SpineAtlas
	 * @constructor
	 * @param {String} atlasText The Spine Atlas data
	 * @param {Object} textureDictionary All of the images required by the atlas.
	 */
	var SpineAtlas = function(atlasText, textureDictionary)
	{
		this.pages = [];
		this.regions = [];

		if (!atlasText) return;

		var reader = new AtlasReader(atlasText);
		var tuple = [];
		tuple.length = 4;
		var page = null;
		while (true)
		{
			var line = reader.readLine();
			if (line === null) break;
			line = reader.trim(line);
			if (!line.length)
			{
				page = null;
			}
			else if (!page)
			{
				page = new AtlasPage();
				page.name = line;

				if (reader.readTuple(tuple) == 2)
				{
					// size is only optional for an atlas packed with an old TexturePacker.
					page.width = parseInt(tuple[0]);
					page.height = parseInt(tuple[1]);
					reader.readTuple(tuple);
				}
				page.format = Atlas.Format[tuple[0]];

				reader.readTuple(tuple);
				page.minFilter = Atlas.TextureFilter[tuple[0]];
				page.magFilter = Atlas.TextureFilter[tuple[1]];

				var direction = reader.readValue();
				page.uWrap = Atlas.TextureWrap.clampToEdge;
				page.vWrap = Atlas.TextureWrap.clampToEdge;
				if (direction == "x")
					page.uWrap = Atlas.TextureWrap.repeat;
				else if (direction == "y")
					page.vWrap = Atlas.TextureWrap.repeat;
				else if (direction == "xy")
					page.uWrap = page.vWrap = Atlas.TextureWrap.repeat;

				page.rendererObject = textureDictionary[line].baseTexture;

				this.pages.push(page);

			}
			else
			{
				var region = new AtlasRegion();
				region.name = line;
				region.page = page;

				region.rotate = reader.readValue() == "true";

				reader.readTuple(tuple);
				var x = parseInt(tuple[0]);
				var y = parseInt(tuple[1]);

				reader.readTuple(tuple);
				var width = parseInt(tuple[0]);
				var height = parseInt(tuple[1]);

				region.u = x / page.width;
				region.v = y / page.height;
				if (region.rotate)
				{
					region.u2 = (x + height) / page.width;
					region.v2 = (y + width) / page.height;
				}
				else
				{
					region.u2 = (x + width) / page.width;
					region.v2 = (y + height) / page.height;
				}
				region.x = x;
				region.y = y;
				region.width = Math.abs(width);
				region.height = Math.abs(height);

				if (reader.readTuple(tuple) == 4)
				{
					// split is optional
					region.splits = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])];

					if (reader.readTuple(tuple) == 4)
					{
						// pad is optional, but only present with splits
						region.pads = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])];

						reader.readTuple(tuple);
					}
				}

				region.originalWidth = parseInt(tuple[0]);
				region.originalHeight = parseInt(tuple[1]);

				reader.readTuple(tuple);
				region.offsetX = parseInt(tuple[0]);
				region.offsetY = parseInt(tuple[1]);

				region.index = parseInt(reader.readValue());

				this.regions.push(region);
			}
		}
	};

	// Extend Object
	var p = extend(SpineAtlas);

	p.findRegion = function(name)
	{
		var regions = this.regions;
		for (var i = 0, n = regions.length; i < n; i++)
			if (regions[i].name == name) return regions[i];
		return null;
	};

	p.dispose = function()
	{
		var pages = this.pages;
		for (var i = 0, n = pages.length; i < n; i++)
			pages[i].rendererObject.destroy(true);
	};

	p.updateUVs = function(page)
	{
		var regions = this.regions;
		for (var i = 0, n = regions.length; i < n; i++)
		{
			var region = regions[i];
			if (region.page != page) continue;
			region.u = region.x / page.width;
			region.v = region.y / page.height;
			if (region.rotate)
			{
				region.u2 = (region.x + region.height) / page.width;
				region.v2 = (region.y + region.width) / page.height;
			}
			else
			{
				region.u2 = (region.x + region.width) / page.width;
				region.v2 = (region.y + region.height) / page.height;
			}
		}
	};

	/**
	 * Adds a standalone image as a page and region
	 * @method addImage
	 * @param  {String} name The name of the texture, so it can get recognized by the Spine
	 *                       skeleton data.
	 * @param  {PIXI.Texture} texture The loaded texture for the image to add.
	 */
	p.addImage = function(name, texture)
	{
		var page = new AtlasPage();
		page.name = name;
		page.width = texture.width;
		page.height = texture.height;
		//shouldn't really be relevant in Pixi
		page.format = "RGBA8888";
		//also shouldn't be relevant in Pixi
		page.minFilter = page.magFilter = "Nearest";
		//use the clamping defaults
		page.uWrap = Atlas.TextureWrap.clampToEdge;
		page.vWrap = Atlas.TextureWrap.clampToEdge;
		//set the texture
		page.rendererObject = texture.baseTexture;
		//keep page
		this.pages.push(page);

		//set up the region
		var region = new AtlasRegion();
		region.name = name;
		region.page = page;
		region.rotate = false;
		//region takes up the full image
		region.u = region.v = 0;
		region.u2 = region.v2 = 1;
		region.x = region.y = 0;
		region.originalWidth = region.width = page.width;
		region.originalHeight = region.height = page.height;
		region.offsetX = region.offsetY = 0;
		//no index
		region.index = -1;
		//keep region
		this.regions.push(region);
	};

	/**
	 * Sets up this SpineAtlas from an instance of our TextureAtlas class to allow for
	 * the use of atlases exported from TexturePacker.
	 * @method fromTextureAtlas
	 * @param  {springroll.pixi.TextureAtlas} atlas The atlas to generate from
	 * @param {String} [name] The name to use for the name of the singular AtlasPage.
	 */
	p.fromTextureAtlas = function(atlas, name)
	{
		var page = new AtlasPage();
		page.name = name;
		page.width = atlas.baseTexture.width;
		page.height = atlas.baseTexture.height;
		//shouldn't really be relevant in Pixi
		page.format = "RGBA8888";
		//also shouldn't be relevant in Pixi
		page.minFilter = page.magFilter = "Nearest";
		//use the clamping defaults
		page.uWrap = Atlas.TextureWrap.clampToEdge;
		page.vWrap = Atlas.TextureWrap.clampToEdge;
		//set the texture
		page.rendererObject = atlas.baseTexture;
		//keep page
		this.pages.push(page);

		for (name in atlas.frames)
		{
			var frame = atlas.frames[name];
			var region = new AtlasRegion();
			region.name = name;
			region.page = page;
			region.rotate = frame.rotate;
			//figure out region coordinates
			var x = frame.crop.x;
			var y = frame.crop.y;

			var width = frame.crop.width;
			var height = frame.crop.height;

			region.u = x / page.width;
			region.v = y / page.height;
			if (region.rotate)
			{
				region.u2 = (x + height) / page.width;
				region.v2 = (y + width) / page.height;
			}
			else
			{
				region.u2 = (x + width) / page.width;
				region.v2 = (y + height) / page.height;
			}
			region.x = x;
			region.y = y;
			region.width = Math.abs(width);
			region.height = Math.abs(height);

			region.originalWidth = frame.width;
			region.originalHeight = frame.height;

			if (frame.trim)
			{
				region.offsetX = frame.trim.x;
				region.offsetY = frame.trim.y;
			}
			else
				region.offsetX = region.offsetY = 0;
			//no index
			region.index = -1;
			//keep region
			this.regions.push(region);
		}
	};

	/**
	 * Destroys the SpineAtlas by nulling the image and frame dictionary references.
	 * @method destroy
	 */
	p.destroy = function()
	{
		this.dispose();
		this.pages = this.regions = null;
	};

	namespace("springroll.pixi").SpineAtlas = SpineAtlas;
}());