forked from LeenkxTeam/LNXSDK
		
	
		
			
				
	
	
		
			256 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
| /*
 | |
|  * format - Haxe File Formats
 | |
|  *
 | |
|  *  BMP File Format
 | |
|  *  Copyright (C) 2007-2009 Trevor McCauley, Baluta Cristian (hx port) & Robert Sköld (format conversion)
 | |
|  *
 | |
|  * Copyright (c) 2009, The Haxe Project Contributors
 | |
|  * 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.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT 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 THE HAXE PROJECT CONTRIBUTORS 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.
 | |
|  */
 | |
| package iron.format.bmp;
 | |
| 
 | |
| 
 | |
| class Tools {
 | |
| 
 | |
| 	//												  a  r  g  b
 | |
| 	static var ARGB_MAP(default, never):Array<Int> = [0, 1, 2, 3];
 | |
| 	static var BGRA_MAP(default, never):Array<Int> = [3, 2, 1, 0];
 | |
| 
 | |
| 	static var COLOR_SIZE(default, never):Int = 4;
 | |
| 	
 | |
| 	/**
 | |
| 		Extract BMP pixel data (24bpp in BGR format) and expands it to BGRA, removing any padding in the process.
 | |
| 	**/
 | |
| 	inline static public function extractBGRA( bmp : iron.format.bmp.Data ) : haxe.io.Bytes {
 | |
| 		return _extract32(bmp, BGRA_MAP, 0xFF);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 		Extract BMP pixel data (24bpp in BGR format) and converts it to ARGB.
 | |
| 	**/
 | |
| 	inline static public function extractARGB( bmp : iron.format.bmp.Data ) : haxe.io.Bytes {
 | |
| 		return _extract32(bmp, ARGB_MAP, 0xFF);
 | |
| 	}
 | |
|   
 | |
| 	/**
 | |
| 		Creates BMP data from bytes in BGRA format for each pixel.
 | |
| 	**/
 | |
| 	inline static public function buildFromBGRA( width : Int, height : Int, srcBytes : haxe.io.Bytes, topToBottom : Bool = false ) : Data {
 | |
| 		return _buildFrom32(width, height, srcBytes, BGRA_MAP, topToBottom);
 | |
| 	}
 | |
|   
 | |
| 	/**
 | |
| 		Creates BMP data from bytes in ARGB format for each pixel.
 | |
| 	**/
 | |
| 	inline static public function buildFromARGB( width : Int, height : Int, srcBytes : haxe.io.Bytes, topToBottom : Bool = false ) : Data {
 | |
| 		return _buildFrom32(width, height, srcBytes, ARGB_MAP, topToBottom);
 | |
| 	}
 | |
| 
 | |
| 	inline static public function computePaddedStride(width:Int, bpp:Int):Int {
 | |
| 		return ((((width * bpp) + 31) & ~31) >> 3);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets number of colors for indexed palettes
 | |
| 	 */
 | |
| 	inline static public function getNumColorsForBitDepth(bpp:Int):Int {
 | |
| 		return switch (bpp) {
 | |
| 			case 1: 2;
 | |
| 			case 4: 16;
 | |
| 			case 8: 256;
 | |
| 			case 16: 65536;
 | |
| 			default: throw 'Unsupported bpp $bpp';
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	
 | |
| 	// `channelMap` contains indices to map into ARGB (f.e. the mapping for ARGB is [0,1,2,3], while for BGRA is [3,2,1,0])
 | |
| 	static function _extract32( bmp : iron.format.bmp.Data, channelMap : Array<Int>, alpha : Int = 0xFF) : haxe.io.Bytes {
 | |
| 		var srcBytes = bmp.pixels;
 | |
| 		var dstLen = bmp.header.width * bmp.header.height * 4;
 | |
| 		var dstBytes = haxe.io.Bytes.alloc( dstLen );
 | |
| 		var srcPaddedStride = bmp.header.paddedStride;
 | |
| 		
 | |
| 		var yDir = -1;
 | |
| 		var dstPos = 0;
 | |
| 		var srcPos = srcPaddedStride * (bmp.header.height - 1);
 | |
|     
 | |
| 		if ( bmp.header.topToBottom ) {
 | |
| 			yDir = 1;
 | |
| 			srcPos = 0;
 | |
| 		}
 | |
| 
 | |
| 		if ( bmp.header.bpp < 8 || bmp.header.bpp == 16 ) {
 | |
| 			throw 'bpp ${bmp.header.bpp} not supported';
 | |
| 		}
 | |
| 
 | |
| 		var colorTable:haxe.io.Bytes = null;
 | |
| 		if ( bmp.header.bpp <= 8 ) {
 | |
| 			var colorTableLength = getNumColorsForBitDepth(bmp.header.bpp);
 | |
| 			colorTable = haxe.io.Bytes.alloc(colorTableLength * COLOR_SIZE);
 | |
| 			var definedColorTableLength = Std.int( bmp.colorTable.length / COLOR_SIZE );
 | |
| 			for( i in 0...definedColorTableLength ) {
 | |
| 				var b = bmp.colorTable.get( i * COLOR_SIZE);
 | |
| 				var g = bmp.colorTable.get( i * COLOR_SIZE + 1);
 | |
| 				var r = bmp.colorTable.get( i * COLOR_SIZE + 2);
 | |
| 
 | |
| 				colorTable.set(i * COLOR_SIZE + channelMap[0], alpha);
 | |
| 				colorTable.set(i * COLOR_SIZE + channelMap[1], r);
 | |
| 				colorTable.set(i * COLOR_SIZE + channelMap[2], g);
 | |
| 				colorTable.set(i * COLOR_SIZE + channelMap[3], b);
 | |
| 			}
 | |
| 			// We want to have the table the full length in case indices outside the range are present
 | |
| 			colorTable.fill(definedColorTableLength, colorTableLength - definedColorTableLength, 0);
 | |
| 			for( i in definedColorTableLength...colorTableLength ) {
 | |
| 				colorTable.set(i * COLOR_SIZE + channelMap[0], alpha);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		switch bmp.header.compression {
 | |
| 			case 0:
 | |
| 				while( dstPos < dstLen ) {
 | |
| 					for( i in 0...bmp.header.width ) {
 | |
| 						if (bmp.header.bpp == 8) {
 | |
| 
 | |
| 							var currentSrcPos = srcPos + i;
 | |
| 							var index = srcBytes.get(currentSrcPos);
 | |
| 							dstBytes.blit( dstPos, colorTable, index * COLOR_SIZE, COLOR_SIZE );
 | |
| 
 | |
| 						} else if (bmp.header.bpp == 24) {
 | |
| 
 | |
| 							var currentSrcPos = srcPos + i * 3;
 | |
| 							var b = srcBytes.get(currentSrcPos);
 | |
| 							var g = srcBytes.get(currentSrcPos + 1);
 | |
| 							var r = srcBytes.get(currentSrcPos + 2);
 | |
| 							
 | |
| 							dstBytes.set(dstPos + channelMap[0], alpha);
 | |
| 							dstBytes.set(dstPos + channelMap[1], r);
 | |
| 							dstBytes.set(dstPos + channelMap[2], g);
 | |
| 							dstBytes.set(dstPos + channelMap[3], b);
 | |
| 
 | |
| 						} else if (bmp.header.bpp == 32) {
 | |
| 
 | |
| 							var currentSrcPos = srcPos + i * 4;
 | |
| 							var b = srcBytes.get(currentSrcPos);
 | |
| 							var g = srcBytes.get(currentSrcPos + 1);
 | |
| 							var r = srcBytes.get(currentSrcPos + 2);
 | |
| 							
 | |
| 							dstBytes.set(dstPos + channelMap[0], alpha);
 | |
| 							dstBytes.set(dstPos + channelMap[1], r);
 | |
| 							dstBytes.set(dstPos + channelMap[2], g);
 | |
| 							dstBytes.set(dstPos + channelMap[3], b);
 | |
| 
 | |
| 						}
 | |
| 						dstPos += 4;
 | |
| 					}
 | |
| 					srcPos += yDir * srcPaddedStride;
 | |
| 				}
 | |
| 			case 1:
 | |
| 				srcPos = 0;
 | |
| 				var x = 0;
 | |
| 				var y = bmp.header.topToBottom ? 0 : bmp.header.height - 1;
 | |
| 				while( srcPos < bmp.header.dataLength ) {
 | |
| 					var count = srcBytes.get(srcPos++);
 | |
| 					var index = srcBytes.get(srcPos++);
 | |
| 					if ( count == 0 ) {
 | |
| 						if ( index == 0 ) {
 | |
| 							x = 0;
 | |
| 							y += yDir;
 | |
| 						} else if ( index == 1 ) {
 | |
| 							break;
 | |
| 						} else if ( index == 2 ) {
 | |
| 							x += srcBytes.get(srcPos++);
 | |
| 							y += srcBytes.get(srcPos++);
 | |
| 						} else {
 | |
| 							count = index;
 | |
| 							for( i in 0...count ) {
 | |
| 								index = srcBytes.get(srcPos++);
 | |
| 								dstBytes.blit( COLOR_SIZE * ((x+i) + y * bmp.header.width), colorTable, index * COLOR_SIZE, COLOR_SIZE );
 | |
| 							}
 | |
| 							if (srcPos % 2 != 0) srcPos++;
 | |
| 							x += count;
 | |
| 						}
 | |
| 					} else {
 | |
| 						for( i in 0...count ) {
 | |
| 							dstBytes.blit( COLOR_SIZE * ((x+i) + y * bmp.header.width), colorTable, index * COLOR_SIZE, COLOR_SIZE );
 | |
| 						}
 | |
| 						x += count;
 | |
| 					}
 | |
| 				}
 | |
| 			default:
 | |
| 				throw 'compression ${bmp.header.compression} not supported';
 | |
| 		}
 | |
| 
 | |
| 		return dstBytes;
 | |
| 	}
 | |
| 	
 | |
| 	// `channelMap` contains indices to map into ARGB (f.e. the mapping for ARGB is [0,1,2,3], while for BGRA is [3,2,1,0])
 | |
| 	static function _buildFrom32( width : Int, height : Int, srcBytes : haxe.io.Bytes, channelMap : Array<Int>, topToBottom : Bool = false ) : Data {
 | |
| 		var bpp = 24;
 | |
| 		var paddedStride = computePaddedStride(width, bpp);
 | |
| 		var bytesBGR = haxe.io.Bytes.alloc(paddedStride * height);
 | |
| 		var topToBottom = topToBottom;
 | |
| 		var dataLength = bytesBGR.length;
 | |
| 		
 | |
| 		var dstStride = width * 3;
 | |
| 		var srcLen = width * height * 4;
 | |
| 		var yDir = -1;
 | |
| 		var dstPos = dataLength - paddedStride;
 | |
| 		var srcPos = 0;
 | |
| 		
 | |
| 		if ( topToBottom ) {
 | |
| 			yDir = 1;
 | |
| 			dstPos = 0;
 | |
| 		}
 | |
| 		
 | |
| 		while( srcPos < srcLen ) {
 | |
| 			var i = dstPos;
 | |
| 			while( i < dstPos + dstStride ) {
 | |
| 				var r = srcBytes.get(srcPos + channelMap[1]);
 | |
| 				var g = srcBytes.get(srcPos + channelMap[2]);
 | |
| 				var b = srcBytes.get(srcPos + channelMap[3]);
 | |
| 				
 | |
| 				bytesBGR.set(i++, b);
 | |
| 				bytesBGR.set(i++, g);
 | |
| 				bytesBGR.set(i++, r);
 | |
| 				
 | |
| 				srcPos += 4;
 | |
| 			}
 | |
| 			dstPos += yDir * paddedStride;
 | |
| 		}
 | |
| 		
 | |
| 		return {
 | |
| 			header: {
 | |
| 				width: width,
 | |
| 				height: height,
 | |
| 				paddedStride: paddedStride,
 | |
| 				topToBottom: topToBottom,
 | |
| 				bpp: bpp,
 | |
| 				dataLength: dataLength,
 | |
| 				compression: 0
 | |
| 			},
 | |
| 			pixels: bytesBGR,
 | |
| 			colorTable: null
 | |
| 		}
 | |
| 	}
 | |
| } |