Loading a BMP file to show
-
- Coder
- Posts: 18
- Joined: Mon Dec 17, 2007 8:41 pm
- Contact:
Loading a BMP file to show
i know there are some good tuts for this such as http://www.petesqbsite.com/sections/tut ... _bmps.html, but my problem is i like to know every little detail and whats going on....it's a lot to ask, but I'd appreciate it if anyone could explain in the code with comments whats actually happening line by line. I just need help reaching the point where i go "Oh! Thats how it works!" and getting it
thank you to anyone in advance,
-Andrew
thank you to anyone in advance,
-Andrew
I can give you some more detailed information. I did not copy the TYPE information because it really doesn't need explaining, and it wouldn't copy with linebreaks anyways. In fact, nothing did...I had to add those manually. Urg! Anyways...
The TYPE you see earlier is just a collection of variables. This line essentially "collects" all of those variables and makes them "children" of another variable, this one called BmpHeader.
I normally don't advocate the use of "#1" for file handles, but dynamic file handles are a subject for another day. Anyways, this just opens the BMP file in "Binary" file mode, which means no special formatting will be done, the file will simply be read byte-for-byte.
This fetches the header information from the BMP file. It will contain all of the information you need to draw the BMP on the screen. All of this information is kept in the variable BmpHeader and can be read by using this format:
PRINT BmpHeader.id
PRINT BmpHeader.hei
etc etc etc.
You know what this does.
BmpHeader.pal contains the image's palette data. This simply assigns that data to a temporary string. Why they do this is beyond me, but whatever.
This is the way the palette registers are affected directly. Port &H3C8 is the index pointer. By setting it to 0, we're telling it to start at index 0, or the very beginning of the palette index.
The palette is stored in segments of 4 bytes. However, only three of those bytes are relevant. So, we STEP 4 to skip through the variable 4 bytes at a time.
Now we begin reading the color data from the variable. We start with the blue. Since it's a string type, and we want numeric values, we use ASC which gives us the numeric value of any string. MID$ will "extract" only the amount of data we need; in this case, 1 byte. At the end of the line, the value has to be reduced to fit into the color rules of the VGA palette. Normally, a BMP will contain a color range of 0 to 255 for each color, but in VGA land, we only have 0 to 63. The \ 4 at the end reduces the color information so it will fit in the palette register. Don't worry, everything will still look fine.
The Green information comes next. Note that the MID$ keyword's parameters now include a + 1. That simply means we're looking at the next byte in the temporary variable we created earlier to hold the palette.
And finally, the red. Note that there is no +3; the fourth byte in the string is "worthlesss" so we don't need to care about it.
Here, all of the values that we collected so far are written to the palette registers through this port, &H3C9. Notice that there isn't another call to &H3C8...conveniently, the palette index increments itself when you write three values to &H3C9, so you don't have to mess with it!
Yep.
Okay, on to the image data. In an 8-bit BMP, each pixel is stored 1 byte at a time. However, since QB doesn't have a BYTE type (huge mistake), the best we can do is STRING * 1 for reading bytes from a file.
This new temporary variable will be used by the loop later to keep track of the Y position, or scanline, of the image. It is given -1 because the PSET later will use screen values, and if you're doing a 320x200 image, you can't write to line 200 on the screen because it doesn't exist! So, reduce this by 1 so it will write from 0-199.
And this one for the X position. Same rules regarding the -1; trying to PSET at the 320th X pixel will break stuff.
Since the image is stored line-by-line, the Y loop comes first. Also, curiously, BMP files are stored upside-down. No one except Microsoft knows what the motive was for this boneheaded move.
Now that Y is taken care of, handle X too.
Here's where we get the pixel information from the BMP. Just one byte needs to be read here, since we're dealing with an 8 bit image.
Now it gets drawn to the screen. Notice the ASC; again, since we need numeric values and we only have a string, it has to be converted to numeric. The data in Pixel corresponds to the index in the palette that we set earlier.
End the loops.
Don't forget to close the file.
As you can see, this is one of the simplest image storage methods available. Rather wasteful, but simple. This code doesn't delve into a lot of things though, such as scanline optimization or even proper byte alignment issues, but it's a start.
Anyways, I hope that was helpful. If you have any other questions, feel free to ask, I'm here all day today.
Code: Select all
DIM BmpHeader AS BMPHeaderType
Code: Select all
OPEN "demo.bmp" FOR BINARY AS #1
Code: Select all
GET #1, , BmpHeader
PRINT BmpHeader.id
PRINT BmpHeader.hei
etc etc etc.
Code: Select all
SCREEN 13
Code: Select all
a$ = BmpHeader.pal
Code: Select all
OUT &H3C8, 0
Code: Select all
FOR I% = 1 TO 1024 STEP 4
Code: Select all
b% = ASC(MID$(a$, I%, 1)) \ 4
Code: Select all
g% = ASC(MID$(a$, I% + 1, 1)) \ 4
Code: Select all
r% = ASC(MID$(a$, I% + 2, 1)) \ 4
Code: Select all
OUT &H3C9, r%
OUT &H3C9, g%
OUT &H3C9, b%
Code: Select all
NEXT
Code: Select all
DIM Pixel AS STRING * 1
Code: Select all
iHeight% = BmpHeader.hei - 1
Code: Select all
iWidth% = BmpHeader.wid - 1
Code: Select all
FOR y% = iHeight% TO 0 STEP -1
Code: Select all
FOR x% = 0 TO iWidth%
Code: Select all
GET #1, , Pixel
Code: Select all
PSET (x%, y%), ASC(Pixel)
Code: Select all
NEXT x%, y%
Code: Select all
CLOSE #1
As you can see, this is one of the simplest image storage methods available. Rather wasteful, but simple. This code doesn't delve into a lot of things though, such as scanline optimization or even proper byte alignment issues, but it's a start.
Anyways, I hope that was helpful. If you have any other questions, feel free to ask, I'm here all day today.
-
- Coder
- Posts: 18
- Joined: Mon Dec 17, 2007 8:41 pm
- Contact:
oh, i love you lol. thanks a whole lot! this REALLY helped me.
Edit: You know what, i think I'm about to feel really stupid, but in the header under TYPE BMPHeaderType where all the AS INTEGER and AS LONGs are, do i replace it with the actual data or not :x?
because it's saying in the comments 'Should be BM' or 'size of the data'
'Nother Edit (since i don't like to double post):
I'm getting the "Overflow" error when i type in the line
in the program and press return to go to the next line :/
Edit: You know what, i think I'm about to feel really stupid, but in the header under TYPE BMPHeaderType where all the AS INTEGER and AS LONGs are, do i replace it with the actual data or not :x?
because it's saying in the comments 'Should be BM' or 'size of the data'
'Nother Edit (since i don't like to double post):
I'm getting the "Overflow" error when i type in the line
Code: Select all
OUT &H3C8, 0
No. The TYPE is to be left alone. It's code after all. The comments simply explain to you what you should find after the header is read from the BMP file.
is always going to fail. It's not valid syntax; it's a result of someone's web script monkeying up. The valid syntax is like this:
Code: Select all
OUT &H3C8, 0
Code: Select all
OUT &H3C8, 0
-
- Coder
- Posts: 18
- Joined: Mon Dec 17, 2007 8:41 pm
- Contact:
- burger2227
- Veteran
- Posts: 2466
- Joined: Mon Aug 21, 2006 12:40 am
- Location: Pittsburgh, PA
Do you want a program that you cannot write? Do you want the code?
Go to QbasicStation.com and look in Member files / Utilities for BSAVER.ZIP if you are using SCREEN 12 or 13. Nodvelt cannot sum it up better than that unless he posts the entire procedure!
Give me a break! He is gone, but if you have a film of Chainey shooting his friend, that would be real cool.
Ted
Go to QbasicStation.com and look in Member files / Utilities for BSAVER.ZIP if you are using SCREEN 12 or 13. Nodvelt cannot sum it up better than that unless he posts the entire procedure!
Give me a break! He is gone, but if you have a film of Chainey shooting his friend, that would be real cool.
Ted
Please acknowledge and thank members who answer your questions!
QB64 is a FREE QBasic compiler for WIN, MAC(OSX) and LINUX : https://www.qb64.org/forum/index.php
Get my Q-Basics demonstrator: https://www.dropbox.com/s/fdmgp91d6h8ps ... s.zip?dl=0
QB64 is a FREE QBasic compiler for WIN, MAC(OSX) and LINUX : https://www.qb64.org/forum/index.php
Get my Q-Basics demonstrator: https://www.dropbox.com/s/fdmgp91d6h8ps ... s.zip?dl=0
-
- Coder
- Posts: 18
- Joined: Mon Dec 17, 2007 8:41 pm
- Contact:
here is the source
Code: Select all
TYPE BMPHeaderType
id AS STRING * 2 'Should be "BM"
size AS LONG 'Size of the data
rr1 AS INTEGER '
rr2 AS INTEGER '
offset AS LONG 'Position of start of pixel data
horz AS LONG '
wid AS LONG 'Image width
hei AS LONG 'Image height
planes AS INTEGER '
bpp AS INTEGER 'should read 8 for a 256 colour image
pakbyte AS LONG '
imagebytes AS LONG 'Width*Height
xres AS LONG '
yres AS LONG '
colch AS LONG '
ic AS LONG '
pal AS STRING * 1024 'Stored as Blue, Green, Red, 0
END TYPE
DIM BmpHeader AS BMPHeaderType
OPEN "1.bmp" FOR BINARY AS #1
GET #1, , BmpHeader
' Don't close the file just yet - were not finished!
SCREEN 13 'Set graphics mode
a$ = BmpHeader.pal 'Pal is stored in a 1024 character string
OUT &H3C8, 0 'Start writing form colour 0
FOR I% = 1 TO 1024 STEP 4
b% = ASC(MID$(a$, I%, 1)) \ 4 'blue
g% = ASC(MID$(a$, I% + 1, 1)) \ 4 'green
r% = ASC(MID$(a$, I% + 2, 1)) \ 4 'red
'I% + 3 is set to zero.
OUT &H3C8, r%
OUT &H3C8, g%
OUT &H3C8, b%
NEXT
DIM Pixel AS STRING * 1 'Our pixel "byte"
iHeight% = BmpHeader.hei - 1 'Subtract 1 for actual screen position
iWidth% = BmpHeader.wid - 1 '
FOR y% = iHeight TO 0 STEP -1 'Coutdown for upsidedown image
FOR x% = 0 TO iWidth%
GET #1, , Pixel
PSET (x%, y%), ASC(Pixel)
NEXT x%, y%
CLOSE #1
-
- Coder
- Posts: 18
- Joined: Mon Dec 17, 2007 8:41 pm
- Contact:
-
- Coder
- Posts: 18
- Joined: Mon Dec 17, 2007 8:41 pm
- Contact:
- burger2227
- Veteran
- Posts: 2466
- Joined: Mon Aug 21, 2006 12:40 am
- Location: Pittsburgh, PA
Did you save it as an 8 bit bitmap? Paint is not the greatest kind of image editor. You should be able to set the palette's BPP to 8 in Photoshop. Your code is from here I gather and it should work for screen 13.
Does it load at all? If it is skewed, the bitmap needs a padder.
Ted
Nodvelt is a well known programmer who rarely finishes what he starts!
Does it load at all? If it is skewed, the bitmap needs a padder.
Ted
Nodvelt is a well known programmer who rarely finishes what he starts!
Please acknowledge and thank members who answer your questions!
QB64 is a FREE QBasic compiler for WIN, MAC(OSX) and LINUX : https://www.qb64.org/forum/index.php
Get my Q-Basics demonstrator: https://www.dropbox.com/s/fdmgp91d6h8ps ... s.zip?dl=0
QB64 is a FREE QBasic compiler for WIN, MAC(OSX) and LINUX : https://www.qb64.org/forum/index.php
Get my Q-Basics demonstrator: https://www.dropbox.com/s/fdmgp91d6h8ps ... s.zip?dl=0
-
- Coder
- Posts: 18
- Joined: Mon Dec 17, 2007 8:41 pm
- Contact:
i got it in 8 bit (it was still in 16 somehow) but its the same thing, but nnow the line is a bunch of random colors...burger2227 wrote:Did you save it as an 8 bit bitmap? Paint is not the greatest kind of image editor. You should be able to set the palette's BPP to 8 in Photoshop. Your code is from here I gather and it should work for screen 13.
Does it load at all? If it is skewed, the bitmap needs a padder.
Ted
Nodvelt is a well known programmer who rarely finishes what he starts!
what is this padding you speak of?
- burger2227
- Veteran
- Posts: 2466
- Joined: Mon Aug 21, 2006 12:40 am
- Location: Pittsburgh, PA
If you can get more than a single line, then we can worry about the padder.
How large is the bitmap? I cannot be over 320 X 200.
Program problems:
1) Width and Height are Long variables so use & instead of % no matter what the bitmap size is.
2) FOR y% = iHeight TO 0 STEP -1 has no type suffix for iHeight.
Use: FOR y& = iHeight& TO 0 STEP - 1
3) OUT &H3C8, r% is wrong. It should be OUT &H3C9 for all three color settings. OUT &H3C8, 0 is used to set the attribute number to start the process. Each 3 OUT &H3C9 statements will increment to a new attribute without any help. 1024 / 4 = 256 colors. The 4th byte is a color padder byte skipped by STEP 4.
Ted
How large is the bitmap? I cannot be over 320 X 200.
Program problems:
1) Width and Height are Long variables so use & instead of % no matter what the bitmap size is.
2) FOR y% = iHeight TO 0 STEP -1 has no type suffix for iHeight.
Use: FOR y& = iHeight& TO 0 STEP - 1
3) OUT &H3C8, r% is wrong. It should be OUT &H3C9 for all three color settings. OUT &H3C8, 0 is used to set the attribute number to start the process. Each 3 OUT &H3C9 statements will increment to a new attribute without any help. 1024 / 4 = 256 colors. The 4th byte is a color padder byte skipped by STEP 4.
Ted
Please acknowledge and thank members who answer your questions!
QB64 is a FREE QBasic compiler for WIN, MAC(OSX) and LINUX : https://www.qb64.org/forum/index.php
Get my Q-Basics demonstrator: https://www.dropbox.com/s/fdmgp91d6h8ps ... s.zip?dl=0
QB64 is a FREE QBasic compiler for WIN, MAC(OSX) and LINUX : https://www.qb64.org/forum/index.php
Get my Q-Basics demonstrator: https://www.dropbox.com/s/fdmgp91d6h8ps ... s.zip?dl=0
Andrew, I ran into the same exact problem while testing that source code. It appears to be a little broken. Unfortunately, I did not test that code before explaining it to you, so I'm partially at fault for that. In any event, if all you need is a working BMP viewer, you can use this:
http://www.nodtveidt.net/bmpload.zip
It could use some optimization but it loads all valid compressed and uncompressed 8-bit BMP files without error and can also handle uncompressed 4-bit images. I would explain how the code works but unfortunately that code in particular is relatively complex and would take quite awhile to completely document. The basic concepts of it are unchanged though, so feel free to look it over. Again, it is rather unoptimized so it's a little slow (I ported it from an FB library I wrote a couple of years back, so it had to be downgraded to work with QB), but it has been tested in QBASIC 1.1 to work just fine.
EDIT: By the way, I found your problem. Your code looks like this:
when it should look like this:
This is one of the unseen dangers of not initializing your variables ahead of time. TYPE SUFFIXES ARE EVIL. I made that one-character change and the code worked almost as expected. Of course, the palette didn't work correctly...I'll see what's the deal there too.
EDIT2: It's all as burger2227 stated; the loop problem and the palette problem. Change the port number and the palette will load fine also.
EDIT3: However, you can disregard the suggestion to use LONG variables. LONGs are slower than INTEGERs and you will take a performance hit. The current method you're using is better. The only reason they are LONGs in the header is because of a format design decision; by making the value 32 bits, you can have an image of virtually unlimited size (do you really need a bitmap over 4 billion pixels wide and tall?). For practical use and speed considerations, stick with INTEGERs whenever possible, use LONG only when you have to.
EDIT4: There are also plenty of ways to speed up this code, and make it more reliable. For example, you can POKE to the screen memory instead of using PSET, and you will gain a tremendous speed increase. Also, the padding issue he was talking about...if you have an images whose width is NOT a multiple of 4, it needs to be "padded"...basically what this means in this case is that you have to read "extra pixels" from the file regardless if those pixels exist or not. This is by design; it's so the BMP format loads and saves as fast as possible (32 bit alignment). However, if all you're doing is 320 pixel wide images, you don't need to worry about padding. If you get into strange widths (like 265 pixels, for example), you will need to use padding or your image will skew on the screen.
http://www.nodtveidt.net/bmpload.zip
It could use some optimization but it loads all valid compressed and uncompressed 8-bit BMP files without error and can also handle uncompressed 4-bit images. I would explain how the code works but unfortunately that code in particular is relatively complex and would take quite awhile to completely document. The basic concepts of it are unchanged though, so feel free to look it over. Again, it is rather unoptimized so it's a little slow (I ported it from an FB library I wrote a couple of years back, so it had to be downgraded to work with QB), but it has been tested in QBASIC 1.1 to work just fine.
EDIT: By the way, I found your problem. Your code looks like this:
Code: Select all
FOR y% = iHeight TO 0 STEP -1 'Coutdown for upsidedown image
Code: Select all
FOR y% = iHeight% TO 0 STEP -1 'Coutdown for upsidedown image
EDIT2: It's all as burger2227 stated; the loop problem and the palette problem. Change the port number and the palette will load fine also.
EDIT3: However, you can disregard the suggestion to use LONG variables. LONGs are slower than INTEGERs and you will take a performance hit. The current method you're using is better. The only reason they are LONGs in the header is because of a format design decision; by making the value 32 bits, you can have an image of virtually unlimited size (do you really need a bitmap over 4 billion pixels wide and tall?). For practical use and speed considerations, stick with INTEGERs whenever possible, use LONG only when you have to.
EDIT4: There are also plenty of ways to speed up this code, and make it more reliable. For example, you can POKE to the screen memory instead of using PSET, and you will gain a tremendous speed increase. Also, the padding issue he was talking about...if you have an images whose width is NOT a multiple of 4, it needs to be "padded"...basically what this means in this case is that you have to read "extra pixels" from the file regardless if those pixels exist or not. This is by design; it's so the BMP format loads and saves as fast as possible (32 bit alignment). However, if all you're doing is 320 pixel wide images, you don't need to worry about padding. If you get into strange widths (like 265 pixels, for example), you will need to use padding or your image will skew on the screen.
Last edited by Nodtveidt on Wed Dec 19, 2007 10:13 pm, edited 1 time in total.
-
- Coder
- Posts: 18
- Joined: Mon Dec 17, 2007 8:41 pm
- Contact:
- burger2227
- Veteran
- Posts: 2466
- Joined: Mon Aug 21, 2006 12:40 am
- Location: Pittsburgh, PA
Now the image should look stretched. To fix that edit your bitmap by changing the height of it in Photoshop. Set the height to 83% and width to 100%. This will squish the bitmap image, but the image will look perfect in SCREEN 13.
If you want to learn how to BSAVE the image and be able to center your image there are BSAVERs on the web. I made one also if you need it.
I have never seen any speed problems, but I agree that you should never really need a long value in this application.
It is a shame that some of the programs on this site have never been edited correctly for years. I gather that is not possible.
Ted
If you want to learn how to BSAVE the image and be able to center your image there are BSAVERs on the web. I made one also if you need it.
I have never seen any speed problems, but I agree that you should never really need a long value in this application.
It is a shame that some of the programs on this site have never been edited correctly for years. I gather that is not possible.
Ted
Please acknowledge and thank members who answer your questions!
QB64 is a FREE QBasic compiler for WIN, MAC(OSX) and LINUX : https://www.qb64.org/forum/index.php
Get my Q-Basics demonstrator: https://www.dropbox.com/s/fdmgp91d6h8ps ... s.zip?dl=0
QB64 is a FREE QBasic compiler for WIN, MAC(OSX) and LINUX : https://www.qb64.org/forum/index.php
Get my Q-Basics demonstrator: https://www.dropbox.com/s/fdmgp91d6h8ps ... s.zip?dl=0
-
- Coder
- Posts: 18
- Joined: Mon Dec 17, 2007 8:41 pm
- Contact:
- burger2227
- Veteran
- Posts: 2466
- Joined: Mon Aug 21, 2006 12:40 am
- Location: Pittsburgh, PA
Remember the squish idea. You may need it someday, especially for pictures of people.
There are other tutorials and code that does not work at all here. That was only one small example of a minor few mistakes. Try the FAST bitmap loader in one of the Interrupt tutorials. It does not work at all!
Ted
There are other tutorials and code that does not work at all here. That was only one small example of a minor few mistakes. Try the FAST bitmap loader in one of the Interrupt tutorials. It does not work at all!
Ted
Please acknowledge and thank members who answer your questions!
QB64 is a FREE QBasic compiler for WIN, MAC(OSX) and LINUX : https://www.qb64.org/forum/index.php
Get my Q-Basics demonstrator: https://www.dropbox.com/s/fdmgp91d6h8ps ... s.zip?dl=0
QB64 is a FREE QBasic compiler for WIN, MAC(OSX) and LINUX : https://www.qb64.org/forum/index.php
Get my Q-Basics demonstrator: https://www.dropbox.com/s/fdmgp91d6h8ps ... s.zip?dl=0