Flex/AIR/AS3/AIR2009.10.11 17:26

[출처] - 오창훈님 블로그 (http://lovedev.tistory.com/)

요즘 한참 파일을 바이트 단위로 읽어서 표현하는 방법에 대해 공부하던 중 오창훈님의 블로그에 올라와 있는 글을 보고
발견한 내용입니다.

BMP 파일을 읽는 예제
	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()

			Loads and reads a 24 Bit bitmap file.
			Based on BMP info from:
		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

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

			//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. = new Vector.(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
					fs.position += padding;

			//done reading file, close stream.

			//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

역시 그래픽 파일의 특성상 LITTLE_ENDIAN으로 바이트를 읽어들입니다. JPEG 같은 경우에는 BIG_ENDIAN이라 한참 헷갈렸던 적이 있었던 기억이 나네요 ^^;

참고 : http://www.mikechambers.com/blog/2009/09/17/parsing-and-displaying-bmp-files-via-actionscript/
예제 다운로드 : http://www.mikechambers.com/blog/files/examples/BMPViewer.zip

오창훈님 블로그에 bmd.setVector (rect, pixels); setVector는 bmd.setPixel보다 빠른 퍼포먼스를 낸다고 조언을 해주셨네요 ^^;
전 setVector 보다 setPixel이 더 퍼포먼스가 좋다고 생각하고 있었는데 역시나 아직 갈길이 멉니다 ㅜ_ㅜ

현재 파일들 구조들에 대해 공부하고 있는데 최종 목표는 flex에서 각 파일 포멧들 간의 변경과 그래픽 파일들로 플래쉬 또는 동영상 파일로 컨버트 할 수 있는 프로그램을 만들려고 하는데 파일들 구조가 생각보다 쉽지 않네요 ㅜ_ㅜ 언제나 완성할 수 있을지 걱정입니다 ㅜ_ㅜ
Posted by 코멧'★

티스토리 툴바