Archive for Сентябрь, 2009

Парсинг BMP средствами ActionScript

Майк Чамберс в своём блоге показал как парсить обыкновенный .bmp-файл несколькими строками ActionScript-кода.

Оказывается, делается это следующим образом:

package
{
    import flash.filesystem.File;
    import flash.filesystem.FileStream;
    import flash.filesystem.FileMode;

    import flash.display.Sprite;
    import flash.display.BitmapData;
    import flash.display.Bitmap;
    import flash.utils.Endian;

    import flash.geom.Rectangle;

    [SWF(width='550', height='400', backgroundColor='#FFFFFF', frameRate='12')]
    public class BMPViewer extends Sprite
    {
        private static const MAGIC_NUMBER:String = "BM";
        private static const BMP_DATA_OFFSET_POSITION:int = 0xA;
        private static const WIDTH_POSITION:int = 0x12;
        private static const HEIGHT_POSITION:int = 0x16;

        public function BMPViewer()
        {
            loadBMP();
            super();
        }

        /*
            Loads and reads a 24 Bit bitmap file.
            
            Based on BMP info from:
            http://en.wikipedia.org/wiki/BMP%5Ffile%5Fformat
        */
        private function loadBMP():void
        {
            //Load BMP. This requires AIR.
            //Use FileReference.browse for
            //Flash Player
            var bmpFile:File = new File("app:/image.bmp");
            var fs:FileStream = new FileStream();

            //BMP files are Little Endian, which means their
            //least significant byte is first (right to left)
            fs.endian = Endian.LITTLE_ENDIAN;

            //open the file in READ mode
            fs.open(bmpFile, FileMode.READ);

            //check the first two bytes to make sure
            //it is a valid BMP file
            if(fs.readUTFBytes(2) != MAGIC_NUMBER)
            {
                trace("FAIL : NOT A BMP FILE");

                //not a BMP file, close steam
                //and exit
                fs.close();
                return;
            }

            //note, we could also grab the length from the 
            //header and make sure the file was the correct
            //length

            //change the cursors position to the point
            //in the header that contains the value / offset
            //of where the actual bitmap data begins
            //read in the 4 Bytes that contain the value
            fs.position = BMP_DATA_OFFSET_POSITION;
            var dataPosition:int = fs.readInt();

            //set cursor position to where the BMP
            //width is stored
            fs.position = WIDTH_POSITION;

            //read in the 4 Bytes that contain the width
            var bmpWidth:int = fs.readInt();

            //read in the 4 Bytes that contain the height
            var bmpHeight:int = fs.readInt();

            //set cursor to where the BMP pixel data begins
            fs.position = dataPosition;

            var row:int = 0;
            var column:int = 0;

            //every row length in a BMP file must bee a multiple
            //of 4 (see the spec). So, we need to determine how much
            //padding we need to add at the end of each line. 
            var padding:int = (bmpWidth % 4);

            //create a fixed length Vector to store the pixel
            //values as we read them.
            var pixels:Vector.<uint> = new Vector.<uint>(bmpWidth * bmpHeight, true);

            //loop through data (rows and columns)
            //note that data stored in BMP is backwards to Flash and is
            //stored from bottom row up, not top row down.
            //So we have to loop backwards
            var counter:int = 0;
            for(var i:int = bmpHeight; i > 0; i--)
            {
                for(var k:int = 0; k < bmpWidth; k++)
                {

                    var position:int = ((i - 1) * bmpWidth) + k;
                    /*
                        This is the original code that I had which works fine
                        but is not as effecient as what I have now.
                        
                        Basically, Pixels are stored within 3 sucessive Bytes
                        in a BMP file, with one Byte each for Blue, Green and
                        Red values (in that order).
                        
                        So, this reads the Bytes for each pixel, one at a time
                        and then combines them into a single value which is
                        the combined RGB pixel value.
                        
                        I left the code as I think it make it a little easier to
                        understand what is going on, as well as how some of these
                        calls can be optimized.
                    */

                    /*
                    var blue:int = fs.readUnsignedByte();
                    var green:int = fs.readUnsignedByte();
                    var red:int = fs.readUnsignedByte();
                    
                    pixels[position] = (red << 16 ^ green << 8 ^ blue);
                    */

                    /*
                        Here is the final code which is more efficient, as it only
                        needs to make 2 read calls in order to get the values.
                        
                        Thanks to Thibault Imbert (bytearray.org) for pointing out
                        and helping me understand the optimization.
                    */

                    //bytes in file are in Blue, Green, Red order
                    //int is 32 bits (8 bytes). So, we store the first two bytes of the pixel
                    // (which contain the Red value), and
                    //then shift everything over 1 byte (8bits) to make room for
                    //the green and blue values (remember the file is little endian), which we
                    // then write into the int in the right position
                    //The final value has the colors in the correct order (Red, Green, Blue)

                    var pixelValue:uint = fs.readUnsignedByte() | fs.readUnsignedShort() << 8;
                    pixels[position] = pixelValue;
                }

                //we are at the end of the row, so now we have to move the cursor
                //forward so it ends on a multiple of 4
                if(padding)
                {
                    fs.position += padding;
                }
            }

            //done reading file, close stream.
            fs.close();

            //create a Rectangle with width / height of Bitmap
            var rect:Rectangle = new Rectangle(0, 0, bmpWidth, bmpHeight);

            //create the BitmapData object to hold hold the BMP data.
            //we do a red fill here so it is easier to see if we have any errors
            //in our code
            var bmpData:BitmapData = new BitmapData(bmpWidth, bmpHeight, false, 0xFF0000);

            //copy the BMP pixel data into the BitmapData
            bmpData.setVector(rect, pixels);

            //create a new Bitmap instance using the BitmapData
            var bitmap:Bitmap = new Bitmap(bmpData);
            bitmap.x = 10;
            bitmap.y = 10;

            //add Bitmap to the display list
            addChild(bitmap);
        }
    }
}

Готовый пример тоже можно скачать, что я уже сделала и терорризирую авиатора, поскольку он спец по AS.

Представление Flash Catalyst

22 примера Tour de Flex для Flex 4

Грегори Уилсон обновил свой проект Tour de Flex – теперь там представлено сразу 22 примера, написанных на Flex 4.