Using BMPDecoder class to Load an External BMP File(RLE Compression Support)

k-paint The flash.display.Bitmap class represents display objects that represent bitmap images. These can be images that you load with the flash.display.Loader class, or they can be images that you create with the Bitmap() constructor. Similar to Bitmap class, BMP files decoder class BMPDecoder read bmp file and display (written by munegon), RLE compression be supported. The usage is very simple:

  1. function loadBMPFile( url:String ):void {
  2.   var loader:URLLoader = new URLLoader();
  3.   loader.dataFormat = URLLoaderDataFormat.BINARY;
  4.   loader.addEventListener( Event.COMPLETE, onCompleteLoad );
  5.   loader.load( new URLRequest( url ) );
  6. }
  7. function onCompleteLoad( e:Event ):void {
  8.   var loader:URLLoader = e.target as URLLoader;
  9.   var decoder:BMPDecoder = new BMPDecoder();
  10.   var bd:BitmapData = decoder.decode( loader.data );
  11. }

The following is source code of BMPDecoder class :

Download: BMPDecoder.as
  1. function loadBMPFile( url:String ):void {
  2.   var loader:URLLoader = new URLLoader();
  3.   loader.dataFormat = URLLoaderDataFormat.BINARY;
  4.   loader.addEventListener( Event.COMPLETE, onCompleteLoad );
  5.   loader.load( new URLRequest( url ) );
  6. }
  7. function onCompleteLoad( e:Event ):void {
  8.   var loader:URLLoader = e.target as URLLoader;
  9.   var decoder:BMPDecoder = new BMPDecoder();
  10.   var bd:BitmapData = decoder.decode( loader.data );
  11. }
  12.  
  13.  
  14. The following is source code of BMPDecoder class :
  15.  
  16. package com.voidelement.images {
  17.   import flash.display.BitmapData;
  18.   import flash.errors.IOError;
  19.   import flash.utils.ByteArray;
  20.   import flash.utils.Endian;
  21.   public class BMPDecoder {
  22.     //___________________________________________________________ const
  23.     private const BITMAP_HEADER_TYPE:String = "BM";
  24.     private const BITMAP_FILE_HEADER_SIZE:int = 14;
  25.     private const BITMAP_CORE_HEADER_SIZE:int = 12;
  26.     private const BITMAP_INFO_HEADER_SIZE:int = 40;
  27.     private const COMP_RGB      :int = 0;
  28.     private const COMP_RLE8     :int = 1;
  29.     private const COMP_RLE4     :int = 2;
  30.     private const COMP_BITFIELDS:int = 3;
  31.     private const BIT1 :int = 1;
  32.     private const BIT4 :int = 4;
  33.     private const BIT8 :int = 8;
  34.     private const BIT16:int = 16;
  35.     private const BIT24:int = 24;
  36.     private const BIT32:int = 32;
  37.     //___________________________________________________________ vars
  38.     private var bytes:ByteArray;
  39.     private var palette:Array;
  40.     private var bd:BitmapData;
  41.     private var nFileSize:uint;
  42.     private var nReserved1:uint;
  43.     private var nReserved2:uint;
  44.     private var nOffbits:uint;
  45.     private var nInfoSize:uint;
  46.     private var nWidth:int;
  47.     private var nHeight:int;
  48.     private var nPlains:uint;
  49.     private var nBitsPerPixel:uint;
  50.     private var nCompression:uint;
  51.     private var nSizeImage:uint;
  52.     private var nXPixPerMeter:int;
  53.     private var nYPixPerMeter:int;
  54.     private var nColorUsed:uint;
  55.     private var nColorImportant:uint;
  56.     private var nRMask:uint;
  57.     private var nGMask:uint;
  58.     private var nBMask:uint;
  59.     private var nRPos:uint;
  60.     private var nGPos:uint;
  61.     private var nBPos:uint;
  62.     private var nRMax:uint;
  63.     private var nGMax:uint;
  64.     private var nBMax:uint;
  65.     /**
  66.      * Constructor
  67.      */
  68.     public function BMPDecoder() {
  69.       nRPos = 0;
  70.       nGPos = 0;
  71.       nBPos = 0;
  72.     }
  73.     /**
  74.      * decoder
  75.      *
  76.      * @param BMP file ByteArray
  77.      */
  78.     public function decode( data:ByteArray ):BitmapData {
  79.       bytes = data;
  80.       bytes.endian = Endian.LITTLE_ENDIAN;
  81.       bytes.position = 0;
  82.       readFileHeader();
  83.       nInfoSize = bytes.readUnsignedInt();
  84.       switch ( nInfoSize ) {
  85.         case BITMAP_CORE_HEADER_SIZE:
  86.           readCoreHeader();
  87.           break;
  88.         case BITMAP_INFO_HEADER_SIZE:
  89.           readInfoHeader();
  90.           break;
  91.         default:
  92.           readExtendedInfoHeader();
  93.           break;
  94.       }
  95.       bd = new BitmapData( nWidth, nHeight );
  96.       switch ( nBitsPerPixel ){
  97.         case BIT1:
  98.           readColorPalette();
  99.           decode1BitBMP();
  100.           break;
  101.         case BIT4:
  102.           readColorPalette();
  103.           if ( nCompression == COMP_RLE4 ){
  104.             decode4bitRLE();
  105.           } else {
  106.             decode4BitBMP();
  107.           }
  108.           break;
  109.         case BIT8:
  110.           readColorPalette();
  111.           if ( nCompression == COMP_RLE8 ){
  112.             decode8BitRLE();
  113.           } else {
  114.             decode8BitBMP();
  115.           }
  116.           break;
  117.         case BIT16:
  118.           readBitFields();
  119.           checkColorMask();
  120.           decode16BitBMP();
  121.           break;
  122.         case BIT24:
  123.           decode24BitBMP();
  124.           break;
  125.         case BIT32:
  126.           readBitFields();
  127.           checkColorMask();
  128.           decode32BitBMP();
  129.           break;
  130.         default:
  131.           throw new VerifyError("invalid bits per pixel : " + nBitsPerPixel );
  132.       }
  133.       return bd;
  134.     }
  135.     /**
  136.      * read BITMAP FILE HEADER
  137.      */
  138.     private function readFileHeader():void {
  139.       var fileHeader:ByteArray = new ByteArray();
  140.       fileHeader.endian = Endian.LITTLE_ENDIAN;
  141.       try {
  142.         bytes.readBytes( fileHeader, 0, BITMAP_FILE_HEADER_SIZE );
  143.         if ( fileHeader.readUTFBytes( 2 ) != BITMAP_HEADER_TYPE ){
  144.           throw new VerifyError("invalid bitmap header type");
  145.         }
  146.         nFileSize  = fileHeader.readUnsignedInt();
  147.         nReserved1 = fileHeader.readUnsignedShort();
  148.         nReserved2 = fileHeader.readUnsignedShort();
  149.         nOffbits   = fileHeader.readUnsignedInt();
  150.       } catch ( e:IOError ) {
  151.         throw new VerifyError("invalid file header");
  152.       }
  153.     }
  154.     /**
  155.      * read BITMAP CORE HEADER
  156.      */
  157.     private function readCoreHeader():void {
  158.       var coreHeader:ByteArray = new ByteArray();
  159.       coreHeader.endian = Endian.LITTLE_ENDIAN;
  160.       try {
  161.         bytes.readBytes( coreHeader, 0, BITMAP_CORE_HEADER_SIZE - 4 );
  162.         nWidth  = coreHeader.readShort();
  163.         nHeight = coreHeader.readShort();
  164.         nPlains = coreHeader.readUnsignedShort();
  165.         nBitsPerPixel = coreHeader.readUnsignedShort();
  166.       } catch ( e:IOError ) {
  167.         throw new VerifyError("invalid core header");
  168.       }
  169.     }
  170.     /**
  171.      * read BITMAP INFO HEADER
  172.      */
  173.     private function readInfoHeader():void {
  174.       var infoHeader:ByteArray = new ByteArray();
  175.       infoHeader.endian = Endian.LITTLE_ENDIAN;
  176.       try {
  177.         bytes.readBytes( infoHeader, 0, BITMAP_INFO_HEADER_SIZE - 4 );
  178.         nWidth  = infoHeader.readInt();
  179.         nHeight = infoHeader.readInt();
  180.         nPlains = infoHeader.readUnsignedShort();
  181.         nBitsPerPixel = infoHeader.readUnsignedShort();
  182.         nCompression = infoHeader.readUnsignedInt();
  183.         nSizeImage = infoHeader.readUnsignedInt();
  184.         nXPixPerMeter = infoHeader.readInt();
  185.         nYPixPerMeter = infoHeader.readInt();
  186.         nColorUsed = infoHeader.readUnsignedInt();
  187.         nColorImportant = infoHeader.readUnsignedInt();
  188.       } catch ( e:IOError ) {
  189.         throw new VerifyError("invalid info header");
  190.       }
  191.     }
  192.     /**
  193.      * read the extend info of BITMAP INFO HEADER
  194.      */
  195.     private function readExtendedInfoHeader():void {
  196.       var infoHeader:ByteArray = new ByteArray();
  197.       infoHeader.endian = Endian.LITTLE_ENDIAN;
  198.       try {
  199.         bytes.readBytes( infoHeader, 0, nInfoSize - 4 );
  200.         nWidth  = infoHeader.readInt();
  201.         nHeight = infoHeader.readInt();
  202.         nPlains = infoHeader.readUnsignedShort();
  203.         nBitsPerPixel = infoHeader.readUnsignedShort();
  204.         nCompression = infoHeader.readUnsignedInt();
  205.         nSizeImage = infoHeader.readUnsignedInt();
  206.         nXPixPerMeter = infoHeader.readInt();
  207.         nYPixPerMeter = infoHeader.readInt();
  208.         nColorUsed = infoHeader.readUnsignedInt();
  209.         nColorImportant = infoHeader.readUnsignedInt();
  210.         if ( infoHeader.bytesAvailable >= 4 ) nRMask = infoHeader.readUnsignedInt();
  211.         if ( infoHeader.bytesAvailable >= 4 ) nGMask = infoHeader.readUnsignedInt();
  212.         if ( infoHeader.bytesAvailable >= 4 ) nBMask = infoHeader.readUnsignedInt();
  213.       } catch ( e:IOError ) {
  214.         throw new VerifyError("invalid info header");
  215.       }
  216.     }
  217.     /**
  218.      * read bitfields
  219.      */
  220.     private function readBitFields():void {
  221.       if ( nCompression == COMP_RGB ){
  222.         if ( nBitsPerPixel == BIT16 ){
  223.           // RGB555
  224.           nRMask = 0x00007c00;
  225.           nGMask = 0x000003e0;
  226.           nBMask = 0x0000001f;
  227.         } else {
  228.           //RGB888;
  229.           nRMask = 0x00ff0000;
  230.           nGMask = 0x0000ff00;
  231.           nBMask = 0x000000ff;
  232.         }
  233.       } else if ( ( nCompression == COMP_BITFIELDS ) && ( nInfoSize < 52 ) ){
  234.         try {
  235.           nRMask = bytes.readUnsignedInt();
  236.           nGMask = bytes.readUnsignedInt();
  237.           nBMask = bytes.readUnsignedInt();
  238.         } catch ( e:IOError ) {
  239.           throw new VerifyError("invalid bit fields");
  240.         }
  241.       }
  242.     }
  243.     /**
  244.      * read color palette
  245.      */
  246.     private function readColorPalette():void {
  247.       var i:int;
  248.       var len:int = ( nColorUsed > 0 ) ? nColorUsed : Math.pow( 2, nBitsPerPixel );
  249.       palette = new Array( len );
  250.       for ( i = 0; i < len; ++i ){
  251.         palette[ i ] = bytes.readUnsignedInt();
  252.       }
  253.     }
  254.     /**
  255.      * decode 1 bit BMP
  256.      */
  257.     private function decode1BitBMP():void {
  258.       var x:int;
  259.       var y:int;
  260.       var i:int;
  261.       var col:int;
  262.       var buf:ByteArray = new ByteArray();
  263.       var line:int = nWidth / 8;
  264.       if ( line % 4 > 0 ){
  265.         line = ( ( line / 4 | 0 ) + 1 ) * 4;
  266.       }
  267.       try {
  268.         for ( y = nHeight - 1; y >= 0; --y ){
  269.           buf.length = 0;
  270.           bytes.readBytes( buf, 0, line );
  271.           for ( x = 0; x < nWidth; x += 8 ){
  272.             col = buf.readUnsignedByte();
  273.             for ( i = 0; i < 8; ++i ){
  274.               bd.setPixel( x + i, y, palette[ col >> ( 7 - i ) & 0x01 ] );
  275.             }
  276.           }
  277.         }
  278.       } catch ( e:IOError ) {
  279.         throw new VerifyError("invalid image data");
  280.       }
  281.     }
  282.     /**
  283.      * decode 4bit RLE
  284.      */
  285.     private function decode4bitRLE():void {
  286.       var x:int;
  287.       var y:int;
  288.       var i:int;
  289.       var n:int;
  290.       var col:int;
  291.       var data:uint;
  292.       var buf:ByteArray = new ByteArray();
  293.       try {
  294.         for ( y = nHeight - 1; y >= 0; --y ){
  295.           buf.length = 0;
  296.           while ( bytes.bytesAvailable > 0 ){
  297.             n = bytes.readUnsignedByte();
  298.             if ( n > 0 ){
  299.               // encode data
  300.               data = bytes.readUnsignedByte();
  301.               for ( i = 0; i < n/2; ++i ){
  302.                 buf.writeByte( data );
  303.               }
  304.             } else {
  305.               n = bytes.readUnsignedByte();
  306.               if ( n > 0 ){
  307.                 // abs mode
  308.                 bytes.readBytes( buf, buf.length, n/2 );
  309.                 buf.position += n/2;
  310.                 if ( n/2 + 1 >> 1 << 1 != n/2 ){
  311.                   bytes.readUnsignedByte();
  312.                 }
  313.               } else {
  314.                 // EOL
  315.                 break;
  316.               }
  317.             }
  318.           }
  319.           buf.position = 0;
  320.           for ( x = 0; x < nWidth; x += 2 ){
  321.             col = buf.readUnsignedByte();
  322.             bd.setPixel( x, y, palette[ col >> 4 ] );
  323.             bd.setPixel( x + 1, y, palette[ col & 0x0f ] );
  324.           }
  325.         }
  326.       } catch ( e:IOError ) {
  327.         throw new VerifyError("invalid image data");
  328.       }
  329.     }
  330.     /**
  331.      * decode 4bit (no Compression)
  332.      */
  333.     private function decode4BitBMP():void {
  334.       var x:int;
  335.       var y:int;
  336.       var i:int;
  337.       var col:int;
  338.       var buf:ByteArray = new ByteArray();
  339.       var line:int = nWidth / 2;
  340.       if ( line % 4 > 0 ){
  341.         line = ( ( line / 4 | 0 ) + 1 ) * 4;
  342.       }
  343.       try {
  344.         for ( y = nHeight - 1; y >= 0; --y ){
  345.           buf.length = 0;
  346.           bytes.readBytes( buf, 0, line );
  347.           for ( x = 0; x < nWidth; x += 2 ){
  348.             col = buf.readUnsignedByte();
  349.             bd.setPixel( x, y, palette[ col >> 4 ] );
  350.             bd.setPixel( x + 1, y, palette[ col & 0x0f ] );
  351.           }
  352.         }
  353.       } catch ( e:IOError ) {
  354.         throw new VerifyError("invalid image data");
  355.       }
  356.     }
  357.     /**
  358.      * decode 8bit( RLE Compression )
  359.      */
  360.     private function decode8BitRLE():void {
  361.       var x:int;
  362.       var y:int;
  363.       var i:int;
  364.       var n:int;
  365.       var col:int;
  366.       var data:uint;
  367.       var buf:ByteArray = new ByteArray();
  368.       try {
  369.         for ( y = nHeight - 1; y >= 0; --y ){
  370.           buf.length = 0;
  371.           while ( bytes.bytesAvailable > 0 ){
  372.             n = bytes.readUnsignedByte();
  373.             if ( n > 0 ){
  374.               // encode data
  375.               data = bytes.readUnsignedByte();
  376.               for ( i = 0; i < n; ++i ){
  377.                 buf.writeByte( data );
  378.               }
  379.             } else {
  380.               n = bytes.readUnsignedByte();
  381.               if ( n > 0 ){
  382.                 // abs mode data
  383.                 bytes.readBytes( buf, buf.length, n );
  384.                 buf.position += n;
  385.                 if ( n + 1 >> 1 << 1 != n ){
  386.                   bytes.readUnsignedByte();
  387.                 }
  388.               } else {
  389.                 // EOL
  390.                 break;
  391.               }
  392.             }
  393.           }
  394.           buf.position = 0;
  395.           for ( x = 0; x < nWidth; ++x ){
  396.             bd.setPixel( x, y, palette[ buf.readUnsignedByte() ] );
  397.           }
  398.         }
  399.       } catch ( e:IOError ) {
  400.         throw new VerifyError("invalid image data");
  401.       }
  402.     }
  403.     /**
  404.      * decode 8bit(no Compression)
  405.      */
  406.     private function decode8BitBMP():void {
  407.       var x:int;
  408.       var y:int;
  409.       var i:int;
  410.       var col:int;
  411.       var buf:ByteArray = new ByteArray();
  412.       var line:int = nWidth;
  413.       if ( line % 4 > 0 ){
  414.         line = ( ( line / 4 | 0 ) + 1 ) * 4;
  415.       }
  416.       try {
  417.         for ( y = nHeight - 1; y >= 0; --y ){
  418.           buf.length = 0;
  419.           bytes.readBytes( buf, 0, line );
  420.           for ( x = 0; x < nWidth; ++x ){
  421.             bd.setPixel( x, y, palette[ buf.readUnsignedByte() ] );
  422.           }
  423.         }
  424.       } catch ( e:IOError ) {
  425.         throw new VerifyError("invalid image data");
  426.       }
  427.     }
  428.     /**
  429.      * decode 16bit
  430.      */
  431.     private function decode16BitBMP():void {
  432.       var x:int;
  433.       var y:int;
  434.       var col:int;
  435.       try {
  436.         for ( y = nHeight - 1; y >= 0; --y ){
  437.           for ( x = 0; x < nWidth; ++x ){
  438.             col = bytes.readUnsignedShort();
  439.             bd.setPixel( x, y, ( ( ( col & nRMask ) >> nRPos )*0xff/nRMax << 16 ) + ( ( ( col & nGMask ) >> nGPos )*0xff/nGMax << 8 ) + ( ( ( col & nBMask ) >> nBPos )*0xff/nBMax << 0 ) );
  440.           }
  441.         }
  442.       } catch ( e:IOError ) {
  443.         throw new VerifyError("invalid image data");
  444.       }
  445.     }
  446.     /**
  447.      * decode 24bit BMP
  448.      */
  449.     private function decode24BitBMP():void {
  450.       var x:int;
  451.       var y:int;
  452.       var col:int;
  453.       var buf:ByteArray = new ByteArray();
  454.       var line:int = nWidth * 3;
  455.       if ( line % 4 > 0 ){
  456.         line = ( ( line / 4 | 0 ) + 1 ) * 4;
  457.       }
  458.       try {
  459.         for ( y = nHeight - 1; y >= 0; --y ){
  460.           buf.length = 0;
  461.           bytes.readBytes( buf, 0, line );
  462.           for ( x = 0; x < nWidth; ++x ){
  463.             bd.setPixel( x, y, buf.readUnsignedByte() + ( buf.readUnsignedByte() << 8 ) + ( buf.readUnsignedByte() << 16 ) );
  464.           }
  465.         }
  466.       } catch ( e:IOError ) {
  467.         throw new VerifyError("invalid image data");
  468.       }
  469.     }
  470.     /**
  471.      * decode 32bit BMP
  472.      */
  473.     private function decode32BitBMP():void {
  474.       var x:int;
  475.       var y:int;
  476.       var col:int;
  477.       try {
  478.         for ( y = nHeight - 1; y >= 0; --y ){
  479.           for ( x = 0; x < nWidth; ++x ){
  480.             col = bytes.readUnsignedInt();
  481.             bd.setPixel( x, y, ( ( ( col & nRMask ) >> nRPos )*0xff/nRMax << 16 ) + ( ( ( col & nGMask ) >> nGPos )*0xff/nGMax << 8 ) + ( ( ( col & nBMask ) >> nBPos )*0xff/nBMax << 0 ) );
  482.           }
  483.         }
  484.       } catch ( e:IOError ) {
  485.         throw new VerifyError("invalid image data");
  486.       }
  487.     }
  488.     /**
  489.      * check color mask
  490.      */
  491.     private function checkColorMask():void {
  492.       if ( ( nRMask & nGMask ) | ( nGMask & nBMask ) | ( nBMask & nRMask ) ){
  493.         throw new VerifyError("invalid bit fields");
  494.       }
  495.       while ( ( ( nRMask >> nRPos ) & 0x00000001 ) == 0 ){
  496.         nRPos++;
  497.       }
  498.       while ( ( ( nGMask >> nGPos ) & 0x00000001 ) == 0 ){
  499.         nGPos++;
  500.       }
  501.       while ( ( ( nBMask >> nBPos ) & 0x00000001 ) == 0 ){
  502.         nBPos++;
  503.       }
  504.       nRMax = nRMask >> nRPos;
  505.       nGMax = nGMask >> nGPos;
  506.       nBMax = nBMask >> nBPos;
  507.     }
  508.     /**
  509.      * print information
  510.      */
  511.     public function traceInfo():void {
  512.       trace("---- FILE HEADER ----");
  513.       trace("nFileSize: " + nFileSize );
  514.       trace("nReserved1: " + nReserved1 );
  515.       trace("nReserved2: " + nReserved2 );
  516.       trace("nOffbits: " + nOffbits );
  517.       trace("---- INFO HEADER ----");
  518.       trace("nWidth: " + nWidth );
  519.       trace("nHeight: " + nHeight );
  520.       trace("nPlains: " + nPlains );
  521.       trace("nBitsPerPixel: " + nBitsPerPixel );
  522.       if ( nInfoSize >= 40 ){
  523.         trace("nCompression: " + nCompression );
  524.         trace("nSizeImage: " + nSizeImage );
  525.         trace("nXPixPerMeter: " + nXPixPerMeter );
  526.         trace("nYPixPerMeter: " + nYPixPerMeter );
  527.         trace("nColorUsed: " + nColorUsed );
  528.         trace("nColorUsed: " + nColorImportant );
  529.       }
  530.       if ( nInfoSize >= 52 ){
  531.         trace("nRMask: " + nRMask.toString( 2 ) );
  532.         trace("nGMask: " + nGMask.toString( 2 ) );
  533.         trace("nBMask: " + nBMask.toString( 2 ) );
  534.       }
  535.     }
  536.   }
  537. }

Cheers!

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DZone
  • Reddit
  • Technorati
  • StumbleUpon
  • Twitter
RSS Enjoy this Post? Subscribe to Ntt.cc

RSS Feed   RSS Feed     Email Feed  Email Feed Follow us Follow us
You can leave a response, or trackback from your own site.

2 Responses to “Using BMPDecoder class to Load an External BMP File(RLE Compression Support)”

  1. cyberlox says:

    I am relatively new to Flex programming. After searching in vain for a way to display 256-color bitmaps in Flex I came across this excellent bitmap decoder. Thank you so much for this! It would have cost me a lot of work to decode a.bmp file from scratch.

  2. lildadou says:

    Error on line 263.
    Correction : var line:int = Math.ceil(nWidth / 8);

    Error commited on trac’s libspark.org

Leave a Reply