BMP load/save code

BlitzPlus Forums/BlitzPlus Programming/BMP load/save code

Andy(Posted 2003) [#1]
Hi,

Has anyone made any code to read BMP files?

I need to be able to read, work on and save individual pixels.

Andy


WolRon(Posted 2003) [#2]
Someone was saying somewhere that bmp files are stored in 32bit integers (8 bits nothing, 8 bits red, 8 bits green, 8 bits blue) and read from bottom to top. I would wager that that's all you need to know.


WolRon(Posted 2003) [#3]
Guess I was somewhat correct (they aren't 32bit).
Did some tests, came up with the following:


54 bytes to header

byte 1 is "B"
byte 2 is "M"
bytes 19-22 is width (32bit integer)
bytes 23-26 is height (32bit integer)
byte 29 is bits/pixel

8 bit or lower images have color table immediately following header

image data is stored bottom to top, left to right
in 8 bit images as (didn't check) format
in 24 bit images as (8b, 8g, 8r) format (notice NOT RGB)


I wrote up this short code to load in a bmp image and display it:
Graphics 640, 480, 0, 2

filein = ReadFile ("image.bmp")
verify$ = Chr$(ReadByte(filein)) + Chr$(ReadByte(filein))
If verify$ <> "BM" Then RuntimeError("Not a .bmp file")

For y = 1 To 16
  ReadByte(filein)
Next
width = ReadInt(filein)
height = ReadInt(filein)

For y = 1 To 2
  ReadByte(filein)
Next
bpp = ReadByte(filein)

For y = 1 To 25
  ReadByte(filein)
Next

Select bpp
  Case 8
    ;??
  Case 24
    For y = height To 1 Step -1
      For x = 1 To width
        b = ReadByte(filein)
        g = ReadByte(filein)
        r = ReadByte(filein)
        Color r, g, b
        Plot x, y
        ;Plot x*2, y*2
        ;Plot x*2+1, y*2
        ;Plot x*2+1, y*2+1
        ;Plot x*2, y*2+1
      Next 
    Next
End Select

WaitKey()
End


I didn't bother checking into 8 bit images. There's a lot more info in the header that I'm not sure what they're for, so the code just skips through them. Maybe somebody else knows.


Tricky(Posted 2003) [#4]
Maybe some leftovers from the pallete era, you know that you needed a color pallete to set for your image...

The code could be handy... If you need with LoadImage?

Maybe, I could use a certain code because there are some image I don't want to pre-load as they only pop-up in the screen once (like a logo or a title screen)...

All I know about BMP is that's the only format that holds no form of compression in it, and is with that the largest image format in the world... But that's MicroSoft's trademark...


Tricky(Posted 2003) [#5]
Oh yeah, I tried your code... It does not work very well...

It was first black-white, and second the picture is eh... (What's the English word for "Scheef"?).... I'll post a screenshot of the result one of these days...


Andy(Posted 2003) [#6]
>WolRon<
I havent tried it yet, but I really appreciate your code, and interest.

>Tricrokra<
It may not be perfect, but WolRon pointed me in the right direction... I need to process a large BMP pixel by pixel, and I have found no software that can do what I need it to do, so I have to write it myself.

Andy


Snarty(Posted 2003) [#7]
http://www.blitzcoder.com/cgi-bin/showcase/showcase_showentry.pl?id=snarty12082002081455&comments=no

Something I started on, never finished it though, but, could be useful to you.


WolRon(Posted 2003) [#8]
Maybe some leftovers from the pallete era, you know that you needed a color pallete to set for your image...

No, the color palette is seperate from the header and in 8 bit images immediately follows the header. There is no color palette in 24 bit images.


Oh yeah, I tried your code... It does not work very well...

Funny, it worked fine for all of the images I loaded with it. Maybe what you loaded was something other than 24 bit (16, 15, 8, 4, 2)? Is there possibly something such as 32 bit bitmaps?


soja(Posted 2003) [#9]
What's the English word for "Scheef"?


In this case you would probably say "slanted" or "leaning at an angle".

Other possibilities:
oblique, angled, tilted, leaning, skewed, sheared, etc. =)


QuietBloke(Posted 2003) [#10]
Perhaps the images you tried that went wrong were saved with run length encoding ?

this might help explain it in a bit more detail.

http://www.daubnet.com/formats/BMP.html


WolRon(Posted 2003) [#11]
The info on daubnet seems to have some inaccuracies (at least from what I can decipher so far), especially the 24 bit images stored as RGB0 and not needing to be 32bit aligned. From what I can tell so far, 24 bit images are stored as BGR and 32bit aligned at the end of each row (if necessary).

Note to Tricrokra: The initial code I posted wasn't 32bit aligning each row, and so (by chance) all of the files I tried worked but the ones you did didn't. I will post an updated version soon.


WolRon(Posted 2003) [#12]
Something I wrote a while ago and (I think) I forgot to post. Maybe I didn't post it because I wasn't finished. Here's what I came up with so far.
;Bitmap reader/viewer

Dim ColorTable(255, 2)

Graphics 640, 480, 0, 2

.beginning
Cls
Color 255, 255, 255

filename$ = Input ("Enter filename to view:")
If filename$ = "" Then End
If Right$(filename$, 4) <> ".bmp" Then filename$ = filename$ + ".bmp"

Cls

filein = ReadFile (filename$)
If filein = 0 Then RuntimeError("File not found.")

;verify if a .bmp file
Signature$ = Chr$(ReadByte(filein)) + Chr$(ReadByte(filein))
If Signature$ <> "BM" Then RuntimeError("Not a .bmp file")

;read in header info
SizeFile = ReadInt(filein)
ReadInt(filein) ;unused
DataOffset = ReadInt(filein)
InfoSize = ReadInt(filein)
Width = ReadInt(filein)
Height = ReadInt(filein)
Planes = ReadShort(filein)
BitsPerPixel = ReadShort(filein)
CompressionType = ReadInt(filein)
ImageSize = ReadInt(filein)
XpixelsPerMeter = ReadInt(filein)
YpixelsPerMeter = ReadInt(filein)
ColorsUsed = ReadInt(filein)
ColorsImportant = ReadInt(filein)

;set data
NumColors = 2^BitsPerPixel

;read in color table if necessary
If BitsPerPixel <= 8 Then
	;read in color table
	For iter = 0 To NumColors-1
		ColorTable(iter, 0) = ReadByte(filein)
		ColorTable(iter, 1) = ReadByte(filein)
		ColorTable(iter, 2) = ReadByte(filein)
		ReadByte(filein) ;unused
	Next
EndIf

;read in raster data
;select compression type
If CompressionType = 0
	;No compression
	Select BitsPerPixel
		Case 1
			SeekFile(filein, DataOffset)
			align = 3 - (width Mod 4)
			;If align > 0 And align < 3 Then align = 3 - align
			For y = Height To 1 Step -1
				x = 8
				While x < Width
					pixels = ReadByte(filein)
					pixel = (pixels And $80) Shr 7
					Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
					Plot x, y
					pixel = pixels And $40 Shr 6
					Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
					Plot x+1, y
					pixel = (pixels And $20) Shr 5
					Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
					Plot x+2, y
					pixel = pixels And $10 Shr 4
					Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
					Plot x+3, y
					pixel = pixels And $08 Shr 3
					Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
					Plot x+4, y
					pixel = (pixels And $04) Shr 2
					Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
					Plot x+5, y
					pixel = pixels And $02 Shr 1
					Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
					Plot x+6, y
					pixel = pixels And $01
					Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
					Plot x+7, y
					x = x + 8
				Wend 
				If x > Width
					pixels = ReadByte(filein)
					For iter = 1 To x - Width
						Pixel = (Pixels And ($01 Shl (8-iter))) Shr (8-iter)
						Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
						Plot x, y
					Next 
				EndIf
				For iter  = 1 To align
					ReadByte(filein)
				Next
			Next
		Case 4
			SeekFile(filein, DataOffset)
			align = 3 - (width Mod 4)
			If align > 0 And align < 3 Then align = 3 - align
			For y = Height To 1 Step -1
				x = 2
				While x < Width
					pixels = ReadByte(filein)
					pixel = (pixels And $F0) Shr 4
					Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
					Plot x, y
					pixel = pixels And $0F
					Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
					Plot x+1, y
					x = x + 2
				Wend
				If x > Width
					pixel = (ReadByte(filein) And $F0) Shr 4
					Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
					Plot x, y
				EndIf
				For iter  = 1 To align
					ReadByte(filein)
				Next
			Next
		Case 8
			SeekFile(filein, DataOffset)
			align = 4 - (Width Mod 4)
			If align = 4 Then align = 0
			For y = Height To 1 Step -1
				For x = 1 To Width
					pixel = ReadByte(filein)
					Color ColorTable(pixel, 0), ColorTable(pixel, 1), ColorTable(pixel, 2)
					Plot x, y
				Next 
				For iter  = 1 To align
					ReadByte(filein)
				Next
			Next
		Case 16
			SeekFile(filein, DataOffset)
			align = Width Mod 2
			For y = Height To 1 Step -1
				For x = 1 To Width
					pixel = ReadShort(filein)
					b = pixel And $001F
					pixel = pixel Shr 5
					g = pixel And $003F
					pixel = pixel Shr 6
					r = pixel And $001F
					Color r, g, b
					Plot x, y
				Next 
				For iter  = 1 To align
					ReadShort(filein)
				Next
			Next
		Case 24
			SeekFile(filein, DataOffset)
			align = Width Mod 4
			For y = Height To 1 Step -1
				For x = 1 To Width
					b = ReadByte(filein)
					g = ReadByte(filein)
					r = ReadByte(filein)
					Color r, g, b
					Plot x, y
				Next
				For iter  = 1 To align
					ReadByte(filein)
				Next
			Next
		Default
			RuntimeError("Image format (bits per pixel "+BitsPerPixel+") unknown.")	
	End Select
ElseIf CompressionType = 1
	;4bit/16color compression
	Print "4bit/16color RLE bitmap decompression not implemented yet."
ElseIf CompressionType = 2
	;8bit/256color compression
	Print "8bit/256color RLE bitmap decompression not implemented yet."
Else
	RuntimeError("Image format (compression level "+CompressionType+") unknown.")
EndIf

WaitKey()
Goto beginning

End






;Bitmap Information
;
;
;Basic Description
;
;This file format is the MS-Windows standard format. It holds black&white-, 16-Color, 256-Color and Truecolor images.
;The palletized 16-Color and 256-Color images may be compressed via run length encoding. Notice there is also an
;OS/2-BMP format. 
;
;
;--------------------------------------------------------------------------------
;
;Basic File Format
;
;  Name				  Size				  Description 
;
;Header				14 bytes			Windows Structure: BITMAPFILEHEADER 
;	Signature		2 bytes				'BM' 
;	FileSize		4 bytes				File size in bytes 
;	reserved		4 bytes				unused (=0) 
;	DataOffset		4 bytes				File offset To Raster Data 
;
;InfoHeader			40 bytes			Windows Structure: BITMAPINFOHEADER 
;	Size			4 bytes				Size of InfoHeader =40  
;	Width			4 bytes				Bitmap Width 
;	Height			4 bytes				Bitmap Height 
;	Planes			2 bytes				Number of Planes (=1) 
;	BitCount		2 bytes				Bits per Pixel   
;										  1 = monochrome palette. NumColors = 2   
;										  4 = 4bit palletized. NumColors = 16   
;										  8 = 8bit palletized. NumColors = 256  
;										  16 = 16bit RGB. NumColors = 65536  
;										  24 = 24bit RGB. NumColors = 16M 
;	Compression		4 bytes				Type of Compression   
;										  0 = BI_RGB   no compression   
;										  1 = BI_RLE8 8bit RLE encoding   
;										  2 = BI_RLE4 4bit RLE encoding 
;	ImageSize		4 bytes				(compressed) Size of Image  
;										  It is valid to set this =0 if Compression = 0 
;	XpixelsPerM		4 bytes				horizontal resolution: Pixels/meter 
;	YpixelsPerM		4 bytes				vertical resolution: Pixels/meter 
;	ColorsUsed		4 bytes				Number of actually used colors 
;	ColorsImportant	4 bytes				Number of important colors  
;										  0 = all 
;
;ColorTable			4 * NumColors bytes	Present only if Info.BitsPerPixel <= 8   
;										  colors should be ordered by importance 
;	Red				1 byte				Red intensity 
;	Green			1 byte				Green intensity 
;	Blue			1 byte				Blue intensity 
;	reserved		1 byte				unused (=0) 
;  repeated NumColors times 
;
;Raster Data		Info.ImageSize bytes	The pixel Data 
; 
;
;
;--------------------------------------------------------------------------------
;
;Raster Data encoding
;
;Depending on the image's BitCount and on the Compression flag there are 6 different encoding schemes. All of them
;share the following:  
;Pixels are stored bottom-up, left-to-right. Pixel lines are padded with zeros to end on a 32bit (4byte) boundary.
;For uncompressed formats every line will have the same number of bytes. Color indices are zero based, meaning a 
;pixel color of 0 represents the first color table entry, a pixel color of 255 (If there are that many) represents
;the 256th entry. For images with more than 256 colors there is no color table. 
; 
;
;  
;Raster Data encoding for 1bit / black & white images
;
;BitCount = 1 Compression = 0  
;Every byte holds 8 pixels, its highest order bit representing the leftmost pixel of those. There are 2 color table
;entries. Some readers will ignore them though, and assume that 0 is black and 1 is white. If you are storing black
;and white pictures you should stick to this. With any other 2 colors this is not an issue. Remember padding with
;zeros up to a 32bit boundary (This can be up to 31 zeros/pixels!)  
;
;  
;Raster Data encoding for 4bit / 16 Color images
;
;BitCount = 4 Compression = 0  
;Every byte holds 2 pixels, its high order 4 bits representing the left of those. There are 16 Color table entries.
;These colors do not have to be the 16 MS-Windows standard colors. Padding each line with zeros up to a 32bit
;boundary will result in up to 28 zeros = 7 'wasted pixels'. 
;
;  
;Raster Data encoding for 8bit / 256 Color images
;
;BitCount = 8 Compression = 0  
;Every byte holds 1 pixel. There are 256 color table entries. Padding each line with zeros up to a 32bit boundary
;will result in up to 3 bytes of zeros = 3 'wasted pixels'. 
;
;  
;Raster Data encoding for 16bit / hicolor images
;
;BitCount = 16 Compression = 0  
;Every 2bytes / 16bit holds 1 pixel.   
;<information missing: the 16 bit was introduced together with Video For Windows? Is it a memory-only-format?>  
;The pixels are no color table pointers. There are no color table entries. Padding each line with zeros up to a
;16bit boundary will result in up to 2 zero bytes. 
;
;  
;Raster Data encoding for 24bit / truecolor images
;
;BitCount = 24 Compression = 0  
;Every 4bytes / 32bit holds 1 pixel. The first holds its red, the second its green, and the third its blue
;intensity. The fourth byte is reserved and should be zero. There are no color table entries. The pixels are no
;color table pointers. No zero padding necessary. 
;
;  
;Raster Data compression for 4bit / 16 Color images
;
;BitCount = 4 Compression = 2  
;The pixel data is stored in 2bytes / 16bit chunks.  The first of these specifies the number of consecutive pixels
;with the same pair of color. The second byte defines two color indices. The resulting pixel pattern will be
;interleaved high-order 4bits and low order 4 bits (ABABA...). If the first byte is zero, the second defines an
;escape code. The end-of-Bitmap is zero padded to end on a 32bit boundary. Due to the 16bit-ness of this structure
;this will always be either two zero bytes or none.   
;
;  n (byte 1)	  c (Byte 2)	  Description 
;
;	>0				any				n pixels are to be drawn. The 1st, 3rd, 5th, ... pixels' color is in c's
;									  high-order 4 bits, the even pixels' color is in c's low-order 4 bits. If both
;									  color indices are the same, it results in just n pixels of color c 
;	0				0				End-of-Line 
;	0				1				End-of-Bitmap 
;	0				2				Delta. The following 2 bytes define an unsigned offset in x and y direction
;									  (y being up) The skipped pixels should get a color zero. 
;	0				>=3				The following c bytes will be read as single pixel colors just as in
;									  uncompressed files. Up to 12 bits of zeros follow, to put the file/memory
;									  pointer on a 16bit boundary again. 
;
;	  Example For 4bit RLE
;
;  Compressed Data	  Expanded Data 
;
;	03 04				0 4 0 
;	05 06				0 6 0 6 0 
;	00 06 45 56 67 00	4 5 5 6 6 7 
;	04 78				7 8 7 8 
;	00 02 05 01			Move 5 Right And 1 up. (Windows docs say down, which is wrong) 
;	00 00				End-of-Line 
;	09 1E				1 E 1 E 1 E 1 E 1 
;	00 01				EndofBitmap 
;	00 00				Zero padding For 32bit boundary 
;  
;
;  
;Raster Data compression For 8bit / 256 Color images
;
;BitCount = 8 Compression = 1  
;The pixel data is stored in 2bytes / 16bit chunks.  The first of these specifies the number of consecutive pixels
;with the same color. The second byte defines their color index. If the first byte is zero, the second defines an
;escape code. The End-of-Bitmap is zero padded to end on a 32bit boundary. Due to the 16bit-ness of this structure
;this will always be either two zero bytes or none.
;
;  n (byte 1)	  c (Byte 2)	  Description 
;
;	>0				any				n pixels of color number c 
;	0				0				End-of-Line 
;	0				1				End-Of-Bitmap 
;	0				2				Delta. The following 2 bytes define an unsigned offset in x and y direction
;									  (y being up) The skipped pixels should get a color zero. 
;	0				>=3				The following c bytes will be read as single pixel colors just as in
;									  uncompressed files. A zero follows, If c is odd, putting the file/memory
;									  pointer on a 16bit boundary again. 
;
;	  Example For 8bit RLE
;
;  Compressed Data	  Expanded Data 
;
;	03 04				04 04 04 
;	05 06				06 06 06 06 06 
;	00 03 45 56 67 00	45 56 67 
;	02 78				78 78 
;	00 02 05 01			Move 5 right and 1 up. (Windows docs say down, which is wrong) 
;	00 00				End-of-Line 
;	09 1E				1E 1E 1E 1E 1E 1E 1E 1E 1E 
;	00 01				End-of-bitmap 
;	00 00				Zero padding for 32bit boundary 
;  
;
;
;--------------------------------------------------------------------------------
;
;Portability
;Although BMPs were invented by Microsoft for its Windows platform, a lot of programs on other platforms are
;capable of reading and writing them. Notice the Intel order in 2byte and 4-byte integer values (Least significant
;byte first). The 16bit BMPs have been introduced to Windows after the others, still puzzling many applications. 
;
;



;end of file



jfk EO-11110(Posted 2003) [#13]
Maybe a stupid question, but why don't you simply use:

img=loadimage(path$)
savebuffer imagebuffer(img),path2$


Andy(Posted 2003) [#14]
>Maybe a stupid question, but why don't you simply use:

Because fitting a 4GB BMP in 512MB doesn't work, and none of my otherwise excellent imagemanipulation programs can cope without killing Windows.

But an adaptation of the above works, so now I can process the entire image witot killing Windows. It does take a while though.

Andy


Rottbott(Posted 2003) [#15]
That's one biiiiig bitmap. What's it for? :-)


Panno(Posted 2003) [#16]
4G -- roffl use png or jpg lol


Andy(Posted 2003) [#17]
>That's one biiiiig bitmap. What's it for? :-)

Heh... If I told ya, I would have to kill ya! Seriously, I am trying to map all land on the earth, from a giant BMP.

The funny thing is that now I need to do the same on a png, so I have to go make a png load routine in Blitz...

Andy