'=========================================================================== ' Subject: READ SPRITE Date: 10-10-97 (20:02) ' Author: Ryan Shaw Code: QB, QBasic, PDS ' Origin: kathleen@iafrica.com.na Packet: GRAPHICS.ABC '=========================================================================== ' RSPR.BAS ' ' Description: ' Read SPRite is an example of how to open a RGB encoded BMP file and ' retrieve information about the bitmap, display the image, save the image ' to another file using BSAVE and then reloading the image into an array ' using BLOAD and finally tiling the image across the display. RSPR also ' describes how to use user defined data types (BMPPal and BMPFile). ' ' It also describes how palettes and setting them works. All sections ' seperated by the ' ****************, can be used seperately (well, they ' were meant to be, but if you can actually do it, I'm not sure, though ' you can figure out what's wrong - probably the DIM's and DataReq& would ' be the only problems.) in your main programme. ' ' Remember: this program is a sub-set of code, eg. use it as a sub. This ' is the reason why there are no other subs in this programme - none are ' required. ' ' Author: ' Ryan Shaw (kathleen@iafrica.com.na) ' Date: 10/10/97 (mdy), 19:47. ' ' Other: ' Use at own risk! If your computer crashes suddenly while reading the ' bitmap and you lose the image, your problem, not mine! Make backups! ' All credits used in program (including who programmed your program :), ' should be directed to the name above and left (under the Author: bit) ' ' Um, use wisely, and no *interresting* pictures... this programme can ' detect what you display :) (maybe I should add that... need a lot of ' testing though...). ' ' ******************************************************** ' Sets all undefined variables to be used as INTeger data ' types (eg. if you typed 'X = 3325', but haven't declared ' X as anything (as in 'DIM X AS INTEGER'), then X would ' be thought of as an SINGLE data type (which uses more ' memory than an INTeger, and is also much slower in ' calculations. SINGLE is the default data type. DEFINT ' sets the default to INTEGER), though I doubt there are ' any. DEFINT A-Z ' ******************************************************** ' Read BMP code ' ******************************************************** ' The following is a file format of the BMP files... ' The record format for a palette entry TYPE BMPPal Blue AS STRING * 1 ' Blue colour component of palette entry Green AS STRING * 1 ' Green component Red AS STRING * 1 ' Red component Reserved AS STRING * 1 ' Reserved (mostly unsupported intensity comp.) END TYPE ' The record format for a BMP file header TYPE BMPFile bfType AS STRING * 2 bfSize AS LONG bfReserved1 AS INTEGER bfReserved2 AS INTEGER bfOffBits AS LONG biSize AS LONG biWidth AS LONG biHeight AS LONG biPlanes AS INTEGER biBitCount AS INTEGER biCompression AS LONG biSizeImage AS LONG biXPelsPerMeter AS LONG biYPelsPerMeter AS LONG biClrUsed AS LONG biClrImportant AS LONG END TYPE ' Create a string limited to one character (a byte) DIM PCol AS STRING * 1 ' Create a record for the BMP file DIM BMPHeader AS BMPFile ' And a temporary palette entry DIM BMPPalEnt AS BMPPal ' Open image OPEN "SPRITE.BMP" FOR BINARY AS #1 GET #1, , BMPHeader ' In all valid BMP files, the first two bytes are the string "BM". ' If it's not, then the file is probably not a bitmap image. IF NOT BMPHeader.bfType = "BM" THEN PRINT "File is not a valid Microsoft BMP image." END END IF ' The image may not be wider than 320 pixels or higher than 200. IF BMPHeader.biWidth > 320 OR BMPHeader.biHeight > 200 THEN PRINT "Image exceeds maximum dimensions for screen mode." END END IF ' The image must be saved as RGB encoded, not run-length encoded (RLE). IF BMPHeader.biCompression > 0 THEN PRINT "Compressed formats are not supported." END END IF ' The image must be a 256 colour bitmap. IF BMPHeader.biClrUsed <> 256 THEN PRINT "Image must be a 256 colour bitmap." END END IF ' Create an array to store the sprite which is a bitmap of 20x20 pixels. ' SizeOfArray = XSize * YSize + 4 ' The value returned is how many *bytes* are required. Because an integer ' takes up two bytes of memory, the answer is divided by two. DataReq& = BMPHeader.biWidth * BMPHeader.biHeight + 4 REDIM Sprite(1 TO DataReq& \ 2) AS INTEGER ' Another bitmap array - notice that this array is not altered until the ' end of the program when the image is loaded from disk and how much ' quicker it is than the bitmap routine reading from the BMP file. REDIM Example(1 TO DataReq& \ 2) AS INTEGER ' Video mode SCREEN 13 ' This sends out the value 0 to the graphics card. The graphics card ' interprets this and sets the data at the *read/write* port (&H3C9) to ' prepare to receive the Red, Green and Blue components of palette ' entry no. 0. After receiving them, the card will prepare for the ' next palette entry (no. 1) automatically, so we tell it to get ready ' once. The r/w (read/write) port is also used to receive information ' about the palette (eg. if you were saving an image), but in that ' case you would OUTput a byte to port &H3C7, and not &H3C8 as here, ' and you would INPut a byte from &H3C9, and not ouput a byte to &H3C9. ' ' In 16 million colour modes (16 million is the maximum colours an human ' eye can differentiate between), there are 256 shades of Red, Green and ' Blue and theses shades make up the colours on the screen. In memory, ' there are no palette entries, only data for each pixels Red, Green and ' Blue components. This is why you have 16 million colours - 256 ^ 3 ' (256 * 256 * 256) = 16 777 216 colours. This is also why a mode ' requires three bytes per pixel (the first byte represents the red, the ' next the green, the next the blue, the next the pixel's red, and next ' the pixel's green, and so-on), but in mode 13, a palette of 256 colours ' is used. The palette entries (Red, Green and Blue) can only have a ' range from 0-63 (64 shades) of each RGB component. When the image is ' saved, the palette stored along with the file has a range of 256 shades ' per colour so that it can be loaded into a 16mil GFX mode, 65kb GFX mode, ' and so-on without loss of colout detail. In mode 13, this means we ' have to divide each palette entry by 4 (256 \ 4 = 64) to get the correct ' shade of the colour. ' ' The palette in the file is stored as one byte per component (3 bytes per ' palette entry), with 256 entries: 1 * 3 * 256 = 768 bytes palette. ' What the ASC function does is returns the ASCII equv. of the byte read. ' eg. The red palette component in the file reads as the character '!'. ' Character '!' is the 33rd ASCII character, and so the shade of the ' palette entry's red component is 33. Divided by 4, this is 8 in the ' 64 shade mode 13 palette. ' ' You might have noticed a 'Reserved' STRING * 1 above in the BMPPal TYPE. ' This is not used (it is for 4gig colour modes, where there is a fourth ' byte which represents intensity of the colour (I think), but it is ' reserved until more graphics cards begin to support 4gig colour modes, ' because at the time of creating the BMP format, possibly no cards ' supported it and it was still being developed). OUT &H3C8, 0 FOR SetPal% = 0 TO 255 GET #1, , BMPPalEnt OUT &H3C9, ASC(BMPPalEnt.Red) \ 4 OUT &H3C9, ASC(BMPPalEnt.Green) \ 4 OUT &H3C9, ASC(BMPPalEnt.Blue) \ 4 NEXT ' Bitmap data is stored from the bottom, right, going up to the top ' left. ' ' This is certainly not the quickest way to load and display an image, ' but it is the easiest. ' ' *Up* Y (pixels begin at 0 and go on to 319, eg. if sprite is 20, then ' go on to 0 from 19) FOR Y% = BMPHeader.biHeight - 1 TO 0 STEP -1 ' Across X, from *right* to *left* FOR X% = BMPHeader.biWidth - 1 TO 0 STEP -1 ' Read a colour GET #1, , PCol ' Draw the pixel PSET (X%, Y%), ASC(PCol) NEXT NEXT ' Close the file CLOSE #1 ' Grab the picture to a buffer GET (0, 0)-(BMPHeader.biWidth - 1, BMPHeader.biHeight - 1), Sprite(1) ' ******************************************************** ' Saving code ' ******************************************************** ' Change the data segment to that of the buffer DEF SEG = VARSEG(Sprite(1)) ' Save DataReq bytes of information at Sprite_SEG:Sprite_OFFSET to a file ' called SPRITE.DAT BSAVE "SPRITE.DAT", VARPTR(Sprite(1)), DataReq& ' Save the sprite's palette (the palette MUST be the same throughout ' your entire program, otherwise some sprites will look deformed). ' Probably best to use something like "GAME.PAL" as a single main ' palette, though perhaps you would like to show an picture after ' something occurs in your program, but the palette is different, then ' seperate palettes is what you use. OPEN "SPRITE.PAL" FOR BINARY AS #1 ' Retrieve palette data from r/w port and save it the sprite's palette. OUT &H3C7, 0 FOR PalEntry% = 0 TO 255 ' Create a character, and convert to a 256 shade (only for ' compatibility, as the shades won't be enhanced to 256 seperate ' shades as in a 16mil colour image). FOR RGB% = 1 TO 3 PCol = CHR$(INP(&H3C9)) PUT #1, , PCol NEXT NEXT CLOSE #1 ' ******************************************************** ' Retrieval code ' ******************************************************** CLS PRINT "Press a key to tile the image." SLEEP DEF SEG = VARSEG(Example(1)) ' Retrieve the saved image into the array Example BLOAD "SPRITE.DAT", VARPTR(Example(1)) DEF SEG OPEN "SPRITE.PAL" FOR BINARY AS #1 OUT &H3C8, 0 FOR SetPal% = 0 TO 767 GET #1, , PCol OUT &H3C9, ASC(PCol) NEXT CLOSE #1 ' Tile the image across the screen FOR Y% = 0 TO 200 - BMPHeader.biWidth STEP BMPHeader.biHeight FOR X% = 0 TO 320 - BMPHeader.biHeight STEP BMPHeader.biWidth PUT (X%, Y%), Example, PSET NEXT NEXT