The QBNews Page 21 Volume 1, Number 2 February 2, 1990 ---------------------------------------------------------------------- G r a p h i c a l l y S p e a k i n g ---------------------------------------------------------------------- Getting and Putting Graphics by Frederick Volking Due to the Graphic nature of this article, parental discretion is advised. I am involved in writing several graphics oriented games. Each of these games require many different images (called sprites). I was evaluating the best method to store these sprites for the games I'm writing. I noticed the graphics GET & PUT commands could store relatively large images in surprisingly small arrays. I decided that if I could understand the method used to store the images in the arrays, then I could write a few routines which would make my games easier to program. Specifically routines which would manipulate the images ONCE THEY WERE ALREADY STORED IN THE ARRAY! Routines like; (1) "flipping" an image upside down or left to right. (2) rotating an image around a fixed point (3) changing all occurrences of color-X to a new color-Y [easy way to make a "good guy" into a "bad guy"] ... and several other routines. I thought that if I could write small fast routines to do the above actions, then I would need far fewer sprites to accomplish the same visual actions, since I could simply "change" the sprite, as needed, on-the-fly, while the program was running. The below article is the result of many hours of research into the method used for storage of images into numeric arrays. Enjoy ... ================================================> Frederick All versions of Microsoft Basic have several commands which provide graphic functions. Two of the most powerful are the graphics GET and graphics PUT. These two commands allow the programmer to move a copy of a graphic image into a numerical array and then, later, redisplay the image whenever needed anyplace on the screen. Almost every graphics game uses the technique of GET'ing and PUT'ing graphics images to create animation. During a game, when our eye sees a picture of an object moving across the screen, we are actually seeing many separate images. Each image was initially stored with a GET command. Then, throughout the game, the image is repeatedly redisplayed with subsequent PUT commands. The key concept, is the speed at which this redisplay happens. The computer is capable of PUT'ing, so rapidly, that the human eye cannot see the individual different frames. The GET and PUT commands use numerical arrays to store images. The method used for storage is the main concept to be examined by this article. In Basic, only rectangularly shaped screen areas can be stored The QBNews Page 22 Volume 1, Number 2 February 2, 1990 and displayed. The height and width dimensions of such areas are determined on a pixel basis. The height and width of a graphic image is described as X-pixels-tall and Y-pixels-wide. GET requires a numeric array which has been pre-dimensioned to the proper size to receive the entire graphic image. Depending upon several criteria, such as screen mode (CGA, EGA or VGA) or how many bytes of graphics memory are available, the exact size and dimension of these arrays tend to vary significantly. It is this variation in array size, which provides the first clue to unraveling the complexity of image storage. In Screen Two a 16x16 pixel image requires an array of 18 integers. However, 16x16 pixels in Screen One requires 34 integers and in Screen Nine 66 integers are needed. It is immediately obvious the screen mode being used has a great impact on the array size needed. In Screen Two (CGA Monochrome), the color of each pixel may only be either white(ON) or black(OFF). In Screen One (CGA color) there are only FOUR possible color choices and in Screen Nine, each pixel must be one of SIXTEEN possible color choices. The quantity of colors available, under any specified screen mode, is the main factor affecting the size of the array required to hold an image. Included with this article is a SUB which will calculate the array size needed, in integers, for most screen modes. GETSIZE.BAS Numeric arrays dimensioned for image storage include two integers in the first and second positions which are not part of the graphics image itself. The first and second integers are the height and width of the image contained therein. These values are automatically set by the GET command. Examination of array sizes (minus the two extra integers) for a variety of screen modes, for storage of a 16x16 pixel image, reveals a geometric pattern rise, in step with the quantity of colors available. SCREEN 2 .... 16 integers needed ... 2 colors available SCREEN 1 .... 32 integers needed ... 4 colors available SCREEN 9 .... 64 integers needed ... 16 colors available If we examine the storage needs of a 16x16 pixel image we find there are a total of 256 pixels to be stored. (16 pixels tall) times (16 pixels wide) equals (256 pixels) to store. The question that must be answered is, "How do you store 256 pixels in arrays which are only 16, 32 or 64 integers in size? CGA - MONOCHROME SCREEN Let's begin by concentrating on Screen Two where the entire 256 pixels are stored in only 16 integers and each pixel is either Black or White ... ON or OFF ... 0(zero) or 1(one) ... The QBNews Page 23 Volume 1, Number 2 February 2, 1990 - One array of 16 INTEGERS - Each integer is composed of TWO BYTES - Each byte is made up of EIGHT BITS - Each bit is either a 0(zero) or 1(one) ... A little mathematics shows us we have (16 integers) times (2 bytes) times (8 bits) which equals (256 bits). Since we have 256 PIXELS which are either 0(zero) or 1(one) ... and Since we have 256 BITS which are either 0(zero) or 1(one) ... We can then THEORIZE that each pixel is "mapped" to the "bit-level". Included with this article is a program which will validate bit level mapping in Screen Two: SCR2BITS.BAS To prove our THEORY ... let's assume we have an array of 18 integers (two for height and width and 16 for pixel storage). Each integer is composed of two bytes and each byte is composed of 8 bits. Set every integer in our array to 0(zero) and examine each bit, of each byte, of each integer: Integer #1 0000 0000 0000 0000 = 0 (tall) Integer #2 0000 0000 0000 0000 = 0 (wide) Byte #1 Byte #2 Integer #3 0000 0000 0000 0000 Integer #4 0000 0000 0000 0000 Integer #5 0000 0000 0000 0000 Integer #6 0000 0000 0000 0000 Integer #7 0000 0000 0000 0000 Integer #8 0000 0000 0000 0000 Integer #9 0000 0000 0000 0000 Integer #10 0000 0000 0000 0000 Integer #11 0000 0000 0000 0000 Integer #12 0000 0000 0000 0000 Integer #13 0000 0000 0000 0000 Integer #14 0000 0000 0000 0000 Integer #15 0000 0000 0000 0000 Integer #16 0000 0000 0000 0000 Integer #17 0000 0000 0000 0000 Integer #18 0000 0000 0000 0000 Let's further ... 1. turn on Screen 2 > SCREEN 2 2. draw a box in the corner > LINE (0,0)-(15,15),1,B 3. draw a diagonal line in the box > LINE (0,0)-(15,15),1 4. GET the image into our array > GET (0,0)-(15,15),Array Now let's examine our array again ... Integer #1 0001 0000 0000 0000 = 16 (tall) Integer #2 0001 0000 0000 0000 = 16 (wide) Byte #1 Byte #2 Integer #3 1111 1111 1111 1111 Integer #4 1100 0000 0000 0001 Integer #5 1010 0000 0000 0001 The QBNews Page 24 Volume 1, Number 2 February 2, 1990 Integer #6 1001 0000 0000 0001 Integer #7 1000 1000 0000 0001 Integer #8 1000 0100 0000 0001 Integer #9 1000 0010 0000 0001 Integer #10 1000 0001 0000 0001 Integer #11 1000 0000 1000 0001 Integer #12 1000 0000 0100 0001 Integer #13 1000 0000 0010 0001 Integer #14 1000 0000 0001 0001 Integer #15 1000 0000 0000 1001 Integer #16 1000 0000 0000 0101 Integer #17 1000 0000 0000 0011 Integer #18 1111 1111 1111 1111 We have proven our theory. Examination of the "bit-pattern" proves under Screen Two, each pixel is mapped at the bit level. Given the visual pattern above, it's rather obvious how Screen Two (CGA Mono) images are stored in the array. CGA - COLOR SCREEN In Screen Two (CGA Mono) it is only necessary to store a single ON/OFF switch (one bit). Thus, mapping to each integer is rather straight forward. However, in Screen One (CGA color), it is necessary to store a value which can be one of FOUR possible colors (0,1,2,3). Using binary mathematics, we can determine it will only require TWO BITS to store a value which has a range of 0 thru 3 .... Bit#1 Bit#2 Value OFF OFF . . . . . 0 0 = 0 OFF on . . . . . 0 1 = 1 on OFF . . . . . 1 0 = 2 on on . . . . . 1 1 = 3 We must now determine how those two bits, for each pixel, are mapped on the integer array. Included with this article is a program which will validate bit level mapping in Screen One: SCR1BITS.BAS Let's examine an example ... Since we are now dealing with Screen One, we will need an array with 34 integers. Let's set the entire array to 0(zero) and examine the "bit-pattern" of the array. Since we need 2 bits to display each pixel, a single line of 16 pixels will now span 2 integers (rather than 1 integer as in Screen Two). Thus we'll change the way our integers are displayed such that we have two integers side by side Integer #1 0001 0000 0000 0000 = 16 (tall) Integer #2 0001 0000 0000 0000 = 16 (wide) Byte #1 Byte #2 Byte #1 Byte #2 Integer # 3 0000 0000 0000 0000 - 0000 0000 0000 0000 # 4 Integer Integer # 5 0000 0000 0000 0000 - 0000 0000 0000 0000 # 6 Integer The QBNews Page 25 Volume 1, Number 2 February 2, 1990 Integer # 7 0000 0000 0000 0000 - 0000 0000 0000 0000 # 8 Integer Integer # 9 0000 0000 0000 0000 - 0000 0000 0000 0000 #10 Integer Integer #11 0000 0000 0000 0000 - 0000 0000 0000 0000 #12 Integer Integer #13 0000 0000 0000 0000 - 0000 0000 0000 0000 #14 Integer Integer #15 0000 0000 0000 0000 - 0000 0000 0000 0000 #16 Integer Integer #17 0000 0000 0000 0000 - 0000 0000 0000 0000 #18 Integer Integer #19 0000 0000 0000 0000 - 0000 0000 0000 0000 #20 Integer Integer #21 0000 0000 0000 0000 - 0000 0000 0000 0000 #22 Integer Integer #23 0000 0000 0000 0000 - 0000 0000 0000 0000 #24 Integer Integer #25 0000 0000 0000 0000 - 0000 0000 0000 0000 #26 Integer Integer #27 0000 0000 0000 0000 - 0000 0000 0000 0000 #28 Integer Integer #29 0000 0000 0000 0000 - 0000 0000 0000 0000 #30 Integer Integer #31 0000 0000 0000 0000 - 0000 0000 0000 0000 #32 Integer Integer #33 0000 0000 0000 0000 - 0000 0000 0000 0000 #34 Integer Let's ... 1. turn on Screen 1 > SCREEN 1 2. define the color to use > Clr = 1 3. draw a box in the corner > LINE (0,0)-(15,15),Clr,B 4. draw a diagonal line in the box > LINE (0,0)-(15,15),Clr 5. GET the image into our array > GET (0,0)-(15,15),Array Now let's examine our array again ... Integer #1 0001 0000 0000 0000 = 16 (tall) Integer #2 0001 0000 0000 0000 = 16 (wide) Byte #1 Byte #2 Byte #1 Byte #2 Integer # 3 0101 0101 0101 0101 - 0101 0101 0101 0101 # 4 Integer Integer # 5 0101 0000 0000 0000 - 0000 0000 0000 0001 # 6 Integer Integer # 7 0100 0100 0000 0000 - 0000 0000 0000 0001 # 8 Integer Integer # 9 0100 0001 0000 0000 - 0000 0000 0000 0001 #10 Integer Integer #11 0100 0000 0100 0000 - 0000 0000 0000 0001 #12 Integer Integer #13 0100 0000 0001 0000 - 0000 0000 0000 0001 #14 Integer Integer #15 0100 0000 0000 0100 - 0000 0000 0000 0001 #16 Integer Integer #17 0100 0000 0000 0001 - 0000 0000 0000 0001 #18 Integer Integer #19 0100 0000 0000 0000 - 0100 0000 0000 0001 #20 Integer Integer #21 0100 0000 0000 0000 - 0001 0000 0000 0001 #22 Integer Integer #23 0100 0000 0000 0000 - 0000 0100 0000 0001 #24 Integer Integer #25 0100 0000 0000 0000 - 0000 0001 0000 0001 #26 Integer Integer #27 0100 0000 0000 0000 - 0000 0000 0100 0001 #28 Integer Integer #29 0100 0000 0000 0000 - 0000 0000 0001 0001 #30 Integer Integer #31 0100 0000 0000 0000 - 0000 0000 0000 0101 #32 Integer Integer #33 0101 0101 0101 0101 - 0101 0101 0101 0101 #34 Integer Since we drew our box using COLOR 1 ... and ... since the two-bit bit-pattern for COLOR 1 is ... 01 ....... It appears each pixel is mapped onto two consecutive bits of each integer. By changing the box to color 2 and 3 and repeating the exercise, it's possible to verify the bit mapping suggested by the above bit pattern. The two-bit bit-pattern for COLOR 2 is - 10 - thus ........ Byte #1 Byte #2 Byte #1 Byte #2 The QBNews Page 26 Volume 1, Number 2 February 2, 1990 Integer # 3 1010 1010 1010 1010 - 1010 1010 1010 1010 # 4 Integer Integer # 5 1010 0000 0000 0000 - 0000 0000 0000 0100 # 6 Integer Integer # 7 1000 1000 0000 0000 - 0000 0000 0000 0100 # 8 Integer Integer # 9 1000 0010 0000 0000 - 0000 0000 0000 0100 #10 Integer Integer #11 1000 0000 1000 0000 - 0000 0000 0000 0100 #12 Integer Integer #13 1000 0000 0010 0000 - 0000 0000 0000 0100 #14 Integer Integer #15 1000 0000 0000 1000 - 0000 0000 0000 0100 #16 Integer Integer #17 1000 0000 0000 0010 - 0000 0000 0000 0100 #18 Integer Integer #19 1000 0000 0000 0000 - 1000 0000 0000 0100 #20 Integer Integer #21 1000 0000 0000 0000 - 0010 0000 0000 0100 #22 Integer Integer #23 1000 0000 0000 0000 - 0000 1000 0000 0100 #24 Integer Integer #25 1000 0000 0000 0000 - 0000 0010 0000 0100 #26 Integer Integer #27 1000 0000 0000 0000 - 0000 0000 1000 0100 #28 Integer Integer #29 1000 0000 0000 0000 - 0000 0000 0010 0100 #30 Integer Integer #31 1000 0000 0000 0000 - 0000 0000 0000 1010 #32 Integer Integer #33 1010 1010 1010 1010 - 1010 1010 1010 1010 #34 Integer The two-bit bit-pattern for COLOR 3 is - 11 - thus ........ Byte #1 Byte #2 Byte #1 Byte #2 Integer # 3 1111 1111 1111 1111 - 1111 1111 1111 1111 # 4 Integer Integer # 5 1111 0000 0000 0000 - 0000 0000 0000 0011 # 6 Integer Integer # 7 1100 1100 0000 0000 - 0000 0000 0000 0011 # 8 Integer Integer # 9 1100 0011 0000 0000 - 0000 0000 0000 0011 #10 Integer Integer #11 1100 0000 1100 0000 - 0000 0000 0000 0011 #12 Integer Integer #13 1100 0000 0011 0000 - 0000 0000 0000 0011 #14 Integer Integer #15 1100 0000 0000 1100 - 0000 0000 0000 0011 #16 Integer Integer #17 1100 0000 0000 0011 - 0000 0000 0000 0011 #18 Integer Integer #19 1100 0000 0000 0000 - 1100 0000 0000 0011 #20 Integer Integer #21 1100 0000 0000 0000 - 0011 0000 0000 0011 #22 Integer Integer #23 1100 0000 0000 0000 - 0000 1100 0000 0011 #24 Integer Integer #25 1100 0000 0000 0000 - 0000 0011 0000 0011 #26 Integer Integer #27 1100 0000 0000 0000 - 0000 0000 1100 0011 #28 Integer Integer #29 1100 0000 0000 0000 - 0000 0000 0011 0011 #30 Integer Integer #31 1100 0000 0000 0000 - 0000 0000 0000 1111 #32 Integer Integer #33 1111 1111 1111 1111 - 1111 1111 1111 1111 #34 Integer EGA - COLOR SCREEN We will use the same methods we have already employed, to help us examine how pixels are stored in Screen Nine. Included with this article is a program which will validate bit level mapping in Screen Nine: SCR9BITS.BAS First let's examine how many bits will be required to store a pixel color in the range of 0 thru 15 (thus 16 colors). Using bit mathematics we can determine we'll need 4 bits. Bits-> (1) (2) (3) (4) 1 + 2 + 4 + 8 = 15 + 1 for Zero Bit Value Switches Pattern 0 ....... OFF OFF OFF OFF .... 0000 The QBNews Page 27 Volume 1, Number 2 February 2, 1990 1 ....... OFF OFF OFF on .... 0001 2 ....... OFF OFF on OFF .... 0010 3 ....... OFF OFF on on .... 0011 4 ....... OFF on OFF OFF .... 0100 5 ....... OFF on OFF on .... 0101 6 ....... OFF on on OFF .... 0110 7 ....... OFF on on on .... 0111 8 ....... on OFF OFF OFF .... 1000 9 ....... on OFF OFF on .... 1001 10 ....... on OFF on OFF .... 1010 11 ....... on OFF on on .... 1011 12 ....... on on OFF OFF .... 1100 13 ....... on on OFF on .... 1101 14 ....... on on on OFF .... 1110 15 ....... on on on on .... 1111 EGA Screen Nine image storage is much different from Screens One & Two. Rather than having one pixel mapped onto 1 or 2 CONSECUTIVE bits, Screen Nine takes a substantially different approach. Let's look at what appears to happen from a pseudo-code standpoint... 100 the quantity of pixels WIDE is determined. 200 that quantity is rounded up to the nearest whole INTEGER quantity 300 the rounded integer quantity is multiplied by the number of bits required (four) 400 the pixel color value, of the first pixel is determined 500 the bit-pattern of that value is separated into its four smallest basic pieces, the individual bits. 600 the first BIT of the 1ST pixel's color value is mapped onto the 1ST BIT position of the FIRST BYTE. Up until this point, things are happening like they would in Screen One or Two. But now things get strange .... 700 rather than mapping the SECOND pixel color BIT onto the BYTE's SECOND BIT (as would happen in CGA), the procedure begins again at line 400. However, instead of working with the 1ST pixel and the byte's first bit position, the FIRST BIT of the SECOND PIXEL is used. Thus, if you have an image, say 13 pixels wide, then the first 13 bits of the 2 bytes of the first integer all reflect the FIRST BITS OF 13 DIFFERENT PIXELS! and the next 13 bits of the next two bytes of the next integer are the SECOND BIT of 13 different pixels, and so on, and so on .... Pixels 1 2 3 4 5 6 7 8 9 10 11 12 13 Their Color 15 1 5 9 1 1 5 9 1 1 5 9 1 The QBNews Page 28 Volume 1, Number 2 February 2, 1990 Their Bit 1 0 0 1 0 0 0 1 0 0 0 1 0 A Patterns 1 0 1 0 0 0 1 0 0 0 1 0 0 B 1 0 0 0 0 0 0 0 0 0 0 0 0 C 1 1 1 1 1 1 1 1 1 1 1 1 1 D Byte #1 Byte #2 Integer #1 1001 0001 0001 0000 A Integer #1 1010 0010 0010 0000 B Integer #1 1000 0000 0000 0000 C Integer #1 1111 1111 1111 1000 D Notice that each integer has been filled to the integer border with zero's. When storing images, with a quantity of pixels which are not evenly divisible by 8, then substantial wasted space occurs. Run the program provided with this article, SCR9BITS and you'll be able to see this bit-spread across-bytes in action. Without going into great detail ... this trait of bit-mapping is found in discussions of "planes" in a variety of books. Planes refer to a technique taken by the lowest software level to maximize speed when addressing hardware requirements of the EGA card. VGA COLOR & MCGA SCREENS Sorry ... but I only have EGA and CGA computers at my disposal. I have not had the opportunity to examine how bits are stored in VGA modes. Possibly someone else will continue my research ... and next issue ... we can learn how VGA images are stored ...? Included is a function which will determine the condition of a specified BIT inside an integer, and return TRUE or FALSE if the BIT is ON or OFF: BITON.BAS