/** flips individual blocks within a bitmap. Specifically used for doing sprite sheets. But I am sure you could find another use if you wanted.
* @param inBM
* @param spritePixelWidth
* @param spritePixelHeight
* @param inTransformType :An enum from TransFiveConstants. Example: TransFiveConstants.VERTICAL_FLIP
* @return a bitmap data object where all the sprites within the sprite sheet bitmap have been rotated or transformed **/
public static function makeTransformedCopyOfSpriteSheet(inSpriteSheet:BitmapData, spritePixelWidth:int, spritePixelHeight:int, inTransformType:int):BitmapData
{
//Do error check to make sure we evenly fit into the bitmap we are doing transforms on.
CONFIG::debug{
if ( (inSpriteSheet.width % spritePixelWidth ) != 0) { throw new Error("input error: width does not go in evenly! Fix it."); }
if ( (inSpriteSheet.height % spritePixelHeight) != 0) { throw new Error("input error: height does not go in evenly! Fix it."); }
}//CONFIG_debug
//Calculate width and height in sprites of the inSpriteSheet.
var widthInSprites :int = inSpriteSheet.width / spritePixelWidth ;
var heightInSprites:int = inSpriteSheet.height / spritePixelHeight;
/** Bitmap that takes rectangle chunks out of inSpriteSheet, one at a time. **/
var inBM:BitmapData = new BitmapData(spritePixelWidth, spritePixelHeight, true, 0x00);
//Inlined copy of code in makeTransformedCopy
////////////////////////////////////////////////////////////////////
var flipWidthHeight:Boolean = false;
if (inTransformType == TransFiveConstants.ROTATE_NEG_90 ||
inTransformType == TransFiveConstants.ROTATE_POS_90 )
{
flipWidthHeight = true;
}
var outWID:int = (flipWidthHeight ? inBM.height : inBM.width );
var outHGT:int = (flipWidthHeight ? inBM.width : inBM.height);
////////////////////////////////////////////////////////////////////
/** Bitmap that is a [rotated||transformed] version of inputBitmap: **/
var outBM:BitmapData = new BitmapData(outWID, outHGT, true, 0x00);
var outputSpriteSheetWidth :int = outBM.width * widthInSprites;
var outputSpriteSheetHeight:int = outBM.height * heightInSprites;
/** The output of this function **/
var outputSpriteSheet :BitmapData = new BitmapData(outputSpriteSheetWidth, outputSpriteSheetHeight, true, 0x00);
//scan through the sheet with a rectangle and make all the transformed copies you need.
//Every time you make a transformed chunk/copy, move it from the inSpriteSheet to the outputSpriteSheet
/** Places the [rotated||transformed] chunk in correct spot on outputSpriteSheet **/
var finalDestination:Point = new Point();
var cookieCutter:Rectangle = new Rectangle();
cookieCutter.width = spritePixelWidth ;
cookieCutter.height = spritePixelHeight;
for (var xx:int = 0; xx < widthInSprites ; ++xx){
for (var yy:int = 0; yy < heightInSprites; ++yy){
cookieCutter.x = xx * spritePixelWidth;
cookieCutter.y = yy * spritePixelHeight;
//Cut chunk out of main sprite sheet:
inBM.copyPixels(inSpriteSheet, cookieCutter, ZZ, null, null, true);
//Transform the chunk you cut out of the main sprite sheet:
makeTransformedCopy(inBM, inTransformType, outBM);
//Paste the transformed copy into the output sheet:
finalDestination.x = xx * outBM.width; //if outBM is rotated, this width will NOT BE SAME AS spritePixelWidth
finalDestination.y = yy * outBM.height;
outputSpriteSheet.copyPixels(outBM, outBM.rect, finalDestination, null, null, true);
}}//next [xx, yy]
return outputSpriteSheet;
}//makeTransformedCopyOfSpriteSheet
/** Flips/Mirrors and Rotates using a 1D index scan remap. "transformUsingScanRemap"
*
* Meaning, I put data into a 1D array, then change my assumptions on the "scan order" ( left-right, then top-bottom is default convention),
* I can effectively rotate or transform the pixel input.
*
* All we have to do is pack the bitmap into a 1D array. (scanning left-right, top-to-bottom)
* And then convert it BACK to the 2D array using a different formula assuming the data is packed via a different scan order.
*
* EXAMPLE:
* If we change our assumption to assume it is left-right, then BOTTOM-to-top, and make a formula for mapping 1D indexes
* to 2D values based on that scan order assumption, we achieve a vertical flip.
*
* @param inBM :Bitmap we are making a transformed copy of.
* @param inTransformType :The enum for the type of [transform||rotation] to use. For values check TransFiveConstants.as
* @param outputBitmap :Supply this to OVERWRITE an existing bitmap instead of create a NEW bitmap for output.
* @return a transformed or rotated bitmap **/
public static function makeTransformedCopy(inBM:BitmapData, inTransformType:int, outputBitmap:BitmapData = null):BitmapData
{
//If the bitmap is being ROTATED, we will have to flip the output width and height.
var flipWidthHeight:Boolean = false;
if (inTransformType == TransFiveConstants.ROTATE_NEG_90 ||
inTransformType == TransFiveConstants.ROTATE_POS_90 )
{
flipWidthHeight = true;
}
var outWID:int = (flipWidthHeight ? inBM.height : inBM.width );
var outHGT:int = (flipWidthHeight ? inBM.width : inBM.height);
//You can supply a reference to the OUTPUT of this function if you are doing some type of batch processing
//And want to avoid repetitively constructing new intermediary bitmaps:
if (outputBitmap == null)
{
var outputBitmap:BitmapData = new BitmapData(outWID, outHGT, true, 0x00);
}
else
{
if (outputBitmap.width != outWID) { ICU.error("Bad output bitmap supplied. Size is wrong."); }
if (outputBitmap.height != outHGT) { ICU.error("Bad output bitmap supplied. Size is wrong."); }
}
/** Max x index when remapping 1D values. **/
var maxXXX:int = outWID - 1;
/** Max y index when remapping 1D values. **/
var YYYmax:int = outHGT - 1;
/** Number of full columns, using 1D scan order for this orientation. **/
var fullColumns:int = 0;
/** Number of full rows, using 1D scan order for orientation specified. **/
var fullRows:int = 0;
/**What is left over after we have calculated the rows or collumns we have. **/
var remainder:int = 0;
var curPix:uint;
var index:int = ( -1);
var trans:IntPoint = new IntPoint();
inBM.lock();
outputBitmap.lock();
for (var yy:int = 0; yy < inBM.height; yy++) {
for (var xx:int = 0; xx < inBM.width ; xx++) {
++index;
//using 1D index position, remap that to correct 2D position.
//To do different transforms and rotations, simply change your assumptions on how we
//map from 1D to 2D.
//Standard 1D to 2D assumes scan lines go left-right, then top-bottom. Row by row.
//2D to 1D formula for standard:
//1D = (Y * width) + X.
//how many full widths you can get out of 1D == Y. Remainder == X.
//2D.Y = (1D/width);
//2D.X = 1D - (2D.Y * width);
if (inTransformType == TransFiveConstants.NO_TRANSFORM)
{ //[1][2] Assumed scan order (AKA 1D to 2D Mapping)
fullRows = (index / outWID); //[3][4] used to get full
remainder = index - (fullRows * outWID); //[5][6] rows and remainder.
trans.iy = fullRows;
trans.ix = remainder;
}else
if (inTransformType == TransFiveConstants.VERTICAL_FLIP)
{ //[5][6] Assumed scan order (AKA 1D to 2D Mapping)
fullRows = (index / outWID); //[3][4] used to get full
remainder = index - (fullRows * outWID); //[1][2] rows and remainder.
trans.iy = YYYmax - fullRows;
trans.ix = remainder;
}else
if (inTransformType == TransFiveConstants.ROTATE_NEG_90)
{ //[2][4][6] Assumed scan order (AKA 1D to 2D Mapping)
fullColumns = (index / outHGT); //[1][3][5] used to get full rows and remainder.
remainder = index - (fullColumns * outHGT);
trans.ix = fullColumns;
trans.iy = YYYmax - remainder;
}else
if (inTransformType == TransFiveConstants.ROTATE_POS_90)
{ //[5][3][1] Assumed scan order (AKA 1D to 2D Mapping)
fullColumns = (index / outHGT); //[6][4][2] used to get full rows and remainder.
remainder = index - (fullColumns * outHGT);
trans.ix = maxXXX - fullColumns;
trans.iy = remainder;
}else
if (inTransformType == TransFiveConstants.FLOOR_MIRROR)
{ //[2][1] Assumed scan order (AKA 1D to 2D Mapping)
fullRows = (index / outWID); //[4][3] used to get full
remainder = index - (fullRows * outWID); //[6][5] rows and remainder.
trans.iy = fullRows;
trans.ix = maxXXX - remainder;
}
else
{
throw new Error("Transform type not recognized");
}
//Copy and paste the pixel now that we know where to put it:
curPix = inBM.getPixel32(xx, yy);
outputBitmap.setPixel32(trans.ix, trans.iy, curPix);
}}//next [xx, yy]
inBM.unlock();
outputBitmap.unlock();
return outputBitmap;
}//transformUsingScanRemap