Created
December 27, 2010 09:42
-
-
Save comfuture/755999 to your computer and use it in GitHub Desktop.
window BMP file format decoder - bigger but supports more
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package jp.plaync.image.codec | |
{ | |
import flash.display.BitmapData; | |
import flash.errors.IOError; | |
import flash.utils.ByteArray; | |
import flash.utils.Endian; | |
/** | |
* Windows BMP file decoder | |
* @see http://ntt.cc/2008/10/01/using-bmpdecoder-class-to-load-an-external-bmp-file-rle-compression-support.html | |
*/ | |
public class BMPDecoder | |
{ | |
private const BITMAP_HEADER_TYPE:String = "BM"; | |
private const BITMAP_FILE_HEADER_SIZE:int = 14; | |
private const BITMAP_CORE_HEADER_SIZE:int = 12; | |
private const BITMAP_INFO_HEADER_SIZE:int = 40; | |
private const COMP_RGB :int = 0; | |
private const COMP_RLE8:int = 1; | |
private const COMP_RLE4:int = 2; | |
private const COMP_BITFIELDS:int = 3; | |
private const BIT1:int = 1; | |
private const BIT4:int = 4; | |
private const BIT8:int = 8; | |
private const BIT16:int = 16; | |
private const BIT24:int = 24; | |
private const BIT32:int = 32; | |
private var bytes:ByteArray; | |
private var palette:Array; | |
private var bd:BitmapData; | |
private var nFileSize:uint; | |
private var nReserved1:uint; | |
private var nReserved2:uint; | |
private var nOffbits:uint; | |
private var nInfoSize:uint; | |
private var nWidth:int; | |
private var nHeight:int; | |
private var nPlains:uint; | |
private var nBitsPerPixel:uint; | |
private var nCompression:uint; | |
private var nSizeImage:uint; | |
private var nXPixPerMeter:int; | |
private var nYPixPerMeter:int; | |
private var nColorUsed:uint; | |
private var nColorImportant:uint; | |
private var nRMask:uint; | |
private var nGMask:uint; | |
private var nBMask:uint; | |
private var nRPos:uint; | |
private var nGPos:uint; | |
private var nBPos:uint; | |
private var nRMax:uint; | |
private var nGMax:uint; | |
private var nBMax:uint; | |
/** | |
* Constructor | |
*/ | |
public function BMPDecoder() | |
{ | |
nRPos = 0; | |
nGPos = 0; | |
nBPos = 0; | |
} | |
/** | |
* decoder | |
* | |
* @param BMP file ByteArray | |
*/ | |
public function decode(data:ByteArray):BitmapData | |
{ | |
bytes = data; | |
bytes.endian = Endian.LITTLE_ENDIAN; | |
bytes.position = 0; | |
readFileHeader(); | |
nInfoSize = bytes.readUnsignedInt(); | |
switch (nInfoSize) { | |
case BITMAP_CORE_HEADER_SIZE: | |
readCoreHeader(); | |
break; | |
case BITMAP_INFO_HEADER_SIZE: | |
readInfoHeader(); | |
break; | |
default: | |
readExtendedInfoHeader(); | |
break; | |
} | |
bd = new BitmapData(nWidth, nHeight); | |
switch (nBitsPerPixel) { | |
case BIT1: | |
readColorPalette(); | |
decode1BitBMP(); | |
break; | |
case BIT4: | |
readColorPalette(); | |
if (nCompression == COMP_RLE4) { | |
decode4bitRLE(); | |
} else { | |
decode4BitBMP(); | |
} | |
break; | |
case BIT8: | |
readColorPalette(); | |
if (nCompression == COMP_RLE8) { | |
decode8BitRLE(); | |
} else { | |
decode8BitBMP(); | |
} | |
break; | |
case BIT16: | |
readBitFields(); | |
checkColorMask(); | |
decode16BitBMP(); | |
break; | |
case BIT24: | |
decode24BitBMP(); | |
break; | |
case BIT32: | |
readBitFields(); | |
checkColorMask(); | |
decode32BitBMP(); | |
break; | |
default: | |
throw new VerifyError("invalid bits per pixel: " + nBitsPerPixel); | |
} | |
return bd; | |
} | |
/** | |
* read BITMAP FILE HEADER | |
*/ | |
private function readFileHeader():void | |
{ | |
var fileHeader:ByteArray = new ByteArray(); | |
fileHeader.endian = Endian.LITTLE_ENDIAN; | |
try { | |
bytes.readBytes(fileHeader, 0, BITMAP_FILE_HEADER_SIZE); | |
if ( fileHeader.readUTFBytes(2) != BITMAP_HEADER_TYPE){ | |
throw new VerifyError("invalid bitmap header type"); | |
} | |
nFileSize = fileHeader.readUnsignedInt(); | |
nReserved1 = fileHeader.readUnsignedShort(); | |
nReserved2 = fileHeader.readUnsignedShort(); | |
nOffbits = fileHeader.readUnsignedInt(); | |
} catch (e:IOError) { | |
throw new VerifyError("invalid file header"); | |
} | |
} | |
/** | |
* read BITMAP CORE HEADER | |
*/ | |
private function readCoreHeader():void | |
{ | |
var coreHeader:ByteArray = new ByteArray(); | |
coreHeader.endian = Endian.LITTLE_ENDIAN; | |
try { | |
bytes.readBytes(coreHeader, 0, BITMAP_CORE_HEADER_SIZE - 4); | |
nWidth = coreHeader.readShort(); | |
nHeight = coreHeader.readShort(); | |
nPlains = coreHeader.readUnsignedShort(); | |
nBitsPerPixel = coreHeader.readUnsignedShort(); | |
} catch (e:IOError) { | |
throw new VerifyError("invalid core header"); | |
} | |
} | |
/** | |
* read BITMAP INFO HEADER | |
*/ | |
private function readInfoHeader():void | |
{ | |
var infoHeader:ByteArray = new ByteArray(); | |
infoHeader.endian = Endian.LITTLE_ENDIAN; | |
try { | |
bytes.readBytes(infoHeader, 0, BITMAP_INFO_HEADER_SIZE - 4); | |
nWidth = infoHeader.readInt(); | |
nHeight = infoHeader.readInt(); | |
nPlains = infoHeader.readUnsignedShort(); | |
nBitsPerPixel = infoHeader.readUnsignedShort(); | |
nCompression = infoHeader.readUnsignedInt(); | |
nSizeImage = infoHeader.readUnsignedInt(); | |
nXPixPerMeter = infoHeader.readInt(); | |
nYPixPerMeter = infoHeader.readInt(); | |
nColorUsed = infoHeader.readUnsignedInt(); | |
nColorImportant = infoHeader.readUnsignedInt(); | |
} catch (e:IOError) { | |
throw new VerifyError("invalid info header"); | |
} | |
} | |
/** | |
* read the extend info of BITMAP INFO HEADER | |
*/ | |
private function readExtendedInfoHeader():void | |
{ | |
var infoHeader:ByteArray = new ByteArray(); | |
infoHeader.endian = Endian.LITTLE_ENDIAN; | |
try { | |
bytes.readBytes(infoHeader, 0, nInfoSize - 4); | |
nWidth = infoHeader.readInt(); | |
nHeight = infoHeader.readInt(); | |
nPlains = infoHeader.readUnsignedShort(); | |
nBitsPerPixel = infoHeader.readUnsignedShort(); | |
nCompression = infoHeader.readUnsignedInt(); | |
nSizeImage = infoHeader.readUnsignedInt(); | |
nXPixPerMeter = infoHeader.readInt(); | |
nYPixPerMeter = infoHeader.readInt(); | |
nColorUsed = infoHeader.readUnsignedInt(); | |
nColorImportant = infoHeader.readUnsignedInt(); | |
if (infoHeader.bytesAvailable >= 4) nRMask = infoHeader.readUnsignedInt(); | |
if (infoHeader.bytesAvailable >= 4) nGMask = infoHeader.readUnsignedInt(); | |
if (infoHeader.bytesAvailable >= 4) nBMask = infoHeader.readUnsignedInt(); | |
} catch (e:IOError) { | |
throw new VerifyError("invalid info header"); | |
} | |
} | |
/** | |
* read bitfields | |
*/ | |
private function readBitFields():void | |
{ | |
if (nCompression == COMP_RGB) { | |
if ( nBitsPerPixel == BIT16 ){ | |
// RGB555 | |
nRMask = 0x00007c00; | |
nGMask = 0x000003e0; | |
nBMask = 0x0000001f; | |
} else { | |
//RGB888; | |
nRMask = 0x00ff0000; | |
nGMask = 0x0000ff00; | |
nBMask = 0x000000ff; | |
} | |
} else if ((nCompression == COMP_BITFIELDS) && (nInfoSize < 52)) { | |
try { | |
nRMask = bytes.readUnsignedInt(); | |
nGMask = bytes.readUnsignedInt(); | |
nBMask = bytes.readUnsignedInt(); | |
} catch (e:IOError) { | |
throw new VerifyError("invalid bit fields"); | |
} | |
} | |
} | |
/** | |
* read color palette | |
*/ | |
private function readColorPalette():void | |
{ | |
var i:int; | |
var len:int = (nColorUsed > 0) ? nColorUsed : Math.pow(2, nBitsPerPixel); | |
palette = new Array(len); | |
for (i = 0; i < len; ++i){ | |
palette[i] = bytes.readUnsignedInt(); | |
} | |
} | |
/** | |
* decode 1 bit BMP | |
*/ | |
private function decode1BitBMP():void | |
{ | |
var x:int; | |
var y:int; | |
var i:int; | |
var col:int; | |
var buf:ByteArray = new ByteArray(); | |
var line:int = nWidth / 8; | |
if (line % 4 > 0){ | |
line = ((line / 4 | 0) + 1) * 4; | |
} | |
try { | |
for (y = nHeight - 1; y >= 0; --y) { | |
buf.length = 0; | |
bytes.readBytes( buf, 0, line ); | |
for (x = 0; x < nWidth; x += 8) { | |
col = buf.readUnsignedByte(); | |
for (i = 0; i < 8; ++i) { | |
bd.setPixel(x + i, y, palette[ col >> ( 7 - i ) & 0x01 ]); | |
} | |
} | |
} | |
} catch (e:IOError) { | |
throw new VerifyError("invalid image data"); | |
} | |
} | |
/** | |
* decode 4bit RLE | |
*/ | |
private function decode4bitRLE():void | |
{ | |
var x:int; | |
var y:int; | |
var i:int; | |
var n:int; | |
var col:int; | |
var data:uint; | |
var buf:ByteArray = new ByteArray(); | |
try { | |
for (y = nHeight - 1; y >= 0; --y) { | |
buf.length = 0; | |
while (bytes.bytesAvailable > 0) { | |
n = bytes.readUnsignedByte(); | |
if (n > 0) { | |
// encode data | |
data = bytes.readUnsignedByte(); | |
for (i = 0; i < n/2; ++i) { | |
buf.writeByte(data); | |
} | |
} else { | |
n = bytes.readUnsignedByte(); | |
if (n > 0) { | |
// abs mode | |
bytes.readBytes(buf, buf.length, n/2); | |
buf.position += n/2; | |
if (n/2 + 1 >> 1 << 1 != n/2) { | |
bytes.readUnsignedByte(); | |
} | |
} else { | |
// EOL | |
break; | |
} | |
} | |
} | |
buf.position = 0; | |
for (x = 0; x < nWidth; x += 2) { | |
col = buf.readUnsignedByte(); | |
bd.setPixel(x, y, palette[col >> 4]); | |
bd.setPixel(x + 1, y, palette[col & 0x0f]); | |
} | |
} | |
} catch (e:IOError) { | |
throw new VerifyError("invalid image data"); | |
} | |
} | |
/** | |
* decode 4bit (no Compression) | |
*/ | |
private function decode4BitBMP():void | |
{ | |
var x:int; | |
var y:int; | |
var i:int; | |
var col:int; | |
var buf:ByteArray = new ByteArray(); | |
var line:int = nWidth / 2; | |
if (line % 4 > 0) { | |
line = ((line / 4 | 0) + 1) * 4; | |
} | |
try { | |
for (y = nHeight - 1; y >= 0; --y) { | |
buf.length = 0; | |
bytes.readBytes(buf, 0, line); | |
for (x = 0; x < nWidth; x += 2) { | |
col = buf.readUnsignedByte(); | |
bd.setPixel(x, y, palette[col >> 4]); | |
bd.setPixel(x + 1, y, palette[col & 0x0f]); | |
} | |
} | |
} catch (e:IOError) { | |
throw new VerifyError("invalid image data"); | |
} | |
} | |
/** | |
* decode 8bit( RLE Compression ) | |
*/ | |
private function decode8BitRLE():void | |
{ | |
var x:int; | |
var y:int; | |
var i:int; | |
var n:int; | |
var col:int; | |
var data:uint; | |
var buf:ByteArray = new ByteArray(); | |
try { | |
for (y = nHeight - 1; y >= 0; --y) { | |
buf.length = 0; | |
while (bytes.bytesAvailable > 0) { | |
n = bytes.readUnsignedByte(); | |
if (n > 0) { | |
// encode data | |
data = bytes.readUnsignedByte(); | |
for (i = 0; i < n; ++i) { | |
buf.writeByte(data); | |
} | |
} else { | |
n = bytes.readUnsignedByte(); | |
if (n > 0){ | |
// abs mode data | |
bytes.readBytes(buf, buf.length, n); | |
buf.position += n; | |
if (n + 1 >> 1 << 1 != n) { | |
bytes.readUnsignedByte(); | |
} | |
} else { | |
// EOL | |
break; | |
} | |
} | |
} | |
buf.position = 0; | |
for (x = 0; x < nWidth; ++x) { | |
bd.setPixel(x, y, palette[buf.readUnsignedByte()]); | |
} | |
} | |
} catch (e:IOError) { | |
throw new VerifyError("invalid image data"); | |
} | |
} | |
/** | |
* decode 8bit(no Compression) | |
*/ | |
private function decode8BitBMP():void | |
{ | |
var x:int; | |
var y:int; | |
var i:int; | |
var col:int; | |
var buf:ByteArray = new ByteArray(); | |
var line:int = nWidth; | |
if (line % 4 > 0) { | |
line = ((line / 4 | 0) + 1) * 4; | |
} | |
try { | |
for (y = nHeight - 1; y >= 0; --y) { | |
buf.length = 0; | |
bytes.readBytes(buf, 0, line); | |
for (x = 0; x < nWidth; ++x) { | |
bd.setPixel(x, y, palette[buf.readUnsignedByte()]); | |
} | |
} | |
} catch (e:IOError) { | |
throw new VerifyError("invalid image data"); | |
} | |
} | |
/** | |
* decode 16bit | |
*/ | |
private function decode16BitBMP():void | |
{ | |
var x:int; | |
var y:int; | |
var col:int; | |
try { | |
for (y = nHeight - 1; y >= 0; --y) { | |
for (x = 0; x < nWidth; ++x) { | |
col = bytes.readUnsignedShort(); | |
bd.setPixel(x, y, (((col & nRMask) >> nRPos) * 0xff / nRMax << 16) | |
+ (((col & nGMask) >> nGPos) * 0xff / nGMax << 8) | |
+ (((col & nBMask) >> nBPos) * 0xff / nBMax << 0)); | |
} | |
} | |
} catch (e:IOError) { | |
throw new VerifyError("invalid image data"); | |
} | |
} | |
/** | |
* decode 24bit BMP | |
*/ | |
private function decode24BitBMP():void | |
{ | |
var x:int; | |
var y:int; | |
var col:int; | |
var buf:ByteArray = new ByteArray(); | |
var line:int = nWidth * 3; | |
if (line % 4 > 0) { | |
line = ((line / 4 | 0) + 1) * 4; | |
} | |
try { | |
for (y = nHeight - 1; y >= 0; --y) { | |
buf.length = 0; | |
bytes.readBytes(buf, 0, line); | |
for (x = 0; x < nWidth; ++x) { | |
bd.setPixel(x, y, buf.readUnsignedByte() | |
+ (buf.readUnsignedByte() << 8) + (buf.readUnsignedByte() << 16)); | |
} | |
} | |
} catch (e:IOError) { | |
throw new VerifyError("invalid image data"); | |
} | |
} | |
/** | |
* decode 32bit BMP | |
*/ | |
private function decode32BitBMP():void | |
{ | |
var x:int; | |
var y:int; | |
var col:int; | |
try { | |
for (y = nHeight - 1; y >= 0; --y) { | |
for (x = 0; x < nWidth; ++x) { | |
col = bytes.readUnsignedInt(); | |
bd.setPixel(x, y, (((col & nRMask) >> nRPos) * 0xff / nRMax << 16) | |
+ (((col & nGMask) >> nGPos) * 0xff / nGMax << 8) | |
+ (((col & nBMask) >> nBPos) * 0xff / nBMax << 0)); | |
} | |
} | |
} catch (e:IOError) { | |
throw new VerifyError("invalid image data"); | |
} | |
} | |
/** | |
* check color mask | |
*/ | |
private function checkColorMask():void | |
{ | |
if ((nRMask & nGMask) | (nGMask & nBMask) | (nBMask & nRMask)) { | |
throw new VerifyError("invalid bit fields"); | |
} | |
while (((nRMask >> nRPos) & 0x00000001) == 0) { | |
nRPos++; | |
} | |
while (((nGMask >> nGPos) & 0x00000001) == 0) { | |
nGPos++; | |
} | |
while (((nBMask >> nBPos) & 0x00000001) == 0) { | |
nBPos++; | |
} | |
nRMax = nRMask >> nRPos; | |
nGMax = nGMask >> nGPos; | |
nBMax = nBMask >> nBPos; | |
} | |
/** | |
* print information | |
*/ | |
public function traceInfo():void { | |
trace("---- FILE HEADER ----"); | |
trace("nFileSize: " + nFileSize); | |
trace("nReserved1: " + nReserved1); | |
trace("nReserved2: " + nReserved2); | |
trace("nOffbits: " + nOffbits); | |
trace("---- INFO HEADER ----"); | |
trace("nWidth: " + nWidth); | |
trace("nHeight: " + nHeight); | |
trace("nPlains: " + nPlains); | |
trace("nBitsPerPixel: " + nBitsPerPixel); | |
if (nInfoSize >= 40) { | |
trace("nCompression: " + nCompression); | |
trace("nSizeImage: " + nSizeImage); | |
trace("nXPixPerMeter: " + nXPixPerMeter); | |
trace("nYPixPerMeter: " + nYPixPerMeter); | |
trace("nColorUsed: " + nColorUsed); | |
trace("nColorUsed: " + nColorImportant); | |
} | |
if (nInfoSize >= 52) { | |
trace("nRMask: " + nRMask.toString(2)); | |
trace("nGMask: " + nGMask.toString(2)); | |
trace("nBMask: " + nBMask.toString(2)); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// .... | |
var bytes:ByteArray = file.data; | |
// BMP? | |
if (/\.bmp/i.test(file.name)) { | |
var bmpDecoder:BMPDecoder = new BMPDecoder(); | |
var bitmapData:BitmapData = bmpDecoder.decode(file.data); | |
var pngEncoder:PNGEncoder = new PNGEncoder(); | |
bytes = pngEncoder.encode(bitmapData); | |
} | |
layer.loadBytes(bytes, context); | |
// .... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment