In almost all circumstances, Stage3D requires you to provide textures with power-of-two dimensions. This is often inconvenient as most images are not already sized that way. Today’s article provides a simple class to easily build a texture with power-of-two dimensions. An example app is also provided.

As this bit twiddling hack shows, rounding up an image’s width and height to the next power of two is quite simple. Here’s an AS3 implementation:

/**
 * Get the next-highest power of two
 * @param v Value to get the next-highest power of two from
 * @return The next-highest power of two from the given value
 */
private static function nextPowerOfTwo(v:uint): uint
{
	v--;
	v |= v >> 1;
	v |= v >> 2;
	v |= v >> 4;
	v |= v >> 8;
	v |= v >> 16;
	v++;
	return v;
}

The following code implements a PowerOfTwoBitmapData class that extends BitmapData to enforce power-of-two dimensions. Here’s its constructor:

public function PowerOfTwoBitmapData(
	original:BitmapData,
	fillColor:uint=0xffffffff,
	square:Boolean=false
)

You simply provide the original texture, presumably without power-of-two dimensions, a fill color to pad out to power-of-two dimensions with, and an option to additionally enforce that the texture is square. You’ll then have a new BitmapData whose dimensions are the next-highest power of two from your original BitmapData. In the top-left corner will be the pixels of your original BitmapData and the rest of the pixels will be set to the fillColor you provide.

PowerOfTwoBitmapData provides a few helpful getters in addition to the inherited BitmapData functionality:

// Original BitmapData dimensions
public function get originalWidth(): Number
public function get originalHeight(): Number
 
// Scale from current size to original size. Useful for adjusting texture coordinates (a.k.a. UVs).
public function get scaleX(): Number
public function get scaleY(): Number
 
// If the the BitmapData is square in addition to having power-of-two dimensions
public function get square(): Boolean

Here’s the full class:

package
{
	import flash.display.BitmapData;
	import flash.geom.Point;
	import flash.geom.Rectangle;
 
	/**
	 * A BitmapData that has power-of-two dimensions and is optionally square
	 * @author Jackson Dunstan, http://JacksonDunstan.com
	 */
	public class PowerOfTwoBitmapData extends BitmapData
	{
		/** Temporary Rectangle to avoid allocations */
		private static const TEMP_RECT:Rectangle = new Rectangle();
 
		/** Temporary Point to avoid allocations */
		private static const TEMP_POINT:Point = new Point();
 
		/** Width of the original */
		private var __originalWidth:uint;
 
		/** Height of the original */
		private var __originalHeight:uint;
 
		/**
		 * Create and copy an existing BitmapData
		 * @param original BitmapData to copy
		 * @param fillColor Color to pad with if necessary
		 * @param square If this BitmapData must be square in addition to having
		 *               power-of-two dimensions
		 */
		public function PowerOfTwoBitmapData(
			original:BitmapData,
			fillColor:uint=0xffffffff,
			square:Boolean=false
		)
		{
			// Save old dimensions
			__originalWidth = original.width;
			__originalHeight = original.height;
 
			// Compute new dimensions
			var newWidth:uint = nextPowerOfTwo(__originalWidth);
			var newHeight:uint = nextPowerOfTwo(__originalHeight);
			if (square)
			{
				newWidth = newHeight = newWidth > newHeight ? newWidth : newHeight;
			}
 
			// Create with enough space to hold new dimensions
			super(newWidth, newHeight, original.transparent, fillColor);
 
			// Copy original pixels to our pixels
			TEMP_RECT.width = __originalWidth;
			TEMP_RECT.height = __originalHeight;
			copyPixels(original, TEMP_RECT, TEMP_POINT);
		}
 
		/**
		 * Width of the original
		 */
		public function get originalWidth(): uint
		{
			return __originalWidth;
		}
 
		/**
		 * Height of the original
		 */
		public function get originalHeight(): uint
		{
			return __originalHeight;
		}
 
		/**
		 * Scale factor from current width to original width
		 */
		public function get scaleX(): Number
		{
			return __originalWidth / Number(this.width);
		}
 
		/**
		 * Scale factor from current height to original height
		 */
		public function get scaleY(): Number
		{
			return __originalHeight / Number(this.height);
		}
 
		/**
		 * If this BitmapData has the same width and height
		 */
		public function get square(): Boolean
		{
			return this.width == this.height;
		}
 
		/**
		 * Get the next-highest power of two
		 * @param v Value to get the next-highest power of two from
		 * @return The next-highest power of two from the given value
		 */
		private static function nextPowerOfTwo(v:uint): uint
		{
			v--;
			v |= v >> 1;
			v |= v >> 2;
			v |= v >> 4;
			v |= v >> 8;
			v |= v >> 16;
			v++;
			return v;
		}
	}
}

Here’s a simple example app to demonstrate using PowerOfTwoBitmapData:

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
 
	[SWF(width="320", height="640")]
	public class PowerOfTwoBitmapDataTest extends Sprite
	{
		private static const SCALE:Number = 7;
 
		public function PowerOfTwoBitmapDataTest()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			var info:TextField = createLabel(
				"-- All bitmaps scaled up " + SCALE + "x --"
			);
			info.x = (stage.stageWidth - info.width) / 2;
			addChild(info);
 
			test(1, 1, false);
			test(3, 1, false);
			test(3, 1, true);
			test(10, 30, false);
			test(10, 30, true);
		}
 
		private function test(width:uint, height:uint, square:Boolean): void
		{
			var originalBMD:BitmapData = new BitmapData(
				width,
				height,
				false,
				0x00ff00
			);
			var potBMD:PowerOfTwoBitmapData = new PowerOfTwoBitmapData(
				originalBMD,
				0xff0000,
				square
			);
			var potBM:Bitmap = new Bitmap(potBMD);
			potBM.scaleX = potBM.scaleY = SCALE;
			var label:TextField = createLabel(
				originalBMD.width + "x" + originalBMD.height
				+ " -> " + potBMD.width + "x" + potBMD.height
				+ (square ? " (square)" : "")
			);
			var container:Sprite = new Sprite();
			container.y = stage.height;
			container.addChild(label);
			potBM.y = label.height;
			container.addChild(potBM);
			addChild(container);
		}
 
		private function createLabel(text:String): TextField
		{
			var label:TextField = new TextField();
			label.autoSize = TextFieldAutoSize.LEFT;
			label.selectable = false;
			label.text = text;
			return label;
		}
	}
}

Launch the example

Here’s a screenshot of the output:

Test App Screenshot

Next time you find yourself with a texture that needs padding to power-of-two dimensions, remember PowerOfTwoBitmapData can be a handy tool for easily doing the job.

Spot a bug? Have a question or suggestion? Post a comment!