Chapter 9 Images and Image Management 136 Fastgraph User's Guide Overview Within the context of Fastgraph, an image is a rectangular area of video memory containing some type of picture. An image might be something as simple as a pointing hand icon, or as detailed as the dashboard of a sports car. Fastgraph includes several routines to display, retrieve, and manipulate images, as well as transfer them between different areas of video memory. This chapter will discuss these routines in detail. The information presented here, combined with the video page management techniques described in the previous chapter, will provide the tools we need for sophisticated animation techniques. Mode-Independent Bit-Mapped Images This section will discuss the image display routines that use the same bit-mapped image format for all graphics video modes. Another class of routines, described in the next section, use different formats for different video modes. While these mode-independent image display routines are more general, they achieve this generality at the sake of execution speed. This may especially be a concern if the image is large, or if speed is critical in an application (as in arcade-style graphics). For many programs, however, the mode-independent routines provide all the image display capability needed. Let's begin with an example of a very simple image. Suppose we need to display a small triangle whose perimeter is a different color than its interior. To use this image with Fastgraph, we must inscribe it in a rectangular area. Hence, the pixel representation of our triangle might appear as shown below. . . . . * . . . . . . . * x * . . . . . * x x x * . . . * x x x x x * . * * * * * * * * * As shown in this diagram, our triangle is 9 pixels wide at its base and 5 pixels high. The pixels indicated by an asterisk (*) are the triangle's perimeter, while those indicated by an x represent its interior points. We need to distinguish between these pixels because they will be different colors. The pixels shown as periods (.) are not part of the triangle itself. They are required to make the image rectangular, so from Fastgraph's perspective they are indeed part of the image. The Fastgraph routine fg_drawmap is a suitable routine for drawing our triangle. To use fg_drawmap, we must create separate bit maps for each color in the image (excluding the points used to fill the rectangular region, which is considered transparent). In this example, we will thus need two bit maps -- one for the perimeter points, and one for the interior points. Let's break the image into these two bit maps. Chapter 9: Images and Image Management 137 . . . . * . . . . . . . . . . . . . . . . * . * . . . . . . . x . . . . . . * . . . * . . . . . x x x . . . . * . . . . . * . . . x x x x x . . * * * * * * * * * . . . . . . . . . perimeter points interior points The next step is to convert these two bit maps into their binary representations. Just as there are eight bits in a byte, we will create a data structure (an array in this case) with each byte holding eight pixels. Bits that are set (1) indicate the corresponding pixel will appear displayed in the color associated with that bit map. Bits that are reset (0) leave the corresponding pixel unchanged. The size of each bit map array must be at least 10 bytes because each bit map contains five rows with nine pixels in each row (that is, two bytes are required for each row of the image). Hence, when we convert these bit maps to their binary representations, and subsequently to their hexadecimal equivalent, the results will appear as shown below. The boldface bits represent the actual image; the other bits are filler bits needed to complete each row of the bit maps after the ninth pixel. All filler bits must be zero. 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 08 00 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 14 00 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 22 00 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 41 00 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 FF 80 perimeter bit map 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 00 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 08 00 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1C 00 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 3E 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 00 interior bit map The next question is the order in which the bit maps are stored in the corresponding data structures. Since our data structure is an array, it is only necessary to show the relationship of the subscripts to the bit map structures above. The next diagram shows the subscript order for the case of a two-column by five-row bit map. [8] [9] [6] [7] [4] [5] [2] [3] [0] [1] 138 Fastgraph User's Guide From this diagram, we see the first element of the array (that is, the element with subscript [0]) represents the lower left corner of the image. The subscript progression then continues right until reaching the end of the first row. It then resumes at the leftmost element of the second row and continues to the right until the end of that row. It continues in this manner for all remaining rows. We are now ready to present an example program to display our triangle. The program will use the Fastgraph routine fg_drawmap, which expects three arguments. The first argument is the bit map array (passed by reference), the second is the width of the bit map in bytes, and the last is the height of the bit map in pixel rows. The fg_drawmap routine displays the image such that its lower left corner is at the graphics cursor position on the active video page. The routine has no effect in text video modes. Additionally, fg_drawmap displays the image using the current color index, which means we will need to call fg_drawmap once for each color in the image. Example 9-1 runs in any 320 by 200 color graphics mode (it could be made to run in mode 12 too, but that would detract from the purpose of the example). After establishing the video mode, the program uses fg_rect to fill the entire screen with a gray rectangle (white in CGA). Next, the program establishes (156,101) as the graphics cursor position; this causes the triangle to be centered on the screen. The two calls to fg_drawmap, one for each of the colors in the image, actually display the triangle. Note especially how fg_setcolor is used before each call to fg_drawmap to define the current color index. The result is a triangle with a blue perimeter (cyan in CGA) and green interior (magenta in CGA). Example 9-1. #include #include #include void main(void); char perimeter[] = { 0xFF,0x80,0x41,0x00,0x22,0x00,0x14,0x00,0x08,0x00 }; char interior[] = { 0x00,0x00,0x3E,0x00,0x1C,0x00,0x08,0x00,0x00,0x00 }; void main() { int old_mode, new_mode; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); Chapter 9: Images and Image Management 139 fg_setcolor(7); fg_rect(0,319,0,199); fg_move(156,101); fg_setcolor(1); fg_drawmap(perimeter,2,5); fg_setcolor(2); fg_drawmap(interior,2,5); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } The different color bit maps used by fg_drawmap do not all have to be the same size. In our triangle example, the perimeter is 9 pixels wide by 5 pixels high, but the interior is only 5 pixels wide by 3 pixels high. Hence, the bit map for the interior pixels only requires one byte for each of its three rows, so we can store it in a three-byte array. Its structure would be: [2] 08 [1] 1C [0] 3E Example 9-2 is similar to example 9-1, but it uses a three-byte array for the interior bit map. Note the second call to fg_move in this example. It is needed because the bottom row of the smaller interior bit map corresponds to the second row of the larger perimeter bit map. In other words, the interior bit map must be displayed one row above the perimeter bit map. Example 9-2. #include #include #include void main(void); char perimeter[] = { 0xFF,0x80,0x41,0x00,0x22,0x00,0x14,0x00,0x08,0x00 }; char interior[] = { 0x3E,0x1C,0x08 }; void main() { int old_mode, new_mode; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { 140 Fastgraph User's Guide printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_setcolor(7); fg_rect(0,319,0,199); fg_move(156,101); fg_setcolor(1); fg_drawmap(perimeter,2,5); fg_move(156,100); fg_setcolor(2); fg_drawmap(interior,1,3); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } In example 9-2, the time required to execute the second call to fg_move may not be worth the saving of 7 bytes. When array space is critical, or when the images are larger, the use of smaller bit maps for certain colors may be more valuable. Yet another possibility for example 9-2 would be to shift the elements of the interior bit map two pixels to the left. In this way, the bit map would be aligned against the left side of the array, just as the perimeter bit map is. The three values comprising the interior bit map would then become F8, 70, and 20. We also would need to change the x coordinate in the second call to fg_move from 156 to 158. Mode-Specific Bit-Mapped Images This section will discuss the image display routines that use bit-mapped image formats that are specific to each text and graphics video mode. The different image formats closely resemble the structure of video memory in each mode, so these routines are much faster than displaying mode-independent bit maps with fg_drawmap. If you use the mode-specific bit maps in a program that supports several video modes, there will be some additional programming that is not needed when using mode-independent bit maps. Usually, however, your efforts will be rewarded with faster graphics. We'll demonstrate the use of mode-specific bit maps in graphics modes with the familiar two-color triangle whose pixel representation appears below. . . . . * . . . . . . . * x * . . . . . * x x x * . . . * x x x x x * . * * * * * * * * * Chapter 9: Images and Image Management 141 As before, our triangle is 9 pixels wide at its base and 5 pixels high. The pixels indicated by an asterisk (*) are the triangle's perimeter, while those indicated by an x represent its interior points. We need to distinguish between these pixels because they will be different colors. The pixels shown as periods (.) are not part of the triangle itself. They are required to make the image rectangular, so from Fastgraph's perspective they are indeed part of the image. Regular Images The Fastgraph routine fg_drwimage displays regular mode-specific bit- mapped images (by regular, we mean an image that is neither clipped nor rotated). Its arguments are the same as for the fg_drawmap routine, and the bit map array's subscript order is also the same as for fg_drawmap. The major difference is the bit map structure -- we combine the information for all colors into a single bit map, in a way consistent with the structure and accessibility of video memory for the various modes. As with the other image display routines, fg_drwimage displays the image on the active video page with its lower left corner at the graphics cursor position (or the text cursor position for text modes). We'll now examine the use of fg_drwimage in several video modes. CGA four-color graphics modes In the four-color CGA graphics modes (modes 4 and 5), each pixel can assume a value between 0 and 3. This means it takes two bits to represent a pixel, or put another way, each byte of video memory holds four pixels. Our triangle image is nine pixels wide, so three bytes are needed for each row of the image. Because the image is five pixels high, we need a bit map array of at least 15 bytes (five rows times three bytes per row) to hold the image. The image's binary representation and its hexadecimal equivalent for the four-color CGA graphics modes are shown below. The binary values in boldface represent the actual image; the others are the filler bits needed to complete each row of the bit map after the ninth pixel. We have coded the perimeter pixels to be color 1 (01 binary) and the interior pixels to be color 2 (10 binary). Any pixel whose value is zero (00 binary) is transparent and will thus leave the contents of video memory at that position unchanged. 00 00 00 00 01 00 00 00 00 00 00 00 00 40 00 00 00 00 01 10 01 00 00 00 00 00 00 01 90 00 00 00 01 10 10 10 01 00 00 00 00 00 06 A4 00 00 01 10 10 10 10 10 01 00 00 00 00 1A A9 00 01 01 01 01 01 01 01 01 01 00 00 00 55 55 40 Example 9-3 uses this mode-specific bit map to display the triangle in the standard CGA four-color graphics mode (mode 4). After establishing the video mode, the program uses fg_rect to fill the entire screen with a white rectangle. Next, the program establishes (156,101) as the graphics cursor position; this causes the triangle to be centered on the screen. The call to 142 Fastgraph User's Guide fg_drwimage produces a triangle with a cyan perimeter (color 1) and a magenta interior (color 2). Example 9-3. #include #include #include void main(void); char triangle[] = { 0x55,0x55,0x40, 0x1A,0xA9,0x00, 0x06,0xA4,0x00, 0x01,0x90,0x00, 0x00,0x40,0x00 }; void main() { int old_mode; if (fg_testmode(4,1) == 0) { printf("This program requires a 320 "); printf("x 200 CGA graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(4); fg_setcolor(7); fg_rect(0,319,0,199); fg_move(156,101); fg_drwimage(triangle,3,5); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } CGA two-color graphics mode In the two-color CGA graphics mode (mode 6), each pixel can assume the values 0 or 1. This means it takes just one bit to represent a pixel, so each byte of video memory holds eight pixels. Our triangle image is nine pixels wide, so two bytes are needed for each row of the image. Because the image is five pixels high, we need a bit map array of at least 10 bytes (five rows times two bytes per row) to hold the image. The image's binary representation and its hexadecimal equivalent for the two-color CGA graphics mode is shown below. The binary values in boldface represent the actual image; the others are the filler bits needed to complete each row of the bit map after the ninth pixel. We have coded both the perimeter pixels and the interior pixels to be color 1. Any pixel whose value is zero is transparent and will thus leave the contents of video memory at that position unchanged. Chapter 9: Images and Image Management 143 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 08 00 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1C 00 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 3E 00 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 7F 00 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 FF 80 Example 9-4 uses this mode-specific bit map to display the triangle in the CGA two-color graphics mode (mode 6). After establishing the video mode, the program establishes (316,101) as the graphics cursor position; this causes the triangle to be centered on the screen. The call to fg_drwimage produces a solid triangle. Example 9-4. #include #include #include void main(void); char triangle[] = { 0xFF,0x80, 0x7F,0x00, 0x3E,0x00, 0x1C,0x00, 0x08,0x00 }; void main() { int old_mode; if (fg_testmode(6,1) == 0) { printf("This program requires a "); printf("CGA graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(6); fg_move(316,101); fg_drwimage(triangle,2,5); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Tandy/PCjr 16-color graphics mode The structure of the mode-specific bit maps for the Tandy/PCjr 16-color graphics mode (mode 9) is the same as the mode-specific bit map structure for the native EGA and VGA graphics modes. Please refer to page 144 for a discussion of EGA and VGA bit maps. 144 Fastgraph User's Guide Hercules graphics modes The structure of the mode-specific bit maps for the Hercules graphics modes (modes 11 and 12) is the same as two of the CGA graphics modes. For the standard Hercules graphics mode (mode 11), please refer to the discussion of CGA two-color (mode 6) bit maps on page 142. For the low-resolution Hercules graphics mode (mode 12), please refer to the discussion of the CGA four-color (mode 4) bit maps on page 141. EGA and VGA graphics modes In the native EGA and VGA graphics modes (modes 13 through 18), each pixel can assume a value between 0 and 15. This means it takes four bits to represent a pixel, so each byte of the bit map holds two pixels. Our triangle image is nine pixels wide, so five bytes are needed for each row of the image. Because the image is five pixels high, we need a bit map array of at least 25 bytes (five rows times five bytes per row) to hold the image. In these modes, it is easy to develop the hexadecimal representation of a bit map without first producing its binary equivalent. This is because a pixel value and a hexadecimal digit each occupy four bits. The triangle's hexadecimal representation for the native EGA and VGA modes is shown below. The pixels in boldface represent the actual image; the others are the filler values needed to complete each row of the bit map after the ninth pixel. We have chosen to display the perimeter pixels in color 1 and the interior pixels in color 2. Any pixel whose value is zero is transparent and will thus leave the contents of video memory at that position unchanged. 00 00 10 00 00 00 01 21 00 00 00 12 22 10 00 01 22 22 21 00 11 11 11 11 10 Example 9-5 is similar to example 9-3, but it uses the 320 x 200 EGA graphics mode (mode 13) and the mode-specific bit map just constructed to display the triangle. The call to fg_drwimage produces a triangle with a blue perimeter (color 1) and a green interior (color 2). Example 9-5. #include #include #include void main(void); char triangle[] = { 0x11,0x11,0x11,0x11,0x10, 0x01,0x22,0x22,0x21,0x00, 0x00,0x12,0x22,0x10,0x00, 0x00,0x01,0x21,0x00,0x00, 0x00,0x00,0x10,0x00,0x00 }; Chapter 9: Images and Image Management 145 void main() { int old_mode; if (fg_testmode(13,1) == 0) { printf("This program requires a 320 "); printf("x 200 EGA graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(13); fg_setcolor(7); fg_rect(0,319,0,199); fg_move(156,101); fg_drwimage(triangle,5,5); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } MCGA and VGA 256-color graphics modes In the MCGA and VGA 256-color graphics modes (modes 19 through 23), each pixel can assume a value between 0 and 255 (FF hex). This means it takes eight bits to represent a pixel, or each byte of video memory holds one pixel. Our triangle image is nine pixels wide, so nine bytes are needed for each row of the image. Because the image is five pixels high, we need a bit map array of at least 45 bytes (five rows times nine bytes per row) to hold the image. Note we will never need any filler bits in the 256-color video modes. In the 256-color graphics video modes, it is simple to develop the bit map for an image because each byte holds exactly one pixel. The triangle's hexadecimal representation for the 256-color graphics modes is shown below. As before, we have coded the perimeter pixels to be color 1 (01 hex) and the interior pixels to be color 2 (02 hex). Any pixel whose value is zero is transparent and will thus leave the contents of video memory at that position unchanged. 00 00 00 00 01 00 00 00 00 00 00 00 01 02 01 00 00 00 00 00 01 02 02 02 01 00 00 00 01 02 02 02 02 02 01 00 01 01 01 01 01 01 01 01 01 146 Fastgraph User's Guide Example 9-6 is also similar to example 9-3, but it uses the MCGA 256- color graphics mode (mode 19) and the mode-specific bit map just constructed to display the triangle. The call to fg_drwimage produces a triangle with a blue perimeter (color 1) and a green interior (color 2). Example 9-6. #include #include #include void main(void); char triangle[] = { 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x00,0x01,0x02,0x02,0x02,0x02,0x02,0x01,0x00, 0x00,0x00,0x01,0x02,0x02,0x02,0x01,0x00,0x00, 0x00,0x00,0x00,0x01,0x02,0x01,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00 }; void main() { int old_mode; if (fg_testmode(19,1) == 0) { printf("This program requires a 320 "); printf("x 200 MCGA graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(19); fg_setcolor(7); fg_rect(0,319,0,199); fg_move(156,101); fg_drwimage(triangle,9,5); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Text Modes You also can use the fg_drwimage routine to display images in text video modes (modes 0, 1, 2, 3, and 7). As one might expect, the image structure in the text modes is rather different from the graphics modes. In Chapter 5 we saw that each character cell on the screen actually consists of a character and an attribute. The character value determines what character is displayed, while the attribute value controls the character's appearance. The structure of the attribute is: Chapter 9: Images and Image Management 147 bits attribute 0-3 foreground color 4-6 background color 7 blinking The text mode image structure used with fg_drwimage also consists of a series of characters and attributes. For example, the following diagram illustrates the structure of an image that is three characters wide and two characters high. char attr char attr char attr char attr char attr char attr To illustrate the use of fg_drwimage in a text video mode, we'll display the phrase "hello there" on two different lines in the center of the screen. Furthermore, let's assume we would like the first character of each word to appear in foreground color 1, the second in color 2, and so forth. Our image will consist of two lines each containing five characters, and each character requires two bytes of storage (one for the character and another for its attribute), so we'll need a 20-byte array for holding the image. The array really doesn't hold a bit map as in the graphics modes, so in the text modes the first argument passed to fg_drwimage is instead called the image array. In our example, the structure of the image array is: 'h' 1 'e' 2 'l' 3 'l' 4 'o' 5 't' 1 'h' 2 'e' 3 'r' 4 'e' 5 The subscript order that fg_drwimage uses for text modes is the same as for the graphics modes. For our five-row by two-column image, this means the array subscripts would be numbered as follows: [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] Depending on the character and attribute values in the image array, fg_drwimage can display new characters and attributes, new characters leaving the existing attribute unchanged, new attributes leaving the existing character unchanged, or leave both the existing character and attribute unchanged in video memory. To keep an existing character or attribute, simply specify a value of 0 in the corresponding element of the image array. This capability is analogous to the fact that zero-valued pixels in graphics mode bit maps leave video memory unchanged. 148 Fastgraph User's Guide Example 9-7 demonstrates the use of the fg_drwimage routine in the 80- column color text mode (mode 3). After establishing the video mode and making the cursor invisible, the program calls fg_drwimage to display the "hello there" image just discussed (note we pass the dimensions of the image array as the number of bytes, not the number of characters). The program waits for a keystroke and then calls fg_drwimage again, passing a different image array (called "image") of the same size. This array changes the first letter of both words from lower case to upper case (leaving the attribute unchanged), and it makes the remaining characters have the same attribute as the first character. This is done in part by using zero-valued characters and attributes to leave video memory unchanged. After waiting for another keystroke, the program exits. Example 9-7. #include void main(void); char hello[] = { 't',1, 'h',2, 'e',3, 'r',4, 'e',5, 'h',1, 'e',2, 'l',3, 'l',4, 'o',5 }; char image[] = { 'T',0, 0,1, 0,1, 0,1, 0,1, 'H',0, 0,1, 0,1, 0,1, 0,1 }; void main() { int old_mode; old_mode = fg_getmode(); fg_setmode(3); fg_cursor(0); fg_locate(12,37); fg_drwimage(hello,10,2); fg_waitkey(); fg_drwimage(image,10,2); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Clipped Images The fg_drwimage routine displays an image without regard to the current clipping limits. If you want the image to be displayed with respect to the clipping limits (as established by the most recent call to fg_setclip), you should use the fg_clpimage routine instead of fg_drwimage. Fg_clpimage takes the same three arguments as fg_drwimage, and also displays the image such that its lower left corner is at the graphics cursor position. Unlike fg_drwimage, the fg_clpimage routine has no effect when used in a text video Chapter 9: Images and Image Management 149 mode. Refer to pages 149 and 151 for example programs that use fg_clpimage. Because of the additional overhead involved in checking the clipping limits, fg_clpimage is not as fast as fg_drwimage. Reversed Images The fg_revimage routine displays an image reversed (that is, mirrored about the y-axis). Fg_revimage takes the same three arguments as fg_drwimage, and also displays the image such that its lower left corner is at the graphics cursor position. The fg_revimage routine has no effect when used in a text video mode. Refer to pages 150 and 151 for example programs that use fg_revimage. Reversed Clipped Images The fg_flpimage routine combines the effects of the fg_revimage and fg_clpimage routines -- it displays a reversed image with respect to the current clipping limits. Fg_flpimage takes the same three arguments as fg_drwimage, and also displays the image such that its lower left corner is at the graphics cursor position. Like the fg_clpimage routine, fg_flpimage has no effect when used in a text video mode. Refer to pages 149 and 151 for example programs that use fg_flpimage. Some Examples Example 9-8 illustrates the use of the fg_drwimage, fg_clpimage, fg_revimage, and fg_flpimage routines in the standard CGA four-color graphics mode (mode 4). The program uses each of these routines to display a small white arrow, as shown in the pixel map below. . . . . . . * . . . . . . . . . * * . . * * * * * * * * * . * * * * * * * * * * * * * * * * * * * . . . . . . . * * . . . . . . . . * . . . As before, we must first convert this image to a bit map. The image is ten pixels wide and seven high. In mode 4, each pixel occupies two bits, so we need a 21-byte array (7 rows by 3 columns) to store the image. Since we want to make the arrow white, each pixel will be displayed in color 3 (11 binary). Here is the bit map and its hexadecimal equivalent for the arrow image in mode 4 (the actual image is in boldface). 00 00 00 00 00 00 11 00 00 00 00 00 00 0C 00 00 00 00 00 00 00 11 11 00 00 00 00 00 0F 00 11 11 11 11 11 11 11 11 11 00 00 00 FF FF C0 11 11 11 11 11 11 11 11 11 11 00 00 FF FF F0 11 11 11 11 11 11 11 11 11 00 00 00 FF FF C0 00 00 00 00 00 00 11 11 00 00 00 00 00 0F 00 00 00 00 00 00 00 11 00 00 00 00 00 00 0C 00 150 Fastgraph User's Guide After establishing the video mode, the program defines the clipping region. It then uses fg_drwimage to display the arrow pointing to the right and fg_clpimage to do the same thing, but with respect to the clipping limits. Because the left edge of the arrow is displayed at x=10 and the right clipping limit is at x=15, the call to fg_clpimage only draws the first six columns of the arrow (that is, it does not draw the arrow head). Next, example 9-8 uses fg_revimage to display the arrow pointing to the left. To allow for the filler pixels, we must establish the graphics cursor position two pixels to the left of the position used by fg_drwimage if we want the tip of the left-pointing arrow to align with the tail of the right- pointing arrow. Finally, the program uses fg_flpimage to display an arrow pointing to the left with regard to the clipping limits. The call to fg_flpimage displays the arrow head and the first two columns of the arrow shaft. Example 9-8. #include #include #include void main(void); char arrow[] = { 0x00,0x0C,0x00, 0x00,0x0F,0x00, 0xFF,0xFF,0xC0, 0xFF,0xFF,0xF0, 0xFF,0xFF,0xC0, 0x00,0x0F,0x00, 0x00,0x0C,0x00 }; void main() { int old_mode; if (fg_testmode(4,1) == 0) { printf("This program requires a 320 "); printf("x 200 CGA graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(4); fg_setclip(0,15,0,199); fg_move(10,10); fg_drwimage(arrow,3,7); fg_move(10,20); fg_clpimage(arrow,3,7); fg_move(8,30); fg_revimage(arrow,3,7); fg_move(8,40); fg_flpimage(arrow,3,7); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Chapter 9: Images and Image Management 151 Example 9-9 is the same as example 9-8, but it uses the low resolution EGA graphics mode (mode 13). If we changed the mode number specified in the calls to fg_testmode and fg_setmode, the program also would run in any native EGA or VGA graphics mode, or in the Tandy/PCjr 16-color graphics mode. In these modes, we store two pixels per byte in the bit map array, so we need a 35-byte array (7 rows by 5 columns) to store the image. Here is the bit map's hexadecimal equivalent for the arrow image in mode 13, followed by the program to display it. 00 00 00 F0 00 00 00 00 FF 00 FF FF FF FF F0 FF FF FF FF FF FF FF FF FF F0 00 00 00 FF 00 00 00 00 F0 00 Example 9-9. #include #include #include void main(void); char arrow[] = { 0x00,0x00,0x00,0xF0,0x00, 0x00,0x00,0x00,0xFF,0x00, 0xFF,0xFF,0xFF,0xFF,0xF0, 0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xF0, 0x00,0x00,0x00,0xFF,0x00, 0x00,0x00,0x00,0xF0,0x00 }; void main() { int old_mode; if (fg_testmode(13,1) == 0) { printf("This program requires a 320 "); printf("x 200 EGA graphics mode.\n"); exit(1); } 152 Fastgraph User's Guide old_mode = fg_getmode(); fg_setmode(13); fg_setclip(0,15,0,199); fg_move(10,10); fg_drwimage(arrow,5,7); fg_move(10,20); fg_clpimage(arrow,5,7); fg_move(8,30); fg_revimage(arrow,5,7); fg_move(8,40); fg_flpimage(arrow,5,7); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Pixel Run Maps The bit maps used with the fg_drawmap, fg_drwimage, and related routines can consume array space quite rapidly. This is especially true if the image is large or contains many colors. For example, a mode-independent bit-mapped image that occupies the entire screen in a 320 by 200 graphics mode requires 8,000 bytes of space per color. Fastgraph provides another mode-independent image format called pixel run maps, which are more efficient in terms of space. In pixel run maps, you store the entire image in a single array. Pixel run maps are particularly useful for displaying static images such as backgrounds. Let's return to our familiar triangle example and show how we could use a pixel run map to display it. . . . . * . . . . . . . * x * . . . . . * x x x * . . . * x x x x x * . * * * * * * * * * As before, the pixels indicated by an asterisk (*) are the triangle's perimeter, while those indicated by an x represent its interior points. The pixels shown as periods (.) are not part of the triangle itself, but they are part of the pixel run map. If we start at the lower left corner of the image and proceed to the right, we could represent the first row of the image as nine pixels of color "asterisk". Such a group of consecutive identically colored pixels is called a pixel run, so a single pixel run describes the first row of the image. The row above this one is a bit more complex. It consists of five pixel runs: one pixel of color "period", followed by one of color "asterisk", then five of color "x", one of color "asterisk", and finally one of color "period". While we could construct separate pixel runs for each row of the image, notice that three of the five rows in our triangle begin with the same color pixel as the rightmost pixel in the previous row. Fastgraph's pixel run map format lets you take advantage of this property by allowing pixel runs to Chapter 9: Images and Image Management 153 wrap from one row to the next. This means we can represent the pixel run of color "period" extending from the right side of the second row to the left side of the third row as a single run of three pixels. The Fastgraph routine fg_display displays an image stored as a pixel run map. The fg_display routine expects three arguments. The first is an array containing the pixel runs (passed by reference), the second is the number of pixel runs in the array, and the third is the width in pixels of the image. As with the other image display routines, the fg_display routine displays the image such that its lower left corner is at the graphics cursor position on the active video page. The pixel run array is of the following format: [0] color for run 1 [1] count for run 1 [2] color for run 2 [3] count for run 2 . . . [2n-2] color for run n [2n-1] count for run n Each color is a value between 0 and 255 specifying the color index for that pixel run. Each count is a value between 0 and 255 specifying the length in pixels of that pixel run. If a run is longer than 255 pixels, it must be broken into two or more runs. For example, we could represent a pixel run of length 265 as a run of length 255 followed by a run of length 10 of the same color. Note also the array space in bytes needed to store a pixel run map is twice the number of runs. It requires 16 pixel runs to store our triangle image as a pixel run map. If we want to display the perimeter pixels in color 1, the interior pixels in color 2, and the filler area in color 7, the pixel run map would contain 16 sets of (color,count) pairs: (1,9), (7,1), (1,1), (2,5), (1,1), (7,3), (1,1), (2,3), (1,1), (7,5), (1,1), (2,1), (1,1), (7,7), (1,1), and (7,4). Example 9-10 uses the fg_display routine to display the triangle as a pixel run map in a 320 by 200 graphics mode. The program displays the triangle against a background of color 7, so the selection of color 7 for the filler area was important. If some other color were chosen, the filler area would not blend in with the background. Example 9-10. #include #include #include void main(void); 154 Fastgraph User's Guide char triangle[] = { 1,9, 7,1, 1,1, 2,5, 1,1, 7,3, 1,1, 2,3, 1,1, 7,5, 1,1, 2,1, 1,1, 7,7, 1,1, 7,4 }; void main() { int old_mode, new_mode; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_setcolor(7); fg_rect(0,319,0,199); fg_move(156,101); fg_display(triangle,16,9); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } If you have a pixel run map that only uses the first 16 color indices (0 to 15), you can use Fastgraph's packed pixel run map image format. This format packs two color values into each color byte and thus needs 25% less array space to store an image. The Fastgraph routine fg_displayp displays an image stored as a packed pixel run map. It is the same as the fg_display routine except for the structure of the pixel run array. Like fg_display, the pixel run array used with fg_displayp is a list of pixel runs, but two runs are packed into three bytes. In each such set of three bytes, the high four bits of the first byte contain the color of the first run, and the low four bits contain the color of the second run. The second byte contains the length of the first run, and the third byte contains the length of the second run. The following diagram illustrates the format of the pixel run array used with the fg_displayp routine. The image is assumed to contain n pixel runs, where n is an even number. If n is odd, the index of the last element is 3n/2 (truncated) instead of 3n/2-1, and the low four bits of the last color byte (that is, the color for pixel run n+1) are ignored. 7 4 3 0 [0] color for run 1 color for run 2 [1] count for run 1 [2] count for run 2 Chapter 9: Images and Image Management 155 [3] color for run 3 color for run 4 [4] count for run 3 [5] count for run 4 . . . [3n/2-3] color for run n-1 color for run n [3n/2-2] count for run n-1 [3n/2-1] count for run n The structure of the packed pixel run array allows for color values to be between 0 and 15, and pixel run lengths to be between 0 and 255. The array space in bytes needed to store a packed pixel run map is 1.5 times the number of runs, compared to twice the number of runs for the standard pixel run format. Example 9-11 is the same as example 9-10, but it uses fg_displayp rather than fg_display to display the image. Note the use of hexadecimal numbers for defining the packed color values, which of course is not necessary but certainly easier to read than expressing the quantities as decimal numbers. Example 9-11. #include #include #include void main(void); char triangle[] = { 0x17,9,1, 0x12,1,5, 0x17,1,3, 0x12,1,3, 0x17,1,5, 0x12,1,1, 0x17,1,7, 0x17,1,4 }; void main() { int old_mode, new_mode; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_setcolor(7); fg_rect(0,319,0,199); fg_move(156,101); 156 Fastgraph User's Guide fg_displayp(triangle,16,9); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Both the fg_display and fg_displayp routines require the pixel run image to be stored in an array. In examples 9-10 and 9-11, the image is defined within the program itself. However, if the image is stored in a file, it must first be read into the pixel run array. Example 9-12 demonstrates this procedure. The program displays two images stored in files, one in standard pixel run format and the other in packed pixel run format. Each image is a picture of the sea floor and some coral, as might be used for the background in an aquarium. The program runs in a 320 by 200 graphics mode, and the image fills the entire screen. It is assumed the image files contain the list of pixel runs as a single byte stream that does not include embedded characters such as carriage returns or line feeds. The first image, in standard pixel run format, is in the file coral.spr. Note the program must open the file for reading in binary mode ("rb" in the call to fopen). The program reads the file's entire contents into the pixel_runs array, whose size must be at least as large as the file size. Because the image is stored in standard pixel run format, the number of pixel runs is one-half the file size. The program then uses the fg_move routine to establish the lower left corner of the screen as the graphics cursor position and then calls fg_display to display the image. As mentioned earlier, the image fills the entire screen, so its width is 320 pixels. After waiting for a keystroke, the program similarly displays the second image. This image is in the file coral.ppr and is stored in packed pixel run format. Because the image is packed, the number of pixel runs is two-thirds the file size. The program then clears the previous image from the screen and calls fg_displayp to display the image. After another keystroke, the program restores the original video mode and screen attributes and returns to DOS. Example 9-12. #include #include #include #include void main(void); char pixel_runs[20000]; void main() { long filelength(); FILE *stream; int file_size, run_count; int old_mode, new_mode; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { Chapter 9: Images and Image Management 157 printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); stream = fopen("coral.spr","rb"); file_size = (int)(filelength(fileno(stream))); fread(pixel_runs,sizeof(char),file_size,stream); fclose(stream); run_count = file_size / 2; fg_move(0,199); fg_display(pixel_runs,run_count,320); fg_waitkey(); stream = fopen("coral.ppr","rb"); file_size = (int)(filelength(fileno(stream))); fread(pixel_runs,sizeof(char),file_size,stream); fclose(stream); run_count = file_size / 3 * 2; fg_erase(); fg_displayp(pixel_runs,run_count,320); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Another Fastgraph routine, fg_dispfile, displays an image directly from a file. This eliminates the need to read the file contents into an array before displaying the image, and it also eliminates the need to compute the number of pixel runs in the image. The fg_dispfile routine can display images stored in either standard or packed pixel run images. The first of its three arguments is the name of the file containing the image (it may include a path name). The file name must be terminated with a null character, so QuickBASIC, FORTRAN, and Turbo Pascal programmers will need to store a zero byte as the last character of the file name string. The second argument is the width in pixels of the image, and the third argument defines the image format (that is, standard or packed). As with fg_display and fg_displayp, the fg_dispfile routine displays the image such that its lower left corner is at the graphics cursor position. Example 9-13 illustrates how to use the fg_dispfile routine to display an image stored in a pixel run file. It is functionally identical to example 9-12, but it is much simpler because it uses fg_dispfile instead of fg_display and fg_displayp to display the images. The value of fg_dispfile's third argument tells Fastgraph the image format. A value of 0 indicates the file contains an image in standard pixel run format, while a value of 1 indicates an image in packed pixel run format. As in example 9-12, the image files are assumed to contain the list of pixel runs as a single byte stream that does not include embedded characters such as carriage returns or line feeds. Example 9-13. #include 158 Fastgraph User's Guide #include #include void main(void); void main() { int old_mode, new_mode; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_move(0,199); fg_dispfile("coral.spr",320,0); fg_waitkey(); fg_erase(); fg_dispfile("coral.ppr",320,1); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } To display the image, the fg_dispfile routine tries to allocate enough dynamic memory to read the entire file. If it is unable to do so, it allocates the available memory and displays the image in more than one pass. In either case, Fastgraph deallocates the memory after fg_dispfile displays the image. The SNAPSHOT utility distributed with Fastgraph is a terminate and stay resident program (TSR) that can capture graphics mode screen images and save them in standard pixel run files. Thus, you can easily create files with SNAPSHOT and display them with the fg_dispfile routine. Another TSR utility, GrabRGB, is useful for capturing RGB color values from 256-color images. Appendix A contains complete descriptions of the SNAPSHOT and GrabRGB utilities. Display Patterns Examples 9-11, 9-12, and 9-13 work well in the graphics video modes with 16 or 256 available colors. However, in the four-color CGA graphics modes the resulting image is not too good because of our limited color choices, and it would look even worse in the Hercules graphics mode. The Fastgraph routine fg_pattern allows you to associate a dither pattern (actually, any pixel sequence) with one of Fastgraph's 256 color indices appearing in a pixel run map. When displaying a pixel run map (with fg_display, fg_displayp, or fg_dispfile), Fastgraph will use the pattern associated with that color index instead of displaying the color itself. Chapter 9: Images and Image Management 159 The fg_pattern routine requires two integer arguments -- a color index (between 0 and 255) and the display pattern defined for that color index. A display pattern's structure resembles the structure of video memory and is thus dependent on the current video mode. The following sections list the initial display patterns and explain how to construct new display patterns for different graphics video modes. CGA four-color graphics modes In the four-color CGA graphics modes (modes 4 and 5), the display pattern is a 16-bit quantity consisting of an 8-bit shift count followed by an 8-bit pixel pattern. Each pixel assumes a value between 0 and 3, so the pattern represents four pixels. In even-numbered pixel rows, Fastgraph uses the pixel pattern itself. In odd-numbered pixel rows, Fastgraph rotates the original pattern to the left by the number of bits specified by the shift count. For example, if we are using the default CGA color palette, we could create a lighter shade of cyan by alternating cyan pixels (color 1, 01 binary) with white pixels (color 3, 11 binary), as shown below. 01 11 01 11 If we convert this pixel pattern to its hexadecimal equivalent, we get the value 77. To complete the display pattern, we need to determine the shift count. If we use a shift count of zero, the resulting display will simply be a series of cyan and white vertical lines. What we really need is a checkerboard effect where a white pixel is above and below each cyan pixel, and vice versa. If we rotate the pattern one pixel (two bits) to the left, we will achieve the desired effect. That is, a shift count of two produces the following pixel patterns: even-numbered rows 01 11 01 11 odd-numbered rows 11 01 11 01 Combining the shift count with the pixel pattern yields the display pattern 0277 hex. The shift count is normally a multiple of two; note that a zero shift count results in the same pattern being applied to all pixel rows. For the CGA four-color graphics modes, the fg_setmode routine establishes the following initial display patterns: color shift count hexadecimal index and pattern equivalent 0 0 00000000 0000 1 0 01010101 0055 2 0 10101010 00AA 3 0 11111111 00FF 160 Fastgraph User's Guide These values are repeated as necessary to define color indices 4 to 255. That is, colors 4, 8, 12, ... , 252 use the same defaults as color 0. Colors 5, 9, 13, ... , 253 use the same defaults as color 1, and so forth. Also note that pattern 0000 represents four pixels of color 0, 0055 represents four pixels of color 1, 00AA represents four pixels of color 2, and 00FF represents four pixels of color 3. CGA two-color graphics mode In the two-color CGA graphics mode (mode 6), the display pattern is also a 16-bit quantity consisting of an 8-bit shift count followed by an 8-bit pixel pattern. Each pixel assumes the value 0 or 1, so the pattern represents eight pixels. In even-numbered pixel rows, Fastgraph uses the pixel pattern itself. In odd-numbered pixel rows, Fastgraph rotates the original pattern to the left by the number of bits specified by the shift count. For example, we could create a lighter shade of white by alternating black pixels (color 0) with white pixels (color 1), as shown below. 0 1 0 1 0 1 0 1 If we convert this pixel pattern to its hexadecimal equivalent, we get the value 55. To complete the display pattern, we need to determine the shift count. We must rotate the pattern one pixel (one bit) to the left to achieve the checkerboard effect as in the CGA four color graphics modes. That is, a shift count of one produces the following pixel patterns: even-numbered rows 0 1 0 1 0 1 0 1 odd-numbered rows 1 0 1 0 1 0 1 0 Combining the shift count with the pixel pattern yields the display pattern 0155 hex. For the CGA two-color graphics mode, the fg_setmode routine establishes the initial display patterns such that all even-numbered color indices are assigned the value 0000, while all odd-numbered color indices are assigned the value 00FF. Note that pattern 0000 represents eight pixels of color 0, and 00FF represents eight pixels of color 1. Tandy/PCjr 16-color graphics mode In the Tandy/PCjr 16-color graphics mode (mode 9), the display pattern is also 16-bit quantity consisting of an 8-bit shift count followed by an 8- bit pixel pattern. Each pixel assumes a value between 0 and 15, so the pattern represents two pixels. In even-numbered pixel rows, Fastgraph uses the pixel pattern itself. In odd-numbered pixel rows, Fastgraph rotates the original pattern to the left by the number of bits specified by the shift count. For example, we could create a lighter shade of blue by alternating blue pixels (color 1, 0001 binary) with white pixels (color 15, 1111 binary), as shown below. Chapter 9: Images and Image Management 161 0001 1111 If we convert this pixel pattern to its hexadecimal equivalent, we get the value 1F. To complete the display pattern, we need to determine the shift count. Using the same process as in the CGA graphics modes, we must rotate the pattern one pixel (four bits) to the left to achieve the checkerboard effect. That is, a shift count of four produces the following pixel patterns: even-numbered rows 0001 1111 odd-numbered rows 1111 0001 Combining the shift count with the pixel pattern yields the display pattern 041F hex. The shift count is normally zero or four; note that a zero shift count results in the same pattern being applied to all pixel rows. For the Tandy/PCjr 16-color graphics modes, the fg_setmode routine establishes the initial display patterns such that color 0 is assigned the value 0000 (two pixels of color 0), color 1 is assigned the value 0011 (two pixels of color 1), color 2 is assigned the value 0022 (two pixels of color 2), and so forth. These values are repeated as necessary to define color indices 16 to 255. That is, colors 0, 16, 32, ... , 240 use the same defaults as color 0. Colors 1, 17, 33, ... , 241 use the same defaults as color 1, and so forth. Hercules graphics modes The structure of the display patterns for the Hercules graphics modes (modes 11 and 12) is the same as two of the CGA graphics modes. For the standard Hercules graphics mode (mode 11), please refer to the discussion of CGA two-color (mode 6) display patterns on page 160. For the low-resolution Hercules graphics mode (mode 12), please refer to the discussion of the CGA four-color (mode 4) display patterns on page 159. EGA graphics modes In the EGA graphics modes (modes 13 to 16), the display pattern is an 8- bit quantity consisting of two 4-bit color values (for consistency with the other video modes, we still pass the display pattern as a 16-bit quantity). Each pixel assumes a value between 0 and 15 (0 and 5 in the EGA monochrome graphics mode), so the pattern represents two pixels. In even-numbered pixel rows, Fastgraph uses the pixel pattern itself. In odd-numbered pixel rows, Fastgraph rotates the original pattern one pixel (four bits) to the left. For example, we could create a lighter shade of blue by alternating blue pixels (color 1, 0001 binary) with white pixels (color 15, 1111 binary), as shown below. 0001 1111 If we convert this pixel pattern to its hexadecimal equivalent, we get the value 1F. The implied four-bit shift count produces the following pixel patterns: 162 Fastgraph User's Guide even-numbered rows 0001 1111 odd-numbered rows 1111 0001 Extending the pixel pattern to a 16-bit quantify yields the display pattern 001F hex. For the EGA and VGA 16-color graphics modes, the fg_setmode routine establishes the initial display patterns such that color 0 is assigned the value 0000 (two pixels of color 0), color 1 is assigned the value 0011 (two pixels of color 1), color 2 is assigned the value 0022 (two pixels of color 2), and so forth. These values are repeated as necessary to define color indices 16 to 255. That is, colors 0, 16, 32, ... , 240 use the same defaults as color 0. Colors 1, 17, 33, ... , 241 use the same defaults as color 1, and so forth. MCGA/VGA 2-color graphics mode In the two-color MCGA/VGA graphics mode (mode 17), the display pattern is a 2-bit quantity consisting of two 1-bit color values (for consistency with the other video modes, we still pass the display pattern as a 16-bit quantity). Each pixel assumes the value 0 or 1, so the pattern represents two pixels. In even-numbered pixel rows, Fastgraph uses the pixel pattern itself. In odd-numbered pixel rows, Fastgraph rotates the original pattern one pixel (one bit) to the left. For example, we could create a lighter shade of white by alternating black pixels (color 0) with white pixels (color 1), as shown below. 0 1 If we convert this pixel pattern to its hexadecimal equivalent, we get the value 01. The implied one-bit shift count produces the following pixel patterns: even-numbered rows 0 1 odd-numbered rows 1 0 Extending the pixel pattern to a 16-bit quantity yields the display pattern 0001 hex. For the MCGA/VGA two-color graphics mode, the fg_setmode routine establishes the initial display patterns such that all even-numbered color indices are assigned the value 0000 (two pixels of color 0), while all odd- numbered color indices are assigned the value 0003 (11 binary, or two pixels of color 1). VGA 16-color graphics mode The structure of the display patterns for the VGA 16-color graphics mode (mode 18) is the same as that of the EGA graphics modes. A discussion of EGA display patterns appears on page 161. Chapter 9: Images and Image Management 163 MCGA and VGA 256-color graphics modes The MCGA and VGA 256-color graphics modes (modes 19 through 23) offer 262,144 different colors, so dithering is seldom (if ever) required. For this reason, the fg_pattern routine has no effect in these video modes. An example Example 9-14 illustrates the use of display patterns in several graphics modes. This program runs in any 320 by 200 color graphics mode and displays the coral image in packed pixel run format, as in example 9-13, but it redefines one or more of the color indices. If the program runs in the standard CGA four-color mode (mode 4), it redefines the first 16 display patterns using the fg_pattern routine and the values in the CGApatterns array. In the Tandy/PCjr 16-color graphics mode (mode 9) and the EGA low- resolution graphics mode (mode 13), the program redefines color index 15 to produce an alternating gray and bright white dither pattern. In the MCGA 256-color mode (mode 19), display patterns are not available, so the program uses fg_setrgb to define color index 15 as slightly darker shade of gray than the default for color 7. Example 9-14. #include #include #include void main(void); int CGApatterns[] = { 0x0000,0x00FF,0x00FF,0x00FF, 0x02BB,0x0000,0x0222,0x0255, 0x00FF,0x00FF,0x00FF,0x0055, 0x00AA,0x00AA,0x00FF,0x0277 }; void main() { int color; int old_mode, new_mode; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); if (new_mode == 4) { fg_palette(0,0); for (color = 0; color < 16; color++) fg_pattern(color,CGApatterns[color]); } else if (new_mode == 9 || new_mode == 13) 164 Fastgraph User's Guide fg_pattern(15,0x04F7); else fg_setrgb(15,38,38,38); fg_move(0,199); fg_dispfile("coral.ppr",320,1); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } PCX Images The PCX file format was originally developed by ZSoft Corporation for their commercial paint program, PC Paintbrush. It has evolved into one of the more popular image file formats because so many products can read PCX files to at least some extent. Fastgraph includes routines for displaying and creating PCX files. The fg_disppcx routine displays an image stored in a PCX file. It positions the image such that its upper left corner is at the graphics cursor position on the active video page. The first argument to fg_disppcx is the name of the PCX file (it may include a path name), and its second argument is a 16-bit mask that controls how the image is displayed. The file name must be terminated with a null character, so QuickBASIC, FORTRAN, and Turbo Pascal programmers will need to store a zero byte as the last character of the file name string. In the current version of Fastgraph, only the low-order bit (bit 0) of the bit mask argument is meaningful. If the bit is set, fg_disppcx will use the current palette settings. If it is zero, fg_disppcx will use the palette values stored in the PCX file. All other bits are reserved and should be zero. The fg_disppcx routine returns a value of 0 if successful, 1 if the specified file wasn't found, and 2 if the file is not a PCX file. The fg_makepcx routine creates a PCX file from the specified rectangular region of the active video page. Its first four arguments define the minimum x, maximum x, minimum y, and maximum y screen space coordinates of the region (the minimum x coordinate is reduced to a byte boundary if necessary). Its fifth argument is the name of the PCX file to create (it may include a path name). As with fg_disppcx, the file name must be terminated with a null character. If an identically named file exists, it is overwritten. The fg_makepcx routine returns a value of 0 if successful, and 1 if the PCX file was not created. Example 9-15 uses the fg_disppcx and fg_makepcx routines to create a new PCX file from selected rows of an existing 256-color 320 x 200 PCX file. As written, the program uses the file CORAL.PCX to create NEW.PCX, but it could easily be extended to work with any PCX files. The call to fg_disppcx displays the contents of CORAL.PCX using the palette settings defined in the PCX file. After waiting for a keystroke, the program calls fg_makepcx to create a PCX file named NEW.PCX from pixel rows 80 through 99 of the original image. In case the program encounters any problems, it prints an error message before exiting. Chapter 9: Images and Image Management 165 Example 9-15. #include #include #include void main(void); void main() { int old_mode; int read_status, write_status; if (fg_testmode(19,1) == 0) { printf("This program requires a 320 "); printf("x 200 MCGA graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(19); read_status = fg_disppcx("coral.pcx",0); fg_waitkey(); if (read_status == 0) write_status = fg_makepcx(0,319,80,99,"new.pcx"); else write_status = 1; fg_setmode(old_mode); fg_reset(); if (read_status == 1) printf("CORAL.PCX not found.\n"); else if (read_status == 2) printf("CORAL.PCX is not a PCX file.\n"); if (write_status == 1) printf("NEW.PCX not created.\n"); } Because their structure parallels the structure of video memory, PCX files are specific to certain video modes. The following table summarizes the compatible video modes for PCX files. If PCX file was You can display created in mode it in these modes 4 or 5 4 or 5 6 or 11 6, 11, 13 to 18 9 9 13 to 18 13 to 18 19 19 to 23 Displaying a PCX file at a lower resolution (for example, a 640x480 PCX file at 320x200) will truncate the display on the right and on the bottom. This effectively displays the upper left corner of the PCX file. If you try to 166 Fastgraph User's Guide display a PCX file in an incompatible video mode, fg_disppcx will still display something, but it will be garbled. In the Tandy/PCjr 16-color graphics mode (mode 9) and the native EGA graphics modes (modes 13 through 16), the palette registers are not readable. Hence, fg_makepcx will use the default palette settings when used in these video modes. In 640-column EGA modes, you can get around this problem by creating PCX files in mode 18 and only using the first 200 or 350 pixel rows. PCX files created in mode 18 preserve the current palette settings and will display properly in EGA modes. The fg_disppcx and fg_makepcx routines have no effect in text video modes or in the Hercules low-resolution graphics mode. Masking Maps It is not possible to include color 0 pixels in an image displayed with the fg_drwimage, fg_clpimage, fg_revimage, or fg_flpimage routines. This is because these routines consider color 0 pixels to be transparent, which means such pixels do not affect the corresponding pixels in video memory. There are times, however, when you will want color 0 pixels to be destructive, or replace the video memory contents. Consider again the arrow image of example 9-8 (see page 149). In this example, we displayed a bright white (color 3) arrow against a black (color 0) background in the standard CGA four-color graphics mode. Suppose, though, that we want to do just the opposite -- display a black (color 0) arrow against a bright white (color 3) background. Example 9-9 (see page 151) does this in an EGA graphics mode, but how would we display the black arrow in a CGA graphics mode? We could of course use the fg_drawmap routine or one of the routines for displaying pixel run maps, but fg_drawmap does not support clipping or reversing an image. There are, however, four Fastgraph routines designed just for this purpose. These routines are fg_drawmask, fg_clipmask, fg_revmask, and fg_flipmask. Each of these routines uses a data structure called a masking map. A masking map is similar in structure to a pixel run map, but it does not include any information about colors. Instead, it consists of a series of pixel runs that alternate between protected and unprotected pixels. An example might best clarify this. Once again, here is the arrow image of examples 9-8 and 9-9. . . . . . . * . . . . . . . . . * * . . * * * * * * * * * . * * * * * * * * * * * * * * * * * * * . . . . . . . * * . . . . . . . . * . . . This time, though, we want the arrow to appear in color 0. Put another way, we need the "period" pixels (.) to protect video memory, while we want the "asterisk" pixels (*) to zero video memory. Looking at this problem from the perspective of a pixel run map, we have an alternating series of "protect" and "zero" runs. We don't need any information about pixel colors, just whether to protect or to zero video memory. Chapter 9: Images and Image Management 167 This is precisely the structure of a masking map. Starting from the lower left corner of the image and proceeding to the right, wrapping up to the next row when needed, we could represent this image as a masking map with 6 protected pixels, 1 zeroed pixel, 9 protected pixels, 2 zeroed pixels, and so on. In general, the structure of a masking map is as follows. [1] length of 1st protect run [2] length of 1st zero run [3] length of 2nd protect run [4] length of 2nd zero run . . . [n-2] length of final protect run [n-1] length of final zero run Looking at this diagram, we see that the even-numbered array elements hold the length of the "protect" runs, and the odd-numbered elements hold the length of the "zero" runs. If you need the first run to be a "zero" run, just include a "protect" run of length zero as the first element of the array. If the final run is a "protect" run, you do not need to include a zero-length "zero" run at the end of the array. Finally, if either type of run exceeds 255 pixels, you'll need to split this into two or more pixel runs. In this case, be sure to include a zero-length run of the other type between the two array elements. Example 9-16 illustrates the use of a masking map through the fg_drawmask, fg_clipmask, fg_revmask, and fg_flipmask routines in the standard CGA four-color graphics mode (mode 4) to draw a black (color 0) arrow against a bright white background. These four routines are respectively analogous to the fg_drwimage, fg_clpimage, fg_revimage, and fg_flpimage routines, but they use masking maps rather than bit maps. The first argument of each routine is the masking map array (passed by reference), the second argument is the number of runs (that is, the number of elements) in the masking map array, and the third argument is the width in pixels of the image. Example 9-16. #include #include #include void main(void); char arrow[] = {6,1,9,2,2,9,1,19,7,2,8,1}; void main() { int old_mode; 168 Fastgraph User's Guide if (fg_testmode(4,1) == 0) { printf("This program requires a 320 "); printf("x 200 CGA graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(4); fg_setclip(0,15,0,199); fg_setcolor(3); fg_rect(0,319,0,199); fg_move(10,10); fg_drawmask(arrow,12,10); fg_move(10,20); fg_clipmask(arrow,12,10); fg_move(10,30); fg_revmask(arrow,12,10); fg_move(10,40); fg_flipmask(arrow,12,10); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } One of the more useful features of masking maps is the ability to clear a portion of video memory before placing an image there. This technique provides an efficient, simple way to include color 0 pixels in an image. It is especially effective when displaying large or dithered images because the masking map is typically much smaller than the bit map required by fg_drawmap or its related routines. Example 9-17 illustrates this process in the standard CGA four-color graphics mode (mode 4) by displaying our arrow image against a colored background. In this example, the arrow has a bright white (color 3) perimeter and a black (color 0) interior. The program displays the arrow in two steps. It first uses fg_drawmask to clear the video memory where the arrow will be displayed. It then draws the arrow's perimeter using the fg_drwimage routine. The interior pixels in the perimeter bit map are transparent, but since we just zeroed that video memory, they appear in color 0. Note we could improve this example by creating a smaller masking map that only applies to the rectangle inscribing the arrow's interior. That is, we don't need to zero the video memory under the arrow's perimeter because we will immediately display other pixels there. Example 9-17. #include #include #include void main(void); char arrow_white[] = { 0x00,0x0C,0x00, 0x00,0x0F,0x00, 0xFF,0xFC,0xC0, 0xC0,0x00,0x30, 0xFF,0xFC,0xC0, 0x00,0x0F,0x00, 0x00,0x0C,0x00 }; Chapter 9: Images and Image Management 169 char arrow_black[] = {6,1,9,2,2,9,1,19,7,2,8,1}; void main() { int old_mode; if (fg_testmode(4,1) == 0) { printf("This program requires a 320 "); printf("x 200 CGA graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(4); fg_setcolor(2); fg_rect(0,319,0,199); fg_move(10,10); fg_drawmask(arrow_black,12,10); fg_drwimage(arrow_white,3,7); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Retrieving Images Sometimes it is necessary to retrieve an image from video memory and store it in one or more bit map arrays. Fastgraph includes two routines, fg_getmap and fg_getimage, for this purpose. The fg_getmap routine retrieves pixels of the current color index and stores them in the mode-independent bit map format used by fg_drawmap. The fg_getimage routine retrieves an image and stores it in the mode-specific bit map format used by fg_drwimage, fg_clpimage, fg_revimage, and fg_flpimage. The arguments to fg_getmap and fg_getimage are respectively analogous to those of fg_drawmap and fg_drwimage: the first is an array (passed by reference) to receive the bit map, the second is the width of the bit map in bytes, and the last is the height of the bit map in pixel rows. With either routine, the graphics cursor position on the active video page defines the lower left corner of the image to retrieve. If we want to use the fg_getmap routine to retrieve an image containing more than one color, we must call the routine once per color. In this case we'll usually want to pass different bit map arrays to fg_getmap (or perhaps different offsets into the same array). This might seem unusual at first, but it parallels the behavior of the fg_drawmap routine. That is, to display a multicolor image using fg_drawmap, we must call it once for each color in the image. Example 9-18 demonstrates a typical use of the fg_getmap routine. The program displays the word "text" in the upper left corner of the screen using a 320 by 200 graphics mode. It uses fg_getmap to retrieve the word as an 170 Fastgraph User's Guide image and then displays it in a new position with the fg_drawmap routine. Let's look at the program now, and afterward we'll more closely examine the screen coordinates and the structure of the bit map array. Example 9-18. #include #include #include void main(void); void main() { char bitmap[32]; int old_mode, new_mode; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_setcolor(9); fg_text("text",4); fg_waitkey(); fg_move(0,7); fg_getmap(bitmap,4,8); fg_move(4,15); fg_drawmap(bitmap,4,8); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } In all 320 by 200 graphics video modes, individual characters are 8 pixels wide and 8 pixels high. This means the lower left corner of the (0,0) character cell is referenced by the screen coordinates (0,7). Hence, these are the coordinates of the first call to fg_move. The image retrieved in example 9-18 is four characters long (32 pixels wide), so we need a bit map array capable of holding 8 rows of 32 pixels (4 bytes) each. Our bit map array is therefore a 32-byte array, logically structured to have 4 columns and 8 rows. These values are the width and height arguments passed to fg_getmap and fg_drawmap. After it retrieves the image, example 9-18 displays it one line below and one-half character cell (four pixels) to the right of its original position. In other words, the program displays the image four pixels to the right of the (1,0) character cell. The lower left corner of that cell is referenced by the screen coordinates (0,15), so the image should appear at the position (4,15). These are the coordinates of the second call to fg_move. Chapter 9: Images and Image Management 171 Example 9-19 illustrates the use of the fg_getmap and fg_drawmap routines to retrieve and display two-color image. This example is similar to example 9-18, but this program first draws a rectangle in the upper left corner of the screen and then displays the word "text" on top of the rectangle in a different color. Each character in a 320 by 200 graphics video mode is 8 pixels wide and 8 pixels high, so the rectangle must be 32 pixels wide (4 characters times 8 pixels per character) and 8 pixels high. The image to retrieve will be the same size as the rectangle. The image retrieved in example 9-18 required a 32-byte array, logically structured to have 4 columns and 8 rows. Example 9-19 will retrieve an image of the same structure, but the image contains two colors instead of just one. This means we need two 32-byte arrays, one for each color, to hold the image. We could instead use a single 64-byte array and pass an offset into that array (specifically, &bitmap[32]) for processing the second color. Example 9-19. #include #include #include void main(void); void main() { char bitmap1[32], bitmap2[32]; int old_mode, new_mode; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_setcolor(7); fg_rect(0,31,0,7); fg_setcolor(9); fg_text("text",4); fg_waitkey(); fg_move(0,7); fg_setcolor(7); fg_getmap(bitmap1,4,8); fg_setcolor(9); fg_getmap(bitmap2,4,8); fg_move(4,15); fg_setcolor(7); fg_drawmap(bitmap1,4,8); fg_setcolor(9); fg_drawmap(bitmap2,4,8); fg_waitkey(); 172 Fastgraph User's Guide fg_setmode(old_mode); fg_reset(); } Example 9-20 is similar to example 9-19, but it uses fg_getimage and fg_drwimage instead of fg_getmap and fg_drawmap to retrieve and display the image. That is, it uses the mode-specific rather than the mode-independent image retrieval and display routines. When using the mode-specific routines, the size of the bit map needed to hold the image depends on the video mode. For programs that run in only one video mode, bit map widths are constant, but when a program must run in several video modes, the width is variable. The Fastgraph routine fg_imagesiz computes the number of bytes required to store a mode-specific bit-mapped image of specified dimensions. Its two integer arguments specify the image width and height in pixels. The program computes the image width in bytes by passing a height of 1 to fg_imagesiz. The size of the bit map array in example 9-20 is 256 bytes, the size required in the MCGA graphics mode (32 bytes times 8 bytes). The other video modes require less storage, so in these modes only a portion of the bit map array will actually be used. The image width is then used in the calls to both fg_getimage and fg_drwimage. Example 9-20. #include #include #include void main(void); void main() { char bitmap[256]; int old_mode, new_mode; int width; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); width = (int)fg_imagesiz(32,1); fg_setcolor(7); fg_rect(0,31,0,7); fg_setcolor(9); fg_text("text",4); fg_waitkey(); fg_move(0,7); fg_getimage(bitmap,width,8); fg_move(4,15); fg_drwimage(bitmap,width,8); fg_waitkey(); Chapter 9: Images and Image Management 173 fg_setmode(old_mode); fg_reset(); } While this example used an array to store the image, it's usually preferable to allocate dynamic memory for this purpose. We could have done this in example 9-20 by calling fg_imagesiz with arguments of 32 (width) and 8 (height). The routine's return value would then tell us the number of bytes we would need to allocate. We also can use the fg_getimage routine to retrieve images in text video modes. In text modes, however, there are a few differences we must consider when using fg_getimage. First, the text cursor position, not the graphics cursor position, specifies the lower left corner of the image. Hence, we must use the fg_locate routine instead of fg_move to define the image location. Second, the image width is always twice the number of characters per image row (that is, for each character we have a character byte and an attribute byte). The fg_getmap routine has no effect when used in a text video mode. Example 9-21 shows a simple use of fg_getimage in text modes. This program is similar to example 9-20, but it runs in an 80-column text mode rather than a 320 by 200 graphics mode. As before, the program will retrieve the four characters "text" as an image from the upper left corner of the screen and then display it in a different location. Because the image consists of four characters in one row, the image width is 8 bytes and the image height is 1. Example 9-21. #include #include #include void main(void); void main() { int old_mode; char image[8]; old_mode = fg_getmode(); if (fg_testmode(3,1)) fg_setmode(3); else if (fg_testmode(7,1)) fg_setmode(7); else { printf("This program requires\n"); printf("an 80-column display.\n"); exit(1); } fg_cursor(0); fg_setattr(9,7,0); fg_text("text",4); 174 Fastgraph User's Guide fg_waitkey(); fg_locate(0,0); fg_getimage(image,8,1); fg_locate(1,1); fg_drwimage(image,8,1); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Byte Boundaries Video memory, like standard random-access memory, is divided into units called bytes. In text modes, each byte holds either a character or an attribute. In graphics modes, each byte of video memory holds one or more horizontally contiguous pixels. If two adjacent horizontal pixels are stored in different bytes, then we say that a byte boundary exists between the two pixels. The number of pixels per byte depends on the video mode being used, so the byte boundaries also depend on the video mode. That is, a byte boundary in a CGA graphics mode is not necessarily a byte boundary in an EGA graphics mode. The following table summarizes the number of pixels per byte of video memory and the byte boundary sequences for each supported graphics video mode. Note that any horizontal coordinate whose value is a multiple of 8 is always a byte boundary, regardless of the video mode. mode pixels horizontal coordinates number per byte of byte boundaries 4 4 0, 4, 8, 12, ... , 316 5 4 0, 4, 8, 12, ... , 316 6 8 0, 8, 16, 24, ... , 632 9 2 0, 2, 4, 6, ... , 318 11 8 0, 8, 16, 24, ... , 712 12 4 0, 4, 8, 12, ... , 316 13 8 0, 8, 16, 24, ... , 312 14 8 0, 8, 16, 24, ... , 632 15 8 0, 8, 16, 24, ... , 632 16 8 0, 8, 16, 24, ... , 632 17 8 0, 8, 16, 24, ... , 632 18 8 0, 8, 16, 24, ... , 632 19 1 0, 1, 2, 3, ... , 319 20 1 0, 1, 2, 3, ... , 319 21 1 0, 1, 2, 3, ... , 319 22 1 0, 1, 2, 3, ... , 319 23 1 0, 1, 2, 3, ... , 319 Chapter 9: Images and Image Management 175 Image Transfer Routines The Fastgraph routines described in this section transfer images between areas of video memory. These routines are often used in animation sequences requiring high-performance graphics, so they must be as efficient as possible. To this end, Fastgraph will force their horizontal pixel coordinates to byte boundaries, which eliminates the need to process any pixels individually. Fastgraph accomplishes this by reducing minimum horizontal coordinates to a byte boundary and extending maximum horizontal coordinates to the last pixel in a video memory byte. Note that since we are talking about pixel coordinates and not character cells, the coordinate modification only occurs in graphics video modes. An example might best help explain this important feature. The CGA four-color graphics modes (modes 4 and 5) store four pixels in each byte of video memory. This means the byte boundaries occur at multiples of four pixels. Thus, when you use the image transfer routines in modes 4 and 5, Fastgraph reduces minimum x coordinates to the next lower multiple of four. Similarly, it extends their maximum x coordinates to the next higher multiple of four, less one pixel. That is, if a minimum x coordinate is 7 and a maximum x coordinate is 30, Fastgraph will modify these values to 4 and 31 respectively. If the x coordinates were originally 4 and 31, Fastgraph would leave them unchanged. Note, too, that because each pixel in the MCGA and VGA 256-color graphics modes (modes 19 through 23) occupies a separate byte of video memory, Fastgraph does not need to modify horizontal coordinates in these video modes. Fastgraph's simplest image transfer routine is fg_copypage, which we introduced in Chapter 8. The fg_copypage routine transfers the entire contents of one video page to another. The first argument is the number of the source video page, and the second argument is the number of the destination video page. The pages may be physical, virtual, or logical video pages. If both the source and destination pages are logical pages, the pages must exist in the same type of memory. For example, you cannot copy a logical page in extended memory to a logical page in conventional memory. Example 9-22 illustrates the use of the fg_copypage routine in a 320 x 200 color graphics mode. The program displays the word "test" in the middle of the visual page (page 0) and then uses fg_allocate to create a virtual video page (page 1). The virtual page is needed in case the program is running in a video mode with only one physical page. Next, the program uses 176 Fastgraph User's Guide fg_copypage to transfer the visual page contents to page 1. After waiting for a keystroke, the program erases the visual page, waits for another keystroke, and copies the contents of page 1 back to the visual page. It then releases the virtual page and exits. Example 9-22. #include #include #include void main(void); void main() { int new_mode, old_mode; new_mode = fg_bestmode(320,200,2); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_setcolor(7); fg_rect(0,319,0,199); fg_setcolor(9); fg_locate(12,18); fg_text("test",4); fg_waitkey(); fg_allocate(1); fg_copypage(0,1); fg_erase(); fg_waitkey(); fg_copypage(1,0); fg_waitkey(); fg_freepage(1); fg_setmode(old_mode); fg_reset(); } Several of Fastgraph's image transfer routines reference a video page called the hidden page. The Fastgraph routine fg_sethpage defines which video page will be used as the hidden page. This routine takes as its only argument an integer value specifying the hidden page number. If you are using a virtual video page for the hidden page, you must call the fg_sethpage routine after allocating that page. There is also a routine named fg_gethpage that returns the hidden page number, as specified in the most recent call to fg_sethpage, as its function value. The fg_gethpage routine takes no arguments. The next two image transfer routines we'll discuss are fg_save and fg_restore. The fg_save routine transfers a rectangular region from the Chapter 9: Images and Image Management 177 active video page (as defined in the most recent call to fg_setpage) to the same position on the hidden video page (as defined in the most recent call to fg_sethpage). The fg_restore routine performs the complementary task -- it transfers a rectangular region from the hidden page to the active page. Each of these routines requires four arguments that define the coordinates of the region to transfer, in the order minimum x, maximum x, minimum y, and maximum y. In text modes, the coordinates are expressed as character space quantities (rows and columns). In graphics modes, they are expressed as screen space values (pixels); the x coordinates are extended to byte boundaries if required. There are also world space versions of these routines named fg_savew and fg_restorew available in graphics modes. Example 9-23 demonstrates the use of Fastgraph's fg_save, fg_restore, and fg_sethpage routines in an 80-column text video mode. After establishing the video mode (note the calls to fg_testmode specify that two video pages are needed), the program fills the screen with text and then waits for a keystroke. Following this, the program displays a small pop-up window prompting for another keystroke. After waiting for the second keystroke, the program erases the pop-up window by restoring the original screen contents, and then waits for yet another keystroke before returning to DOS. We'll present the program now, and afterward analyze it in detail. Example 9-23. #include #include #include void main(void); void main() { int row; int old_mode; char string[17]; old_mode = fg_getmode(); if (fg_testmode(3,2)) fg_setmode(3); else if (fg_testmode(7,2)) fg_setmode(7); else { printf("This program requires\n"); printf("an 80-column display.\n"); exit(1); } fg_cursor(0); fg_setattr(9,7,0); for (row = 0; row < 25; row++) { sprintf(string," This is row %2d ",row); fg_locate(row,0); fg_text(string,16); fg_text(string,16); fg_text(string,16); fg_text(string,16); 178 Fastgraph User's Guide fg_text(string,16); } fg_waitkey(); fg_allocate(1); fg_sethpage(1); fg_save(32,47,11,13); fg_setcolor(1); fg_rect(32,47,11,13); fg_setattr(15,1,0); fg_locate(12,33); fg_text("Press any key.",14); fg_waitkey(); fg_restore(32,47,11,13); fg_waitkey(); fg_freepage(1); fg_setmode(old_mode); fg_reset(); } Example 9-23 first establishes the video mode and uses the fg_cursor routine to make the BIOS cursor invisible. It then executes a for loop that fills each row of the screen with the phrase "This is row n", where n is the row number (between 0 and 24). Next, the program uses the fg_allocate routine to create video page 1 as a virtual video page. This is needed in case the program is running in mode 7, which has only one true page (if the program is running in mode 3, the call to fg_allocate has no effect). The program then makes page 1 the hidden page by calling the fg_sethpage routine. After setting up the hidden video page, but before displaying the pop-up window, example 9-23 uses the fg_save routine to save the current contents of the area that the pop-up window will replace. The fg_save routine copies this region to the hidden page. The program then displays the pop-up window in the middle of the screen and leaves it there until a key is pressed. After this, the program uses the fg_restore routine to replace the pop-up window with the original contents of that region. This effectively erases the pop-up window and restores the original screen. The program then waits for another keystroke, after which it releases the virtual page and returns to DOS. The next example, 9-24, is similar to example 9-23, but it runs in a 320 by 200 color graphics mode instead of a text mode. The main differences between this program and example 9-23 are the use of 40-column text and the use of screen space coordinates instead of character space coordinates in the calls to fg_save, fg_restore, and fg_rect. Note that the call to fg_allocate creates a virtual page if the program is running in modes 4, 9, or 19. In mode 13, which has 8 true pages, the fg_allocate routine does nothing. Example 9-24. #include #include #include void main(void); Chapter 9: Images and Image Management 179 void main() { int row; int new_mode, old_mode; char string[21]; new_mode = fg_bestmode(320,200,2); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_setcolor(7); fg_rect(0,319,0,199); fg_setcolor(9); for (row = 0; row < 25; row++) { sprintf(string," This is row %2d ",row); fg_locate(row,0); fg_text(string,20); fg_text(string,20); } fg_waitkey(); fg_allocate(1); fg_sethpage(1); fg_save(96,223,88,111); fg_setcolor(1); fg_rect(96,223,88,111); fg_setcolor(15); fg_locate(12,13); fg_text("Press any key.",14); fg_waitkey(); fg_restore(96,223,88,111); fg_waitkey(); fg_freepage(1); fg_setmode(old_mode); fg_reset(); } The fg_save and fg_restore routines each copy a rectangular region from one video page to the same position on another video page. What if you need to copy the region to a different position on another video page, or copy it elsewhere on the same video page? Fastgraph provides a more general image transfer routine named fg_transfer. The fg_transfer routine copies a rectangular region on any video page to any position on any video page. Like fg_save and fg_restore, the fg_transfer routine works in text and graphics video modes. In graphics modes, fg_transfer extends its x coordinates to byte boundaries if necessary. The fg_transfer routine requires eight integer arguments. The first four arguments define the region to copy, in the same order as expected by the fg_save and fg_restore routines. The next two arguments define the lower 180 Fastgraph User's Guide left corner of the image destination, while the final two arguments respectively specify the source and destination video page numbers. In short, fg_transfer copies the specified region from the source page to the specified position on the destination page. Example 9-25 is the same as example 9-23, but it uses fg_transfer rather than fg_save and fg_restore. We have arbitrarily chosen to copy the region overwritten by the pop-up window to the lower left corner of the hidden page (page 1). When we copy this region back to the visual page, we copy from the lower left corner of the hidden page back to the original position on the visual page (page 0). This sequence is shown in the following diagram. (11,32) (11,47) (22,0) (22,15) first call This is row 11 ------------> This is row 11 This is row 12 This is row 12 This is row 13 <------------ This is row 13 second call (13,32) (13,47) (24,0) (24,15) visual page (0) hidden page (1) To copy one region to a new position and then back to its original position, note how we make the fifth and sixth arguments in the first call to fg_transfer the same values as the first and fourth arguments in the second call. Similarly, the fifth and sixth arguments in the second call must be the same as the first and fourth arguments in the first call. Now, here is example 9-25. Example 9-25. #include #include #include void main(void); void main() { int row; int old_mode; char string[17]; old_mode = fg_getmode(); if (fg_testmode(3,2)) fg_setmode(3); else if (fg_testmode(7,2)) fg_setmode(7); else { printf("This program requires\n"); printf("an 80-column display.\n"); exit(1); } fg_cursor(0); fg_setattr(9,7,0); Chapter 9: Images and Image Management 181 for (row = 0; row < 25; row++) { sprintf(string," This is row %2d ",row); fg_locate(row,0); fg_text(string,16); fg_text(string,16); fg_text(string,16); fg_text(string,16); fg_text(string,16); } fg_waitkey(); fg_allocate(1); fg_transfer(32,47,11,13,0,24,0,1); fg_setcolor(1); fg_rect(32,47,11,13); fg_setattr(15,1,0); fg_locate(12,33); fg_text("Press any key.",14); fg_waitkey(); fg_transfer(0,15,22,24,32,13,1,0); fg_fg_waitkey(); fg_freepage(1); fg_setmode(old_mode); fg_reset(); } Example 9-26 illustrates another use of the fg_transfer routine. This example is functionally identical to example 9-18 (see page 170), but it uses fg_transfer instead of fg_getmap and fg_drawmap. With the fg_transfer routine, we eliminate the calls to fg_getmap and fg_drawmap, the two calls to fg_move, and the 32-byte array needed to retrieve the image. As an added bonus, using fg_transfer is much faster than the technique of example 9-18, although we probably won't notice this gain with such a small image. The image copied in example 9-26 is one row of four characters, so its width in screen space is 32 pixels and its height is 8 pixels. Because the image is in the upper left corner of the screen, the image boundaries are xmin=0, xmax=31, ymin=0, and ymax=7. We want to move the image one-half character cell (4 pixels) to the right and one row (8 pixels) down, so our destination coordinates are x=4 (xmin+4) and y=15 (ymax+8). Also, we are copying the image from one position to another on the visual page, so both the source and destination pages are 0. Example 9-26. #include #include #include void main(void); void main() { int old_mode, new_mode; 182 Fastgraph User's Guide new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_setcolor(9); fg_text("text",4); fg_waitkey(); fg_transfer(0,31,0,7,4,15,0,0); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Example 9-27 shows yet another application of the fg_transfer routine in a graphics video mode. The program displays a rectangle in the upper left quadrant of the screen and then centers the word "quadrant" inside the rectangle. After waiting for a keystroke, the program uses fg_transfer to first copy the upper left quadrant to the upper right quadrant. It then uses fg_transfer again to copy the upper half of the screen to the lower half. The result of this is the screen being filled with what was originally in the upper left quadrant. Example 9-27. #include #include #include void main(void); void main() { int new_mode, old_mode; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_setcolor(7); fg_rect(0,159,0,99); fg_setcolor(9); fg_locate(6,6); fg_text("quadrant",8); fg_waitkey(); Chapter 9: Images and Image Management 183 fg_transfer(0,159,0,99,160, 99,0,0); fg_transfer(0,319,0,99, 0,199,0,0); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } The final routines pertaining to image transfer are fg_tcxfer and fg_tcmask. The fg_tcxfer routine is similar to fg_transfer in that it copies a rectangular region from one position to another, but fg_tcxfer allows you to treat one or more colors as transparent (the name fg_tcxfer stands for "transparent color transfer"). In other words, any pixel whose color value is defined to be transparent is not copied to the destination area. The fg_tcxfer routine's arguments are the same as for the fg_transfer routine, but fg_tcxfer has no effect in text video modes. Because fg_tcxfer must examine the color of individual pixels, it is not as fast as the fg_transfer routine. The fg_tcmask routine defines which colors are considered transparent in subsequent calls to fg_tcxfer. Its argument is an integer bit mask (specifically, a 16-bit mask) where each bit indicates whether or not the color is transparent. For example, if bit 0 (the rightmost bit) is set in the mask, then color 0 will be transparent; if bit 0 is reset, color 0 will not be transparent. Because the bit mask size is 16 bits, only the first 16 color values may be defined as transparent. Example 9-28 illustrates the use of the fg_tcxfer and fg_tcmask routines. This program is the same as example 9-27, except the color of the word "quadrant" (color 9) is defined to be transparent, and fg_tcxfer is used in place of fg_transfer. Because color 9 maps to color 1 in the CGA four-color graphics mode (mode 4), we must define both colors 1 and 9 to be transparent (remember, fg_tcmask considers actual color values transparent, not color indices). The bit mask passed to fg_tcmask thus will be 0000 0010 0000 0010 binary, or 0202 hex. The result of this program is the same as example 9-27, but the word "quadrant" appears in the background color (color 0) instead of color 9 in the upper right, lower left, and lower right quadrants. Example 9-28. #include #include #include void main(void); void main() { int new_mode, old_mode; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); 184 Fastgraph User's Guide fg_setcolor(7); fg_rect(0,159,0,99); fg_setcolor(9); fg_locate(6,6); fg_text("quadrant",8); fg_waitkey(); fg_tcmask(0x0202); fg_tcxfer(0,159,0,99,160, 99,0,0); fg_tcxfer(0,319,0,99, 0,199,0,0); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Summary of Image Display Routines This section summarizes the functional descriptions of the Fastgraph routines presented in this chapter. More detailed information about these routines, including their arguments and return values, may be found in the Fastgraph Reference Manual. For all image display routines, images are positioned so their lower left corner is at the graphics cursor position (or text cursor position for those routines that also work in text video modes). For all image transfer routines, Fastgraph extends the horizontal pixel coordinates to a byte boundary when the routines are used in a graphics video mode. FG_CLIPMASK displays a clipped image stored as a masking map. This routine has no effect when used in a text video mode. FG_CLPIMAGE displays a clipped image stored as a mode-specific bit map. This routine has no effect when used in a text video mode. FG_COPYPAGE transfers the contents of one video page to another. The pages may be physical, virtual, or logical video pages. If both pages are logical pages, they must exist in the same type of memory. FG_DISPFILE displays an image stored in Fastgraph's standard or packed pixel run format, where the image resides in an external file. This routine has no effect when used in a text video mode. FG_DISPLAY displays an image stored in Fastgraph's standard pixel run format, where the image resides in an array. This routine has no effect when used in a text video mode. FG_DISPLAYP displays an image stored in Fastgraph's packed pixel run format, where the image resides in an array. This routine has no effect when used in a text video mode. FG_DISPPCX displays an image stored in a PCX file. The image will be positioned so its upper left corner is at the graphics cursor position of the active video page. Chapter 9: Images and Image Management 185 FG_DRAWMAP displays an image stored as a mode-independent bit map. This routine has no effect when used in a text video mode. FG_DRAWMASK displays an image stored as a masking map. This routine has no effect when used in a text video mode. FG_DRWIMAGE displays an image stored as a mode-specific bit map. FG_FLIPMASK displays a reversed clipped image stored as a masking map. This routine has no effect when used in a text video mode. FG_FLPIMAGE displays a reversed clipped image stored as a mode-specific bit map. This routine has no effect when used in a text video mode. FG_GETHPAGE returns the hidden page number, as defined in the most recent call to fg_sethpage. FG_GETIMAGE retrieves an image as a mode-specific bit map. FG_GETMAP retrieves an image as a mode-independent bit map. This routine has no effect when used in a text video mode. FG_IMAGESIZ determines the number of bytes required to store a mode- specific bit-mapped image of specified dimensions. FG_MAKEPCX creates a PCX file from the specified rectangular region of the active video page. The region's extremes are expressed in screen space units. FG_PATTERN defines a display pattern for use with the fg_dispfile, fg_display, or fg_displayp routines. This routine has no effect when used in a text video mode. FG_RESTORE copies an image from the hidden video page to the same position on the active video page. FG_RESTOREW is the same as fg_restore, but the image extremes are specified as world space coordinates. FG_REVIMAGE displays a reversed image stored as a mode-specific bit map. This routine has no effect when used in a text video mode. FG_REVMASK displays a reversed image stored as a masking map. This routine has no effect when used in a text video mode. FG_SAVE copies an image from the active video page to the same position on the hidden video page. FG_SAVEW is the same as fg_save, but the image extremes are specified as world space coordinates. FG_SETHPAGE defines the hidden video page (used by fg_restore, fg_restorew, fg_save, and fg_savew). FG_TCMASK defines which colors the fg_tcxfer routine will consider transparent. This routine has no effect when used in a text video mode. 186 Fastgraph User's Guide FG_TCXFER copies an image from any position on any video page to any position on any video page, excluding any pixels whose color value is transparent. This routine has no effect when used in a text video mode. FG_TRANSFER copies an image from any position on any video page to any position on any video page. It is Fastgraph's most general image transfer routine. Chapter 10 Animation Techniques 188 Fastgraph User's Guide Overview Unlike other microcomputers, the IBM PC and PS/2 family of systems do not have any special graphics hardware or firmware to help in performing animation. This means that any animation done on these systems must be implemented entirely through software. This chapter will describe how to do this using Fastgraph's video page management and image management routines. The methods described in this chapter are not intended to be all inclusive, for that would itself fill a separate volume at least as large as this manual. However, the animation techniques presented here should provide a basis that you can readily extend to develop more sophisticated uses of animation. The examples in this chapter are restricted to graphics video modes. Simple Animation The first type of animation we'll examine is called simple animation. In simple animation, we display an object, erase it, and then display it in a new position. When we perform this "erase and redisplay" sequence repetitively, the object moves. This method, however, has two drawbacks. First, unless the object is rather small, it will flicker because the erasing and display of the object does not coincide with the refresh rate of the video display. Second, and perhaps more importantly, anything underneath the object is not saved as the object moves across it. Despite these limitations, simple animation is sometimes useful, and it is a good place to begin our discussion of animation techniques. Example 10-1 moves a small bright green rectangle (magenta in CGA) from left to right across the screen in any 320 by 200 color graphics mode. The program moves the rectangle, 20 pixels wide and 10 pixels high, using a for loop. This loop first uses the fg_clprect routine to display the rectangle, then uses the fg_waitfor routine to leave the object on the screen momentarily, and finally uses fg_clprect again to erase the rectangle by redisplaying it in the original background color (the fg_waitfor routine is described in Chapter 14). We use fg_clprect rather than fg_rect because the first few and last few loop iterations result in at least part of the rectangle being off the screen. Each successive loop iteration displays the rectangle five pixels to the right of its previous position. Example 10-1. #include #include #include void main(void); void main() { int new_mode, old_mode; int x; /* initialize the video environment */ new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { Chapter 10: Animation Techniques 189 printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); /* move the object across the screen */ for (x = -20; x < 320; x+=5) { fg_setcolor(10); fg_clprect(x,x+19,95,104); fg_waitfor(1); fg_setcolor(0); fg_clprect(x,x+19,95,104); } /* restore the original video mode and return to DOS */ fg_setmode(old_mode); fg_reset(); } Example 10-2 is the same as example 10-1, but it shows what happens when we move the rectangle across an existing background (in this case, the background is solid white). If you run this program, you'll see that the rectangle leaves a trail of color 0 behind it. While this might be occasionally useful, it demonstrates that simple animation is destructive because it does not preserve the background. In this example, if we changed the second call to fg_setcolor within the for loop so revert to color 15 instead of color 0, the background would be restored. In general, though, it may not be this easy to replace the background, so we must rely on some other method for preserving it. Example 10-2. #include #include #include void main(void); void main() { int new_mode, old_mode; int x; /* initialize the video environment */ new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); 190 Fastgraph User's Guide /* draw some type of background */ fg_setcolor(15); fg_rect(0,319,0,199); /* move the object across the screen */ for (x = -20; x < 320; x+=5) { fg_setcolor(10); fg_clprect(x,x+19,95,104); fg_waitfor(1); fg_setcolor(0); fg_clprect(x,x+19,95,104); } /* restore the original video mode and return to DOS */ fg_setmode(old_mode); fg_reset(); } To summarize, we see that simple animation is easy to implement, but it is destructive and typically causes the animated object to flicker. For these reasons, it is not used too frequently. XOR Animation "Exclusive or" animation, or XOR animation for short, is an interesting extension of simple animation and is most useful when animating a single- color object against a single-color background. Like simple animation, it uses the "erase and redisplay" technique to move an object, but it does this differently. Instead of erasing the object by displaying it in the background color, XOR animation does so by displaying it in the same color using an exclusive or, or XOR, operation. This method relies on a specific property of the exclusive or operator: (object XOR background) XOR object = background In other words, if you XOR something twice in the same position, the result is the same as the original image in that position. Example 10-3 demonstrates XOR animation. This program is similar to example 10-2, but it only runs in the 320 by 200 EGA graphics mode (mode 13). After establishing the video mode, it uses the Fastgraph routine fg_setfunc to select XOR mode. This causes any subsequent graphics output to be XORed with the contents of video memory instead of just replacing it. The fg_setfunc routine is described further in Chapter 15. The other differences between examples 10-3 and 10-2 are that the call to fg_setcolor has been moved outside the for loop, and that fg_setcolor takes a different value. Since the existing background is bright white (color 15), we can't just use color 10 if we want to display a bright green object. The desired value is that which when XORed with color 15 produces color 10; the easiest way to obtain this value is to XOR these two numbers. The call to fg_setcolor can be moved outside the loop because we display the object using the same color index throughout. Chapter 10: Animation Techniques 191 Example 10-3. #include #include #include void main(void); void main() { int old_mode; int x; /* initialize the video environment */ if (fg_testmode(13,1) == 0) { printf("This program requires EGA.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(13); fg_setfunc(3); /* draw some type of background */ fg_setcolor(15); fg_rect(0,319,0,199); /* move the object across the screen */ fg_setcolor(10^15); for (x = -20; x < 320; x+=5) { fg_clprect(x,x+19,95,104); fg_waitfor(1); fg_clprect(x,x+19,95,104); } /* restore the original video mode and return to DOS */ fg_setmode(old_mode); fg_reset(); } Fastgraph only supports the XOR pixel operation in the native EGA and VGA graphics video modes (modes 13 through 18). Thus, you cannot use XOR animation in CGA, Tandy/PCjr, Hercules, or MCGA graphics modes. While XOR animation is non-destructive (that is, it restores the original background), it still suffers from the flickering encountered in simple animation. In spite of this, it may be useful when animating a single-color object against a single-color background. 192 Fastgraph User's Guide Static Frame Animation Static frame animation uses a different strategy than simple animation or XOR animation. The general scheme of this method is to create the entire animation sequence off-screen and then successively display each item, or frame, in this sequence on one position of the visual video page. This results in a visually appealing animation that is non-destructive and does not include the flickering associated with simple animation and XOR animation. Static frame animation requires the visual video page and one or more additional pages to implement. The number of pages needed depends on the number of frames and the size of each frame. Example 10-4 runs in any 320 by 200 color graphics video mode and illustrates a simple use of static frame animation. The program displays an animation sequence containing 12 frames; it displays this sequence three times. The animation sequence consists of a bright green rectangle (magenta in CGA) moving from left to right across the center of the frame. Each frame is 96 pixels wide and 50 pixels high. The 12 frames are set up on an off- screen video page as shown below. 0 95 96 191 192 287 0 frame 1 frame 2 frame 3 49 50 frame 4 frame 5 frame 6 99 100 frame 7 frame 8 frame 9 149 150 frame 10 frame 11 frame 12 199 Example 10-4 first establishes the video mode and allocates the additional video page (needed if using a video mode in which page 1 is a virtual video page). The program then generates the background for frame 1; the background is a blue rectangle (cyan in CGA) with a white ellipse centered on it. After the call to fg_ellipse, the first frame is ready. The next step is to create the remaining 11 frames. In frame 2, the right half of the 20-pixel wide rectangle will enter the left edge of the frame. In frame 3, the rectangle will be ten pixels farther right, or aligned against the left edge of the frame. In frames 4 through 12, the rectangle will be ten pixels farther right in each frame, so by frame 12 only the left half of the rectangle appears on the right edge of the frame. The first for loop in the program builds frames 2 through 12 by copying the background from frame 1 and then displaying the rectangle (that is, the animated object) in the proper position for that frame. The second for loop performs the animation sequence. To display the 12- frame sequence three times, it must perform 36 iterations. The loop simply Chapter 10: Animation Techniques 193 copies each frame from the proper position on video page 1 to the middle of the visual video page. Note how the fg_waitfor routine is used to pause momentarily between each frame. Example 10-4. #include #include #include void main(void); #define VISUAL 0 #define HIDDEN 1 int xmin[] = { 0, 96,192, 0, 96,192, 0, 96,192, 0, 96,192}; int ymax[] = { 49, 49, 49, 99, 99, 99,149,149,149,199,199,199}; void main() { int new_mode, old_mode; int frame, offset; int i, x, y; /* initialize the video environment */ new_mode = fg_bestmode(320,200,2); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_allocate(HIDDEN); /* draw the background in the upper left corner */ fg_setpage(HIDDEN); fg_setcolor(1); fg_rect(0,95,0,49); fg_setcolor(15); fg_move(48,25); fg_ellipse(20,20); /* display the animated object against each background */ fg_setcolor(10); offset = -10; for (i = 1; i < 12; i++) { x = xmin[i]; y = ymax[i]; fg_transfer(0,95,0,49,x,y,HIDDEN,HIDDEN); fg_setclip(x,x+95,0,199); fg_clprect(x+offset,x+offset+19,y-29,y-20); offset += 10; } 194 Fastgraph User's Guide /* slide the object across the background three times */ for (i = 0; i < 36; i++) { frame = i % 12; x = xmin[frame]; y = ymax[frame]; fg_transfer(x,x+95,y-49,y,112,124,HIDDEN,VISUAL); fg_waitfor(2); } /* restore the original video mode and return to DOS */ fg_freepage(HIDDEN); fg_setmode(old_mode); fg_reset(); } Dynamic Frame Animation Dynamic frame animation is similar to static frame animation, but all the animation frames are built as needed during the animation sequence instead of in advance. When using this method, you must first store a copy of the background on an off-screen video page. Then, to build a frame, create another copy (called the workspace) of the background elsewhere on the off-screen page (or even to a different off-screen page) and display the object on that copy. Finally, transfer the workspace to the visual page. Like static frame animation, this method produces a non-destructive, flicker- free animation sequence. Example 10-5 is functionally identical to example 10-4, but it uses dynamic rather than static frame animation. As before, the program builds the background in the upper left corner of video page 1, but it then uses fg_transfer to copy it to the center of the visual video page. The for loop builds each frame as it is needed and also copies it to the center of the visual page. Again, fg_waitfor creates the necessary pause between frames. Example 10-5. #include #include #include void main(void); #define VISUAL 0 #define HIDDEN 1 void main() { int new_mode, old_mode; int frame, offset; int i; /* initialize the video environment */ new_mode = fg_bestmode(320,200,2); if (new_mode < 0 || new_mode == 12) { Chapter 10: Animation Techniques 195 printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_allocate(HIDDEN); /* draw the background in the upper left corner */ fg_setpage(HIDDEN); fg_setcolor(1); fg_rect(0,95,0,49); fg_setcolor(15); fg_move(48,25); fg_ellipse(20,20); /* copy it to the center of the visual page */ fg_transfer(0,95,0,49,112,124,HIDDEN,VISUAL); /* slide the object across the background three times */ fg_setcolor(10); for (i = 0; i < 36; i++) { frame = i % 12; offset = 10 * frame - 10; fg_transfer(0,95,20,29,112,105,HIDDEN,HIDDEN); fg_rect(112+offset,131+offset,96,105); fg_transfer(112,207,96,105,112,105,HIDDEN,VISUAL); fg_waitfor(2); } /* restore the original video mode and return to DOS */ fg_freepage(HIDDEN); fg_setmode(old_mode); fg_reset(); } Two items in example 10-5 merit further discussion. First, we have chosen our workspace on page 1 so it uses the same screen space coordinates as the image area on the visual page. This is not necessary unless you are using the fg_restore routine instead of fg_transfer. Second, the program can use the faster fg_rect routine in place of fg_clprect. It can do this because even though the object will extend beyond the workspace limits, we only transfer the workspace itself. However, for this to function properly, the workspace's horizontal limits must fall on byte boundaries. Note too that we do not need to transfer the entire frame during the animation sequence. In example 10-5, we know the vertical extremes of the moving image are y=96 and y=105, so we only transfer 10 rows instead of the entire frame. We could similarly compute the x extremes for each frame and only transfer the necessary portion. Recall, however, that fg_transfer extends the horizontal coordinates to byte boundaries, so we may copy a few extra pixels as well. This may or may not affect the animation sequence. 196 Fastgraph User's Guide Again, the problem is eliminated if you align your workspace on byte boundaries. When we use dynamic frame animation, it is easy to change the number of frames in the animation sequence. Suppose we wish to produce a smoother animation by increasing the number of frames from 12 to 24. This means the object will move in increments of five pixels instead of ten. The only changes needed are to double the number of loop iterations, modify the calculations for the frame number and offset values as shown below, and reduce the fg_waitfor pause from 2 to 1. frame = i % 24; offset = 5 * frame - 10; Compare this to all the changes that would be necessary if we were using static frame animation. Page Flipping Page flipping is a variation of frame animation in which you construct images on off-screen video pages and then repetitively make those pages the visual page. We can further divide the page flipping technique into static and dynamic variants, as we did with frame animation. In static page flipping, we construct the entire animation sequence in advance, with one frame per video page. Once this is done, we can display each frame by using the fg_setvpage routine to switch instantly from one video page to another. Although this produces a smooth, flicker-free animation, we cannot carry the sequence very far before running out of video pages (and hence animation frames). In dynamic page flipping, we construct each animation frame when it is needed. As in static page flipping, we construct each frame on a separate video page. However, as example 10-6 demonstrates, we only need three video pages to produce the animation sequence, regardless of the number of frames in the sequence. Two of the three video pages will alternate as the visual page, while the remaining video page keeps a copy of the background. Example 10-6, which performs an animation sequence similar to examples 10-4 and 10-5, illustrates dynamic frame animation in the 320 by 200 EGA graphics video mode (mode 13). The program begins by displaying the background on video page 2. Video pages 0 and 1 will alternate as the visual page; the page that is not the visual page is called the hidden page. We start with page 0 as the visual page, and hence page 1 as the hidden page. To build each frame, the program uses fg_transfer to copy the background from page 2 to the hidden page and then uses fg_clprect to display the animated object at the correct position on the hidden page. After this, it displays the next frame by using fg_setvpage to make the hidden page the visual page. Before beginning the next iteration, the program toggles the hidden page number in preparation for the next frame. Example 10-6. #include #include Chapter 10: Animation Techniques 197 #include void main(void); void main() { int old_mode; int hidden; int x; /* initialize the video environment */ if (testmode(fg_13,3) == 0) { printf("This program requires EGA.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(13); /* draw the background on page two */ fg_setpage(2); fg_setcolor(1); fg_rect(0,319,0,199); fg_setcolor(15); fg_move(160,100); fg_ellipse(20,20); /* slide the object across the screen */ hidden = 1; setcolor(10); for (x = -10; x < 320; x+=4) { fg_setpage(hidden); fg_transfer(0,319,0,199,0,199,2,hidden); fg_clprect(x,x+19,96,105); fg_setvpage(hidden); hidden = 1 - hidden; fg_waitfor(1); } /* restore the original video mode and return to DOS */ fg_setmode(old_mode); fg_reset(); } A problem with either page flipping technique arises if we use virtual video pages. Page flipping relies on the fact that changing the visual page number occurs instantly, which is exactly what happens when we use physical video pages. However, such is not the case with virtual or logical pages because Fastgraph must copy the entire page contents into video memory. While this occurs quite rapidly, it is not instantaneous, and its effects are immediately apparent on the animation. 198 Fastgraph User's Guide Summary of Animation Techniques This chapter has presented five animation techniques: simple animation, XOR animation, static frame animation, dynamic frame animation, and page flipping. The following table summarizes their behavior. technique destructive? flicker-free? simple yes no XOR no no static frame no yes dynamic frame no yes page flipping no yes Simple animation and XOR animation are elementary techniques that are seldom used once you master frame animation and page flipping. As stated at the beginning of this chapter, the simple examples presented here serve as the basis for understanding the mechanics of the animation techniques we have discussed. In "real world" programs, you'll typically want to display an image using the fg_drwimage or fg_drawmap routines instead using rudimentary images such as the rectangles in our examples. A helpful rule is to use pixel run maps (displayed by fg_dispfile, fg_display, or fg_displayp) for both backgrounds and moving objects, and then use fg_getimage or fg_getmap to retrieve the moving objects as bit-mapped images for later display. Of course, it is desirable to do this "behind the scenes" work on video pages other than the visual page. Chapter 11 Special Effects 200 Fastgraph User's Guide Overview This chapter will discuss the Fastgraph routines that help produce special visual effects. These include the ability to dissolve the screen contents in small increments, scroll areas of the screen, and change the physical origin of the screen. The accompanying example programs illustrate how to use these routines to produce some interesting effects. Screen Dissolving Screen dissolving is the process of replacing the entire screen contents in random small increments instead of all at once. Fastgraph includes two routines, fg_fadeout and fg_fadein, for this purpose. The fg_fadeout routine incrementally replaces the visual page contents with pixels of the current color, while fg_fadein incrementally replaces the visual page contents with the hidden page contents (that is, the page defined in the most recent call to fg_sethpage). Both routines accept an integer argument that defines the delay between each incremental replacement. A value of zero means to perform the replacement as quickly as possible, while 1 is slightly slower, 2 is slower yet, and so forth. The fg_fadeout and fg_fadein routines have no effect in text video modes. Example 11-1 shows how to use the fg_fadeout routine. The program, which runs in any graphics video mode, first fills the screen with a rectangle of color 2. After waiting for a keystroke, the program incrementally replaces the screen contents with pixels of color 15 (the current color index when fg_fadeout is called). After another keystroke, the program exits gracefully. Example 11-1. #include void main(void); void main() { int old_mode; old_mode = fg_getmode(); fg_setmode(fg_automode()); fg_setcolor(2); fg_rect(0,fg_getmaxx(),0,fg_getmaxy()); fg_waitkey(); fg_setcolor(15); fg_fadeout(0); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Example 11-2 shows how to use the fg_fadein routine in any 320 by 200 color graphics video mode. The program first fills the screen with a Chapter 11: Special Effects 201 rectangle of color 2 and then fills video page 1 with a rectangle of color 1. After waiting for a keystroke, the program incrementally transfers the contents of page 1 to the visual page. After the call to fg_fadein, both page 0 (the visual page) and page 1 (the hidden page) will contain rectangles of color 1 that fill the entire video page. Finally, the program waits for another keystroke before returning to DOS. Example 11-2. #include #include #include void main(void); void main() { int new_mode, old_mode; new_mode = fg_bestmode(320,200,2); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_allocate(1); fg_sethpage(1); fg_setcolor(2); fg_rect(0,319,0,199); fg_setpage(1); fg_setcolor(1); fg_rect(0,319,0,199); fg_waitkey(); fg_fadein(0); fg_waitkey(); fg_freepage(1); fg_setmode(old_mode); fg_reset(); } You also can produce some appealing visual effects by replacing the screen contents in a non-random fashion using the fg_restore or fg_transfer routines. For example, you could copy the hidden page contents to the visual page with a through a series of concentric rectangular areas, each slightly larger than the previous, until the entire screen is copied. Another interesting effect is to start around the screen perimeter and proceed toward the screen center, thus producing a "snake-like" effect. Experimenting with such techniques may reveal other effects that suit your application. 202 Fastgraph User's Guide Scrolling Another useful effect is scrolling, and Fastgraph provides a routine that performs vertical scrolling within a given region of the active video page. The fg_scroll routine scrolls a region defined in screen space or character space. It can scroll up or down and offers two types of scrolling: circular and end-off. In circular scrolling, rows that scroll off one edge of the defined region appear at its opposite edge. In end-off scrolling, such rows are simply wind up above or below the scrolling area. The following diagrams illustrate the two types of scrolling. end-off scrolling circular scrolling before after before after C B A A A A B B In these diagrams, the area bounded by the double lines is the scrolling region, as specified in the call to fg_scroll. Also, the scrolling direction is assumed to be down (that is, toward the bottom of the screen), and the number of rows to scroll is the height of the area designated B. The number of rows to scroll is often called the scrolling increment. For the end-off scrolling example, the scrolling operation transfers region A downward so part of it is copied into area B. The area C (which is the same size as area B) at the top of the scrolling region is filled with pixels of the current color index (as defined in the most recent call to fg_setcolor), and the original contents of area B are lost. The circular scrolling example also copies region A downward into the original area B. Unlike end-off scrolling, however, circular scrolling preserves the area B by copying it to the opposite edge of the scrolling region. The fg_scroll routine takes six arguments. The first four define the scrolling region in the order minimum x coordinate, maximum x coordinate, minimum y coordinate, and maximum y coordinate. In graphics video modes, the x coordinates are extended by byte boundaries (see page 174) if needed. The fifth argument is the scrolling increment. It specifies the number of rows to scroll. If it is positive, the scrolling direction is toward the bottom of the screen; if it is negative, the scrolling direction is toward the top of the screen. The sixth and final argument specifies the scroll type. If this value is zero, the scroll will be circular; if it is any other value, the scroll will be end-off. If the scroll type is circular, Fastgraph will use the hidden page (as defined in the most recent call to fg_sethpage) as a workspace (more specifically, the area bounded by the scrolling region extremes on the hidden page will be used). We'll now present three example programs that use the fg_scroll routine. Example 11-3 runs in any 320 by 200 graphics video mode. The program displays two lines of text ("line one" and "line two") in the upper left corner of the screen against a white background. It then uses the fg_scroll routine to move the second line down four pixel rows using an end-off scroll. After waiting for a keystroke, the program again uses fg_scroll to move the Chapter 11: Special Effects 203 text back to its original position. Note especially how the fg_setcolor routine appears before the first call to fg_scroll to replace the "scrolled off" rows with pixels of color 15, thus preserving the white background. Example 11-3. #include #include #include void main(void); void main() { int new_mode, old_mode; new_mode = fg_bestmode(320,200,1); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); fg_setcolor(15); fg_rect(0,319,0,199); fg_setcolor(10); fg_text("line one",8); fg_locate(1,0); fg_text("line two",8); fg_waitkey(); fg_setcolor(15); fg_scroll(0,63,8,15,4,1); fg_waitkey(); fg_scroll(0,63,12,19,-4,1); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Example 11-4 is similar to example 11-3, but it runs in the 80-column color text mode (mode 3). In text modes, we cannot scroll half a character row (four pixels) as in example 11-3, so the program scrolls the minimum one row instead. Example 11-4. #include void main(void); void main() { int old_mode; 204 Fastgraph User's Guide old_mode = fg_getmode(); fg_setmode(3); fg_cursor(0); fg_setcolor(7); fg_rect(0,79,0,24); fg_setattr(10,7,0); fg_text("line one",8); fg_locate(1,0); fg_text("line two",8); fg_waitkey(); fg_setcolor(7); fg_scroll(0,7,1,1,1,1); fg_waitkey(); fg_scroll(0,7,2,2,-1,1); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Example 11-5, the final scrolling example, demonstrates a circular scroll. The program runs in any 320 by 200 color graphics video mode; note the use of video page 1 for the workspace required when the fg_scroll routine performs a circular scroll. The program first fills the screen with a light blue rectangle (cyan in CGA), displays a smaller white rectangle in the center of the screen, and then uses fg_move, fg_draw, and fg_paint to display a light green star (magenta in CGA) within the white rectangle. The program executes a while loop to scroll the star upward in four pixel increments. Because the scroll is circular, rows of the star that "scroll off" the top edge of the white rectangle (whose height is the same as the scrolling region) reappear at its bottom edge. The use of fg_waitfor within the loop simply slows down the scroll. The scrolling continues until any key is pressed. Example 11-5. #include #include #include #include void main(void); void main() { int new_mode, old_mode; new_mode = fg_bestmode(320,200,2); if (new_mode < 0 || new_mode == 12) { printf("This program requires a 320 "); printf("x 200 color graphics mode.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(new_mode); Chapter 11: Special Effects 205 fg_allocate(1); fg_sethpage(1); fg_setcolor(9); fg_rect(0,319,0,199); fg_setcolor(15); fg_rect(132,188,50,150); fg_setcolor(10); fg_move(160,67); fg_draw(175,107); fg_draw(140,82); fg_draw(180,82); fg_draw(145,107); fg_draw(160,67); fg_paint(160,77); fg_paint(150,87); fg_paint(160,87); fg_paint(170,87); fg_paint(155,97); fg_paint(165,97); while (kbhit() == 0) { fg_waitfor(1); fg_scroll(136,184,50,150,-4,0); } fg_waitkey(); fg_freepage(1); fg_setmode(old_mode); fg_reset(); } Changing the Screen Origin Fastgraph includes two routines for changing the screen origin. By changing the screen origin, we simply mean defining the (x,y) coordinate of the upper left corner of the display area. The fg_pan routine performs this function in screen space, while the fg_panw routine does in world space. Neither routine changes the graphics cursor position. Each of these routines has two arguments that specify the x and y coordinates of the screen origin. For the fg_pan routine, the arguments are integer quantities. For the fg_panw routine, they are floating point quantities. In the native EGA and VGA graphics modes (modes 13 through 18), you can set the screen origin to any (x,y) coordinate position (that is, to any pixel). In the other graphics modes, certain restrictions exist, as imposed by specific video hardware. These constraints limit the coordinate positions that can be used as the screen origin. Fastgraph compensates for these restrictions by reducing the specified x and y coordinates to values that are acceptable to the current video mode, as shown in the following table. 206 Fastgraph User's Guide x will be reduced y will be reduced video mode to a multiple of: to a multiple of: 4, 5 8 2 6 16 2 9 4 4 11 8 4 12 4 2 or 3 19 to 23 4 1 For example, in modes 4 and 5, the x coordinate will be reduced to a multiple of 8 pixels, and the y coordinate will be reduced to a multiple of 2 pixels. In the Hercules low resolution mode (mode 12), the y coordinate reduction depends on whether or not the specified pixel row is scan doubled. Example 11-6 shows a useful effect that can be made with the fg_pan or fg_panw routines. This program uses the fg_automode routine to select a video mode and then draws an unfilled rectangle in color 15 (bright white). The top and bottom sides of the rectangle are intentionally drawn just smaller than the physical screen size. After waiting for a keystroke, the program uses a for loop to make the rectangle jiggle up and down. The rectangle moves because the fg_pan routine is called inside the loop to switch the screen origin between the rectangle's upper left corner and the original origin. Note also the use of the fg_waitfor routine to cause slight delays after each call to fg_pan. If we didn't use fg_waitfor, the changing of the origin would occur so rapidly we wouldn't notice the effect. Finally, the program restores the original video mode and screen attributes before returning to DOS. Example 11-6. #include #include #include void main(void); #define DELAY 2 #define JUMP 4 void main() { int i; int old_mode; old_mode = fg_getmode(); fg_setmode(fg_automode()); fg_setcolor(15); fg_move(0,JUMP); fg_draw(fg_getmaxx(),JUMP); fg_draw(fg_getmaxx(),fg_getmaxy()-JUMP); fg_draw(0,fg_getmaxy()-JUMP); fg_draw(0,JUMP); fg_waitkey(); Chapter 11: Special Effects 207 for (i = 0; i < 6; i++) { fg_pan(0,JUMP); fg_waitfor(DELAY); fg_pan(0,0); fg_waitfor(DELAY); } fg_setmode(old_mode); fg_reset(); } The real power of the fg_pan routine becomes clear when it is used with the fg_resize routine to perform smooth panning. Recall from Chapter 7 that fg_resize changes the video page dimensions in native EGA graphics modes (modes 13 through 16), native VGA graphics modes (modes 17 and 18), and the extended VGA graphics modes (modes 20 through 23). We'll now present an example that shows how to use these two routines to perform panning in the low-resolution EGA graphics mode (mode 13). The method it uses also would work in any mode that supports video page resizing. Example 11-7 begins by establishing the video mode and then immediately calls fg_resize to increase the video page size to 640 x 400 pixels. Thus, the video page is now four times its original size. Following this, the program fills the page (the entire page, not just what is displayed) with a bright green rectangle with a white border around it. It then displays the message "Press arrow keys to pan" as close as possible to the center of the page. The main part of the program is a loop that accepts keystrokes and then calls fg_pan to perform the panning one pixel at a time. When you press any of the four arrow keys, the program adjusts the x and y coordinates for the screen origin as directed. For example, pressing the up arrow key scrolls the screen upward one pixel. Note that when we reach the edge of the video page, the program prevents further scrolling in that direction. This process continues until you press the Esc key, at which time the program restores the original video mode and screen attributes before exiting. Example 11-7. #include void main(void); void main() { unsigned char key, aux; int old_mode; int x, y; old_mode = fg_getmode(); fg_setmode(13); fg_resize(640,400); fg_setcolor(2); fg_rect(0,fg_getmaxx(),0,fg_getmaxy()); fg_setcolor(15); fg_box(0,fg_getmaxx(),0,fg_getmaxy()); 208 Fastgraph User's Guide fg_locate(24,28); fg_text("Press arrow keys to pan.",24); x = 0; y = 0; do { fg_getkey(&key,&aux); if (aux == 72 && y < 200) y++; else if (aux == 75 && x < 320) x++; else if (aux == 77 && x > 0) x--; else if (aux == 80 && y > 0) y--; fg_pan(x,y); } while (key != 27); fg_setmode(old_mode); fg_reset(); } Summary of Special Effects Routines This section summarizes the functional descriptions of the Fastgraph routines presented in this chapter. More detailed information about these routines, including their arguments and return values, may be found in the Fastgraph Reference Manual. FG_FADEIN incrementally replaces the visual page contents with the hidden page contents. This routine has no effect in text video modes. FG_FADEOUT incrementally replaces the visual page contents with pixels of the current color. This routine has no effect in text video modes. FG_PAN changes the screen origin (the upper left corner of the screen) to the specified screen space coordinates. This routine has no effect in text video modes. FG_PANW is the world space version of the fg_pan routine. FG_RESIZE changes the dimensions of a video page in EGA and VGA graphics modes. FG_SCROLL vertically scrolls a region of the active video page. The scrolling may be done either up or down, using either an end-off or circular method. Circular scrolling uses part of the hidden page as a temporary workspace. Chapter 12 Input Device Support 210 Fastgraph User's Guide Overview The selection of application input devices is an important part of designing a program for the IBM PC and PS/2 family of systems. The keyboard and mouse are very popular, and in fact more and more applications, especially those that use a graphical interface, actually require a mouse to use the product. Another input device, primarily used in entertainment software, is the joystick. Although not as popular as the mouse, the joystick nevertheless can simplify the use of certain applications. Fastgraph provides support for these three types of input devices, and this chapter will discuss this in detail. Keyboard Support Fastgraph's keyboard support includes routines to read keystrokes, check the state of certain keys, and set the state of these keys. These routines are independent of the other parts of Fastgraph and thus do not require that you call fg_setmode. All keyboard-related routines work in text and graphics video modes. The IBM PC and PS/2 keyboards produce two types of character codes -- standard codes and extended codes (extended codes are sometimes called auxiliary codes). The standard codes correspond to the 128 characters in the ASCII character set. In general, pressing keys on the main part of the keyboard, or on the numeric keypad with NumLock turned on, will generate a standard code. The 128 extended codes are specific to the IBM PC and PS/2 keyboards. Some common keystrokes that produce extended codes are keys on the numeric keypad with NumLock turned off, the function keys, or pressing Alt with another key. The following tables show the standard and extended keyboard codes. Table of standard keyboard codes key code key code key code key code (none) 0 space 32 @ 64 ` 96 Ctrl+A 1 ! 33 A 65 a 97 Ctrl+B 2 " 34 B 66 b 98 Ctrl+C 3 # 35 C 67 c 99 Ctrl+D 4 $ 36 D 68 d 100 Ctrl+E 5 % 37 E 69 e 101 Ctrl+F 6 & 38 F 70 f 102 Ctrl+G 7 ' 39 G 71 g 103 Ctrl+H 8 ( 40 H 72 h 104 Ctrl+I 9 ) 41 I 73 i 105 Ctrl+J 10 * 42 J 74 j 106 Ctrl+K 11 + 43 K 75 k 107 Ctrl+L 12 , 44 L 76 l 108 Ctrl+M 13 - 45 M 77 m 109 Ctrl+N 14 . 46 N 78 n 110 Ctrl+O 15 / 47 O 79 o 111 Ctrl+P 16 0 48 P 80 p 112 Ctrl+Q 17 1 49 Q 81 q 113 Ctrl+R 18 2 50 R 82 r 114 Chapter 12: Input Device Support 211 Ctrl+S 19 3 51 S 83 s 115 Ctrl+T 20 4 52 T 84 t 116 Ctrl+U 21 5 53 U 85 u 117 Ctrl+V 22 6 54 V 86 v 118 Ctrl+W 23 7 55 W 87 w 119 Ctrl+X 24 8 56 X 88 x 120 Ctrl+Y 25 9 57 Y 89 y 121 Ctrl+Z 26 : 58 Z 90 z 122 Ctrl+[ 27 ; 59 [ 91 { 123 Ctrl+\ 28 < 60 \ 92 | 124 Ctrl+] 29 = 61 ] 93 } 125 Ctrl+^ 30 > 62 ^ 94 ~ 126 Ctrl+- 31 ? 63 _ 95 Ctrl+BS 127 Table of extended keyboard codes code key 3 Ctrl+@ 15 Shift+Tab (back tab) 16-25 Alt+Q to Alt+P (top row of letters) 30-38 Alt+A to Alt+L (middle row of letters) 44-50 Alt+Z to Alt+M (bottom row of letters) 59-68 F1 to F10 71 Home 72 up arrow 73 PgUp 75 left arrow 77 right arrow 79 End 80 down arrow 81 PgDn 82 Ins 83 Del 84-93 Shift+F1 to Shift+F10 94-103 Ctrl+F1 to Ctrl+F10 104-113 Alt+F1 to Alt+F10 114 Ctrl+PrtSc 115 Ctrl+left arrow 116 Ctrl+right arrow 117 Ctrl+End 118 Ctrl+PgDn 119 Ctrl+Home 120-131 Alt+1 to Alt+= (top row of keys) 132 Ctrl+PgUp In addition, four keys generate the same standard codes as other control key combinations. These keys are: key same as code Backspace Ctrl+H 8 Tab Ctrl+I 9 Enter Ctrl+M 13 Escape Ctrl+[ 27 212 Fastgraph User's Guide The CapsLock, NumLock, and ScrollLock keys do not generate a standard or extended code when pressed. Instead, they toggle between off and on states. Reading Keystrokes When you press a key or key combination, the standard or extended code representing that keystroke is stored in the ROM BIOS keyboard buffer. This buffer can hold up to 16 keystrokes and thus provides a type-ahead capability. Fastgraph includes three routines for reading keystroke information from the keyboard buffer. The fg_getkey routine reads the next item in the keyboard buffer if one is available (that is, if a key has been pressed). If the keyboard buffer is empty (meaning no key has been pressed), fg_getkey waits for a keystroke and then reports information about it. Another routine, fg_intkey, reads the next keystroke from the keyboard buffer if one is available. If the keyboard buffer is empty, fg_intkey immediately returns and reports this condition. The fg_intkey routine is useful when a program must continue performing a task until a key is pressed. We've already seen the third routine, fg_waitkey, which flushes the keyboard buffer and then waits for another keystroke. Unlike fg_getkey and fg_intkey, fg_waitkey does not return any keystroke information. It is most useful in "press any key to continue" situations. Both the fg_getkey and fg_intkey routines require two one-byte arguments passed by reference. If the keystroke is represented by a standard keyboard code, fg_getkey and fg_intkey return its code in the first argument and set the second argument to zero. Similarly, if the keystroke generates an extended code, the routines return its code in the second argument and set the first argument to zero. If the fg_intkey routine detects an empty keyboard buffer, it sets both arguments to zero. Example 12-1 is a simple program that uses the fg_getkey routine. It solicits keystrokes and then displays the two values returned by fg_getkey, one of which will always be zero. The variable key receives the key's standard code, while aux receives its extended code. Note that fg_getkey is the only Fastgraph routine in the program; this can be done because the keyboard support routines are logically independent from the rest of Fastgraph. The program returns to DOS when you press the Escape key. Example 12-1. #include #include void main(void); #define ESC 27 void main() { unsigned char key, aux; do { fg_getkey(&key,&aux); printf("key = %3d aux = %3d\n",key,aux); } while (key != ESC); } Chapter 12: Input Device Support 213 Example 12-2 reads keystrokes using the fg_intkey routine at half-second intervals (18 fg_waitfor units equals one second). As in the previous example, the program displays the standard and extended codes for each keystroke. However, example 12-2 will continuously execute the while loop even if no keystrokes are available, in which case the key and aux values will both be zero. The program returns to DOS when you press the Escape key. Example 12-2. #include #include void main(void); #define ESC 27 void main() { unsigned char key, aux; do { fg_waitfor(9); fg_intkey(&key,&aux); printf("key = %3d aux = %3d\n",key,aux); } while (key != ESC); } When you use fg_intkey in a "tight" loop that does little else, you should force a small delay within the loop by calling fg_waitfor as in example 12-2. Typically a delay of one or two clock ticks is enough. Without this delay, the BIOS may not be able to handle all keyboard activity, and thus some keystrokes may not be available to your program. Testing and Setting Key States As mentioned earlier, the CapsLock, NumLock, and ScrollLock keys do not generate a standard or extended code when pressed but instead toggle between off and on states. Fastgraph includes routines for checking the state of these keys, as well as setting the state of the CapsLock and NumLock keys. The Fastgraph routines fg_capslock, fg_numlock, and fg_scrlock respectively read the state of the CapsLock, NumLock, and ScrollLock keys. Each routine has no arguments and returns the key state as its function value. A return value of 0 means the associated key is in the off state, while 1 indicates the key is in the on state. If the keyboard does not have a ScrollLock key, fg_scrlock considers the key off and returns a value of zero. Example 12-3 is a simple program that uses the fg_capslock, fg_numlock, and fg_scrlock routines to print messages describing the current state of these three keys. 214 Fastgraph User's Guide Example 12-3. #include #include void main(void); void main() { if (fg_capslock()) printf("CapsLock is on.\n"); else printf("CapsLock is off.\n"); if (fg_numlock()) printf("NumLock is on.\n"); else printf("NumLock is off.\n"); if (fg_scrlock()) printf("ScrollLock is on.\n"); else printf("ScrollLock is off.\n"); } You also can set the state of the CapsLock and NumLock keys within a program. Fastgraph includes two routines, fg_setcaps and fg_setnum, for this purpose. Each routine requires an integer argument that specifies the new key state. If the argument value is 0, the key will be turned off; if the value is 1, the key will be turned on. Example 12-4 uses fg_setcaps and fg_setnum to turn off CapsLock and NumLock. Example 12-4. #include void main(void); void main() { fg_setcaps(0); fg_setnum(0); } On most keyboards, changing key states with fg_setcaps or fg_setnum also will change the keyboard state light to reflect the new key state. However, some older keyboards, especially when used on PC, PC/XT, or Tandy 1000 systems, do not update the state light. This makes the state light inconsistent with the true key state. Mouse Support The mouse is a very popular input and pointing device, especially in graphically-oriented programs. Fastgraph contains several routines to support mice. These routines perform such tasks as mouse initialization, controlling and defining the mouse cursor, and reporting information about the mouse position and button status. Chapter 12: Input Device Support 215 The underlying software that controls the mouse is called the mouse driver. Fastgraph's mouse support routines provide a high-level interface to this driver. The Microsoft Mouse and its accompanying mouse driver have become an industry standard, and other manufacturers of mice have also made their mouse drivers Microsoft compatible. For this reason, the Fastgraph mouse support routines assume you are using a Microsoft or compatible mouse driver. Unfortunately, not all mouse drivers are created equal. That is, some drivers are not Microsoft compatible, even though they may be advertised as such. In some cases, these incompatibilities are rather trivial, but others are significant. For example, early versions of some third party mouse drivers had real problems in the EGA graphics modes. The Microsoft mouse driver, the Logitech mouse driver (version 3.2 or above), and the DFI mouse driver (version 3.00 or above) are known to work well with Fastgraph's mouse support routines. Any other Microsoft compatible mouse driver also should work properly. Initializing the Mouse There are two steps required to use Fastgraph's mouse support routines within an application program. First, you must install the mouse driver. This is done before running the application, typically by entering the command MOUSE at the DOS command prompt. Second, you must use the Fastgraph routine fg_mouseini to initialize the mouse within the program. The fg_mouseini routine has no arguments and returns a "success or failure" indicator as its function value. If the return value is -1, it means fg_mouseini could not initialize the mouse (either because the mouse driver is not installed, or the driver is installed but the mouse is physically disconnected). The fg_mouseini routine also will return -1 when used in the extended VGA graphics video modes (modes 20 through 23) because there is no mouse support available in these video modes. If fg_mouseini returns a positive integer value, then the mouse initialization was successful. The value itself indicates the number of buttons (either 2 or 3) on the mouse. If you don't call fg_mouseini, or if fg_mouseini can't initialize the mouse, none of Fastgraph's other mouse support routines will have any effect.(3) Example 12-5 illustrates how to initialize the mouse. Unlike the keyboard support routines, Fastgraph's mouse support routines require that fg_setmode first be called. In this example, we simply pass fg_setmode the value -1 to initialize Fastgraph for whatever video mode is in effect when we run the program. The program then calls fg_mouseini and prints a message indicating whether or not the initialization was successful. If it was, the message includes the number of buttons on the mouse. Example 12-5. #include ____________________ (3) If you use another mouse library or communicate directly with the mouse driver, you must still call fg_mouseini if your program runs in modes 13 through 18. Otherwise, Fastgraph won't know that your program is using a mouse and may display graphics incorrectly. 216 Fastgraph User's Guide #include void main(void); void main() { int status; fg_setmode(-1); status = fg_mouseini(); if (status < 0) printf("Mouse not available.\n"); else printf("%d button mouse found.\n",status); } You should be aware that certain mouse drivers do not fully initialize the mouse when a program changes video modes. This problem most frequently occurs when you restore the original video mode at the end of a program that has called fg_mouseini. When changing video modes, you must first make the mouse cursor invisible (this is described in the next section), change the video mode, and then call fg_mouseini again to initialize the mouse for the new video mode. Controlling the Mouse Cursor The mouse cursor indicates the current position of the mouse. By default, the cursor is a small white arrow in graphics modes and a one- character rectangle in text modes. After you use fg_mouseini to initialize the mouse, the mouse cursor is invisible. To make it visible, you must use the fg_mousevis routine. This routine has a single integer argument that defines the mouse cursor visibility. If it is 0, the mouse cursor will be invisible; if it is 1, the mouse cursor becomes visible. If the mouse cursor is in an area of the screen that is being updated, or if it moves into this area during the update process, you must make the mouse cursor invisible. Additionally, when performing any video output in the native EGA and VGA graphics modes (modes 13 through 18), you also must make the mouse cursor invisible. Instead of checking for these conditions, it is more convenient and efficient to make the mouse cursor invisible during all screen updates and then make it visible again when the updating is finished. Finally, you must make the mouse cursor invisible whenever you change the visual page number with fg_setvpage. After you initialize the mouse, the cursor is positioned in the center of the screen. Moving the mouse of course changes the cursor position, but you also can position the mouse cursor with the Fastgraph routine fg_mousemov. This routine has two arguments that specify the new horizontal and vertical cursor position. The position is expressed in screen space units for graphics modes, while it is expressed in character cells for text modes. The fg_mousemov routine moves the cursor whether or not it is visible. Sometimes it is useful to restrict the mouse cursor to a specific area of the screen. The Fastgraph routine fg_mouselim prevents the mouse cursor from moving outside the specified rectangular area. It requires four Chapter 12: Input Device Support 217 arguments that specify the minimum horizontal coordinate, maximum horizontal coordinate, minimum vertical coordinate, and maximum vertical coordinate of this area. Again, the coordinates are expressed in screen space units for graphics modes and character cells for text modes. One of the most important functions of the mouse driver is to translate the horizontal and vertical mouse movements into a position on the screen. The mouse reports these movements to the mouse driver in units called mickeys (one mickey is about 1/200 of an inch). By default, moving the mouse 8 mickeys in the horizontal direction moves the mouse cursor one horizontal pixel. Similarly, moving the mouse 16 mickeys vertically moves the cursor one vertical pixel. Fastgraph provides a routine named fg_mousespd that can change these values, which effectively allows you to control the speed at which the mouse cursor moves relative to the movement of the mouse itself. The fg_mousespd routine requires two arguments that define the number of mickeys required for eight pixels of mouse cursor movement. The first argument specifies this for the horizontal direction, and the second for the vertical direction. Example 12-6, which runs in any graphics mode, demonstrates the fg_mousevis, fg_mousemov, fg_mouselim, and fg_mousespd routines. The program first establishes the video mode, initializes the mouse, and fills the screen with a white rectangle. Next, the program calls fg_mousevis to make the mouse cursor visible and then calls fg_mouselim to restrict the mouse cursor to an area one-fourth the size of the screen, centered in the middle of the screen. At this point you should move the mouse cursor around the screen to see the effect of fg_mouselim and note the speed at which the cursor moves relative to the mouse itself. The program continues when you press any key. The program then uses fg_mousemov to move the mouse cursor to each corner of the region established by fg_mouselim. The call to fg_waitfor keeps the cursor in each corner for two seconds, unless you move the mouse. Note how the program tries to move the mouse cursor to each corner of the screen, but since doing so would move the cursor outside the defined region of movement, fg_mousemov just positions the cursor at the nearest point possible within this region. The last call to fg_mousemov moves the cursor back to the middle of the screen. After doing this, the program calls fg_mousespd to change the mouse cursor speed. The values passed to fg_mousespd (16 and 32) are twice the defaults and therefore make you move the mouse twice as far as before to move the mouse cursor the same distance. When you run the program, compare the mouse sensitivity to the original speed. After a keystroke, the program returns to DOS. Example 12-6. #include #include #include void main(void); void main() { int maxx, maxy; int old_mode; old_mode = fg_getmode(); 218 Fastgraph User's Guide fg_setmode(fg_automode()); if (fg_mouseini() < 0) { fg_setmode(old_mode); fg_reset(); exit(1); } maxx = fg_getmaxx(); maxy = fg_getmaxy(); fg_setcolor(15); fg_rect(0,maxx,0,maxy); fg_mousevis(1); fg_mouselim(maxx/4,3*maxx/4,maxy/4,3*maxy/4); fg_waitkey(); fg_mousemov(0,0); fg_waitfor(36); fg_mousemov(maxx,0); fg_waitfor(36); fg_mousemov(maxx,maxy); fg_waitfor(36); fg_mousemov(0,maxy); fg_waitfor(36); fg_mousemov(maxx/2,maxy/2); fg_mousespd(16,32); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Reporting the Mouse Status It is obviously important to be able to track the mouse position and button status. The Fastgraph routines fg_mousepos and fg_mousebut enable you to do this. The fg_mousepos routine returns information about the current mouse cursor position and button status. It requires three integer arguments, all passed by reference. The first two arguments respectively receive the horizontal and vertical coordinates of the mouse cursor. These values are expressed in screen space units for graphics modes and character cells for text modes. The third argument receives a three-bit mask containing the button status as indicated below. bit number meaning 0 1 if left button pressed, 0 if not 1 1 if right button pressed, 0 if not 2 1 if middle button pressed, 0 if not Chapter 12: Input Device Support 219 For example, if both the left and right buttons are pressed, the button status will be set to 3. If the mouse only has two buttons, bit 2 will always be zero. Another routine, fg_mousebut, is available for returning the number of button press or release counts that have occurred since the last check, or since calling fg_mouseini. Each mouse button maintains its own separate counters, so fg_mousebut returns this information for a specific button. Additionally, fg_mousebut returns the horizontal and vertical position of the mouse cursor at the time the specified button was last pressed or released. The fg_mousebut routine takes four integer arguments, of which the last three are passed by reference. The first argument specifies the button of interest (1 means the left button, 2 is the right button, and 3 is the middle button). If this value is positive, button press counts will be reported. If it is negative, release counts will be reported. The second, third, and fourth arguments respectively receive the press or release count, the horizontal mouse cursor position at the time of the last press or release, and the vertical position at that same time. If the press or release count is zero, the mouse cursor position is returned as (0,0). The coordinate positions are expressed in screen space units for graphics modes and character cells for text modes. Example 12-7 runs in any graphics video mode and illustrates the use of the fg_mousepos and fg_mousebut routines. The program first establishes the video mode and then initializes the mouse (the program exits if the initialization fails). It next fills the entire screen with a white rectangle and then calls fg_mousevis to make the mouse cursor visible. The main part of example 12-7 is a while loop that polls the mouse at three-second intervals (the call fg_waitfor(54) delays the program for three seconds). Within the loop, the program first uses fg_mousebut to get the number of times the left mouse button was pressed in the last three seconds. Following this, the fg_mousepos routine gets the current mouse position. The program then displays this information in the upper left corner of the screen; note how fg_mousevis is used to make the cursor invisible during graphics operations. The program continues until you press the right mouse button, checked by the call to fg_mousebut at the end of the loop. Example 12-7. #include #include #include void main(void); void main() { int old_mode; int buttons, count; int x, y; char string[25]; old_mode = fg_getmode(); fg_setmode(fg_automode()); 220 Fastgraph User's Guide if (fg_mouseini() < 0) { fg_setmode(old_mode); fg_reset(); exit(1); } fg_setcolor(15); fg_rect(0,fg_getmaxx(),0,fg_getmaxy()); fg_mousevis(1); do { fg_waitfor(54); fg_mousebut(1,&count,&x,&y); fg_mousepos(&x,&y,&buttons); sprintf(string,"X=%3d Y=%3d count=%4d",x,y,count); fg_mousevis(0); fg_setcolor(15); fg_rect(0,fg_xconvert(25),0,fg_yconvert(1)); fg_setcolor(0); fg_locate(0,0); fg_text(string,24); fg_mousevis(1); fg_mousebut(2,&count,&x,&y); } while (count == 0); fg_setmode(old_mode); fg_reset(); } Defining the Mouse Cursor By default, the mouse cursor is a small white arrow in graphics modes and a one-character rectangle in text modes. In graphics modes, you can change the mouse cursor to any 16 by 16 pixel image with the Fastgraph routine fg_mouseptr (in the CGA four-color graphics modes, the cursor size is 8 by 16 pixels). You cannot change the mouse cursor shape in text modes, but you can use the Fastgraph routine fg_mousecur to define how it interacts with existing characters on the screen. Text Modes To change the mouse cursor in text modes, you must first define two 16- bit quantities called the screen mask and cursor mask. The following figure defines the format of each mask. bits meaning 0 to 7 ASCII character value 8 to 11 foreground color 12 to 14 background color 15 blink Chapter 12: Input Device Support 221 Notice how this structure parallels the character and attribute bytes associated with each character cell. The default screen mask is 77FF hex, and the default cursor mask is 7700 hex. When you position the mouse over a specific character cell, the mouse driver uses the current screen and cursor masks to determine the mouse cursor's appearance. First, the mouse driver logically ANDs the screen mask with the existing contents of that character cell. It then XORs that result with the cursor mask to display the mouse cursor. For example, consider how the mouse cursor is produced in the 80-column color text mode (mode 3). Suppose a specific character cell contains the ASCII character 0 (48 decimal, 30 hex) and an attribute byte that specifies a white (color 15) foreground on a blue background (color 1) and does not blink (blink bit 0). The binary structure of the character and its attribute are: attribute character 0 001 1111 00110000 Now let's see what happens when we apply the screen and cursor masks to the character and its attribute. attribute/character 0001 1111 0011 0000 (1F30 hex) default screen mask 0111 0111 1111 1111 (77FF hex) ------------------- result of AND 0001 0111 0011 0000 (1730 hex) default cursor mask 0111 0111 0000 0000 (7700 hex) ------------------- result of XOR 0110 0000 0011 0000 (6030 hex) The resulting character (30 hex) is the original character, but the new attribute (60 hex) represents a black foreground with a brown background and does not blink. As long as the mouse cursor remains positioned on this character cell, it would appear black on brown. When we use the default screen and cursor masks, the mouse cursor will always display the original character and it will not blink. The cursor foreground color will be 15-F, where F is the displayed character's foreground color. Similarly, the cursor background color will be 7-B, where B is the displayed character's background color. The default masks will virtually always produce a satisfactory mouse cursor. It is possible, however, to change the appearance of the mouse cursor in text modes by using your own screen and cursor masks. The Fastgraph routine fg_mousecur does just that. It expects two arguments, the first being the cursor mask and the second the screen mask. Example 12-8 demonstrates the use of fg_mousecur. The program displays some text and uses the default mouse cursor. After waiting for a keystroke, the program calls fg_mousecur to define a new mouse cursor. The new cursor is similar to the default cursor, but it displays the foreground colors in the opposite intensity as 222 Fastgraph User's Guide the default cursor. The program then waits for another keystroke before returning to DOS. Example 12-8. #include #include #include void main(void); void main() { int old_mode; int row; old_mode = fg_getmode(); fg_setmode(3); if (fg_mouseini() < 0) { fg_setmode(old_mode); fg_reset(); exit(1); } fg_setattr(7,0,0); fg_rect(0,fg_getmaxx(),0,fg_getmaxy()); fg_setattr(12,7,0); for (row = 0; row < 25; row++) { fg_locate(row,34); fg_text("example 12-8",12); } fg_mousevis(1); fg_waitkey(); fg_mousecur(0x7FFF,0x7F00); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } Graphics Modes Defining the mouse cursor in graphics video modes also requires creating a screen mask and cursor mask, but as one might expect, the structure of these masks is vastly different from for text modes. In fact, it closely resembles the mode-independent bit map format used by the fg_drawmap routine. Although their structure differs, the way the mouse driver uses the masks is the same as in the text modes. That is, the driver displays the mouse cursor by first logically ANDing video memory with the screen mask, and then XORing that result with the cursor mask. Let's begin by looking at the masks for the default mouse cursor in graphics modes. The size of each mask (and hence the mouse cursor) is 16 pixels wide and 16 pixels high. As mentioned earlier, the default cursor is Chapter 12: Input Device Support 223 a small white arrow with a black outline around it. Here are its screen and cursor masks expressed as binary values. screen cursor cursor mask mask appearance 1001111111111111 0000000000000000 ** 1000111111111111 0010000000000000 *x* 1000011111111111 0011000000000000 *xx* 1000001111111111 0011100000000000 *xxx* 1000000111111111 0011110000000000 *xxxx* 1000000011111111 0011111000000000 *xxxxx* 1000000001111111 0011111100000000 *xxxxxx* 1000000000111111 0011111110000000 *xxxxxxx* 1000000000011111 0011111111000000 *xxxxxxxx* 1000000000001111 0011111000000000 *xxxxx***** 1000000011111111 0011011000000000 *xx*xx* 1000100001111111 0010001100000000 *x* *xx* 1001100001111111 0000001100000000 ** *xx* 1111110000111111 0000000110000000 *xx* 1111110000111111 0000000110000000 *xx* 1111111000111111 0000000000000000 *** The mouse driver first ANDs the screen mask with video memory at the mouse cursor position. This means the screen mask 1 bits leave video memory intact, while the 0 bits change the corresponding pixels to black. Next, the mouse driver XORs the result with the cursor mask. This time the cursor mask 0 bits leave video memory unchanged, while the 1 bits change the corresponding pixels to white. This produces a mouse cursor as shown above on the right, where a dot ( ) represents an unchanged pixel, an asterisk (*) a black pixel, and an x a white pixel. The following table summarizes the cursor appearance for all possible combinations of mask bits. screen mask bit cursor mask bit resulting cursor pixel 0 0 black 0 1 white 1 0 unchanged 1 1 inverted The color of an "inverted" pixel is n-k, where n is the maximum color number in the current video mode, and k is the color of the pixel being replaced. Also, "black" and "white" pixels are not necessarily these colors in 16-color and 256-color modes. More correctly, "black" pixels are displayed in the color assigned to palette 0, and "white" pixels are the displayed in the color assigned to palette 15. If you're using the CGA color modes, "black" pixels are displayed in the background color, and "white" pixels appear in color 3 (whose actual color is determined by the selected CGA palette). With an understanding of the way the default mouse cursor works in graphics modes, we're now ready to define our own mouse cursor. Shown below are the screen mask, cursor mask, and resulting appearance for a solid plus- shaped cursor. The hexadecimal equivalents of the binary mask values are also given. 224 Fastgraph User's Guide ----- screen mask ---- ----- cursor mask ---- cursor binary hex binary hex appearance 1110000000111111 E03F 0000000000000000 0000 ...*******...... 1110000000111111 E03F 0000111110000000 0F80 ...*xxxxx*...... 1110000000111111 E03F 0000111110000000 0F80 ...*xxxxx*...... 0000000000000111 0007 0000111110000000 0F80 ****xxxxx****... 0000000000000111 0007 0111111111110000 7FF0 *xxxxxxxxxxx*... 0000000000000111 0007 0111111111110000 7FF0 *xxxxxxxxxxx*... 0000000000000111 0007 0111111111110000 7FF0 *xxxxxxxxxxx*... 0000000000000111 0007 0111111111110000 7FF0 *xxxxxxxxxxx*... 0000000000000111 0007 0111111111110000 7FF0 *xxxxxxxxxxx*... 0000000000000111 0007 0000111110000000 0F80 ****xxxxx****... 1110000000111111 E03F 0000111110000000 0F80 ...*xxxxx*...... 1110000000111111 E03F 0000111110000000 0F80 ...*xxxxx*...... 1110000000111111 E03F 0000000000000000 0000 ...*******...... 1111111111111111 FFFF 0000000000000000 0000 ................ 1111111111111111 FFFF 0000000000000000 0000 ................ 1111111111111111 FFFF 0000000000000000 0000 ................ If we wanted to make the mouse cursor hollow rather than solid, the masks and resulting cursor appearance would look like this. ----- screen mask ---- ----- cursor mask ---- cursor binary hex binary hex appearance 1110000000111111 E03F 0000000000000000 0000 ...*******...... 1110111110111111 EFBF 0000000000000000 0000 ...*.....*...... 1110111110111111 EFBF 0000000000000000 0000 ...*.....*...... 0000111110000111 0F87 0000000000000000 0000 ****.....****... 0111111111110111 7FF7 0000000000000000 0000 *...........*... 0111111111110111 7FF7 0000000000000000 0000 *...........*... 0111111111110111 7FF7 0000001000000000 0200 *.....x.....*... 0111111111110111 7FF7 0000000000000000 0000 *...........*... 0111111111110111 7FF7 0000000000000000 0000 *...........*... 0000111110000111 0F87 0000000000000000 0000 ****.....****... 1110111110111111 EFBF 0000000000000000 0000 ...*.....*...... 1110111110111111 EFBF 0000000000000000 0000 ...*.....*...... 1110000000111111 E03F 0000000000000000 0000 ...*******...... 1111111111111111 FFFF 0000000000000000 0000 ................ 1111111111111111 FFFF 0000000000000000 0000 ................ 1111111111111111 FFFF 0000000000000000 0000 ................ Note that the center bit defined in the cursor mask causes the corresponding pixel in video memory to be inverted. There is one more item needed to define a graphics mode mouse cursor completely. That item is the hot spot, or the actual screen position used or reported by the mouse driver. For the plus-shaped cursors just constructed, it would be sensible to define the hot spot in the center of the plus. The hot spot is specified relative to the upper left corner of the cursor, so its position within the cursor would be (6,6) -- that is, six pixels to the right and six pixels below the upper left corner. You can specify the hot spot offsets using negative values or values above 15 to position it outside the mouse cursor matrix if desired. Chapter 12: Input Device Support 225 The Fastgraph routine fg_mouseptr defines a mouse cursor in graphics modes. The first of its three arguments is a 32-element integer array, passed by reference. The array's first 16 elements contain the screen mask, and its second 16 elements contain the cursor mask. The remaining two arguments respectively specify the horizontal and vertical offsets for the hot spot. The fg_mouseptr routine has no effect in a text video mode. Example 12-9 is similar to example 12-8. It shows how to define a graphics mode mouse cursor using fg_mouseptr. The values stored in the solid and hollow arrays define the screen and cursor masks for the solid and hollow plus-shaped mouse cursors discussed earlier. After making the mouse cursor visible, the program uses the default mouse cursor until a key is pressed. Following this, it changes to the solid cursor. After another keystroke, the program changes to the hollow cursor. When you run example 12-9, compare the differences among the three mouse cursors. Example 12-9. #include #include #include void main(void); int solid[] = {0xE03F,0xE03F,0xE03F,0x0007,0x0007,0x0007,0x0007,0x0007, 0x0007,0x0007,0xE03F,0xE03F,0xE03F,0xFFFF,0xFFFF,0xFFFF, 0x0000,0x0F80,0x0F80,0x0F80,0x7FF0,0x7FF0,0x7FF0,0x7FF0, 0x7FF0,0x0F80,0x0F80,0x0F80,0x0000,0x0000,0x0000,0x0000}; int hollow[] = {0xE03F,0xEFBF,0xEFBF,0x0F87,0x7FF7,0x7FF7,0x7FF7,0x7FF7, 0x7FF7,0x0F87,0xEFBF,0xEFBF,0xE03F,0xFFFF,0xFFFF,0xFFFF, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0200,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; void main() { int old_mode; int column, row, last_row; old_mode = fg_getmode(); fg_setmode(fg_automode()); if (fg_mouseini() < 0) { fg_setmode(old_mode); fg_reset(); exit(1); } fg_setcolor(15); fg_rect(0,fg_getmaxx(),0,fg_getmaxy()); fg_setcolor(12); column = fg_xalpha(fg_getmaxx()/2) - 6; last_row = fg_yalpha(fg_getmaxy()) + 1; for (row = 0; row < last_row; row++) { fg_locate(row,column); fg_text("example 12-9",12); 226 Fastgraph User's Guide } fg_mousevis(1); fg_waitkey(); fg_mouseptr(solid,6,6); fg_waitkey(); fg_mouseptr(hollow,6,6); fg_waitkey(); fg_setmode(old_mode); fg_reset(); } CGA Considerations The mouse driver treats the screen and cursor masks differently in the CGA four-color graphics modes (modes 4 and 5) than in the other graphics modes. In the CGA modes, each pair of mask bits corresponds to one pixel. This means the masks more closely resemble the mode-specific format used by fg_drwimage instead of the mode-independent format of fg_drawmap. Fastgraph uses a different default mouse cursor for modes 4 and 5. Its screen and cursor masks, as well as the resulting cursor appearance, are shown in the following diagram. screen cursor cursor mask mask appearance 0000111111111111 0000000000000000 ** 0000001111111111 0011000000000000 *** 0000000011111111 0011110000000000 **** 0000000000111111 0011111100000000 ***** 0000000000001111 0011111111000000 ****** 0000000000000011 0011111111110000 ******* 0000000000000011 0011111100000000 ******* 0000000000111111 0011111110000000 ***** 0000000000001111 0011000011000000 ****** 0000110000001111 0000000011000000 ** *** 1111111100000011 0000000000110000 *** 1111111100000011 0010000000110000 *** 1111111111000011 0000000000000000 ** 1111111111111111 0000000000000000 1111111111111111 0000000000000000 1111111111111111 0000000000000000 As you can see, the resulting mouse cursor is eight pixels wide instead of 16. Another important point concerning mouse cursors in modes 4 and 5 is the chance of pixel bleeding, or the changing of colors within the mouse cursor as it moves horizontally. Bleeding will occur if you use the bit pairs 01 or 10 in either mask to represent a pixel. In the default masks for modes 4 and 5, note that only the binary values 00 and 11 appear as bit pairs. Keep this in mind if you create your own masks in these video modes. Chapter 12: Input Device Support 227 Joystick Support The third type of input device supported by Fastgraph is the joystick. Although joysticks are not as popular as mice, they are often preferable when a user's reactions are critical, such as in an arcade-style game. Fastgraph includes routines for initializing a joystick, reading a joystick's position or button status, and making a joystick behave analogously to the keyboard. These routines are independent of the rest of Fastgraph and thus do not require that you first call the fg_setmode routine. Joysticks are connected to a system through a game port. The PCjr and Tandy 1000 systems come equipped with two game ports, and hence support two joysticks. On other systems in the IBM family, you can install a game port card that contains either one or two game ports. If the card only has one game port, you can use a splitter cable to fork two joysticks into the port. Initializing Joysticks Before you can use any of Fastgraph's joystick support routines with a specific joystick, you must initialize that joystick. The fg_initjoy routine performs this task. This routine requires a single integer argument that specifies which joystick to initialize, either 1 or 2. If successful, fg_initjoy returns 0 as the function value. If the machine has no game port, or if the requested joystick is not connected to the game port, fg_initjoy returns -1. When you use fg_initjoy, the joystick being initialized must be centered (that is, the stick itself must not be tilted in either direction). Example 12-10 uses the fg_initjoy routine to try to initialize both joysticks. For each joystick, the program prints a message stating whether or not the initialization was successful. Example 12-10. #include #include void main(void); void main() { if (fg_initjoy(1) < 0) printf("Joystick 1 not available.\n"); else printf("Joystick 1 found.\n"); if (fg_initjoy(2) < 0) printf("Joystick 2 not available.\n"); else printf("Joystick 2 found.\n"); } 228 Fastgraph User's Guide Reporting Joystick Status Each joystick can report three items: its horizontal position, its vertical position, and the button status. Fastgraph includes routines for obtaining each of these quantities. The fg_getxjoy and fg_getyjoy routines respectively return the horizontal and vertical position of the indicated joystick. Both routines require a single integer argument, whose value is either 1 or 2, to identify the joystick. The requested position is returned as the function value. Horizontal coordinates increase as the joystick moves to the right, while vertical coordinates increase as the joystick moves downward. If fg_initjoy did not initialize the specified joystick, or if your program hasn't yet called fg_initjoy, both fg_getxjoy and fg_getyjoy will return the value -1. Joystick characteristics vary more than those of any other input device. The values returned by fg_getxjoy and fg_getyjoy depend on the system's processor speed and the brand of joystick used. It often suffices to know the joystick position relative to its previous position, in which case the actual coordinate values do not matter. However, if you must rely on specific coordinate values, your program must perform some type of manual joystick calibration and then scale the coordinates reported by fg_getxjoy and fg_getyjoy as needed. The other piece of information joysticks provide is the button status. Most joysticks have two buttons, called the top and bottom buttons. Others have three buttons, but one of them duplicates the functionality of another (for example, a joystick might have one bottom button on its left side and another on its right side). The Fastgraph routine fg_button returns the joystick button status as its function value. Like fg_getxjoy and fg_getyjoy, the fg_button routine requires a single argument that specifies the joystick number. The meaning of the returned value is shown below. value meaning 0 neither button pressed 1 top button pressed 2 bottom button pressed 3 top and bottom buttons pressed You don't need to call fg_initjoy before using fg_button. If the specified joystick is not present, the fg_button routine will return a value of 0. Example 12-11 uses fg_getxjoy, fg_getyjoy, and fg_button to poll both joysticks at half-second intervals. It then displays the joystick number (1 or 2), horizontal position, vertical position, and button status for each joystick. As the program runs, you can move the joysticks and watch how the movements affect the displayed coordinate values. The program continues doing this until you press Ctrl/C or Ctrl/Break to stop it. Chapter 12: Input Device Support 229 Example 12-11. #include #include void main(void); void main() { int b, x, y; fg_initjoy(1); fg_initjoy(2); while (1) { x = fg_getxjoy(1); y = fg_getyjoy(1); b = fg_button(1); printf("1: %3d %3d %1d\n",x,y,b); x = fg_getxjoy(2); y = fg_getyjoy(2); b = fg_button(2); printf("2: %3d %3d %1d\n\n",x,y,b); fg_waitfor(9); } } There are two ways of effectively monitoring joystick button status. One is to call fg_button at many places in your program and then take the necessary action depending on the button status. However, the preferable method is to extend the BIOS time-of-day interrupt to check the button status at each clock tick (there are 18.2 clock ticks per second), set a flag if a button is pressed, and then check the flag as needed in your program. Information on changing the BIOS time-of-day interrupt appears in Appendix C of this document. Keyboard Emulation Although we can use the fg_getxjoy and fg_getyjoy routines to monitor relative joystick movements, it is usually easier to do this with another Fastgraph routine, fg_intjoy. This routine is similar to the fg_intkey routine in that it returns two values that are equivalent to the standard or extended keyboard codes for analogous keystrokes. The fg_intjoy routine needs three arguments. The first argument specifies the joystick number, either 1 or 2. The second and third arguments, both one-byte quantities passed by reference, receive the standard and extended keyboard codes analogous to the joystick movement and button status. The second argument receives a value of 13 (the standard keyboard code for the Enter key) if any joystick button is pressed; it receives a value of 0 if not. The third argument receives a value corresponding to the extended keyboard code for one of the directional keys on the numeric keypad, as summarized in the following table. 230 Fastgraph User's Guide joystick position corresponding key extended key code up and left Home 71 up up arrow 72 up and right PgUp 73 left left arrow 75 centered (no action) 0 right right arrow 77 down and left End 79 down down arrow 80 down and right PgDn 81 The fg_intjoy routine will set both key code arguments to zero if the specified joystick has not yet been initialized. Example 12-12 is similar to example 12-10, but it uses fg_intjoy in place of fg_getxjoy and fg_getyjoy to report relative joystick position. This program does not report the joystick button status as example 12-10 does, but you could readily add this feature to it. Example 12-12. #include #include void main(void); void main() { char key, aux; fg_initjoy(1); fg_initjoy(2); while (1) { fg_intjoy(1,&key,&aux); printf("1: %2d %2d\n",key,aux); fg_intjoy(2,&key,&aux); printf("2: %2d %2d\n\n",key,aux); fg_waitfor(9); } } Special Joystick Considerations If you develop a program that supports only one joystick, you should use joystick 1. The reasons for this are twofold. First, it will make your program consistent with most other products that support joysticks. Second, and perhaps more importantly, many Tandy 1000 series machines cannot determine if joystick 2 is present when neither joystick is connected. This means if you use joystick 2 instead of joystick 1 in a single joystick program, you won't be able to tell if a joystick is available when running on a Tandy 1000. Chapter 12: Input Device Support 231 Summary of Input Routines This section summarizes the functional descriptions of the Fastgraph routines presented in this chapter. More detailed information about these routines, including their arguments and return values, may be found in the Fastgraph Reference Manual. FG_BUTTON returns information about the state of either joystick's buttons. FG_CAPSLOCK determines the state of the CapsLock key. FG_GETKEY waits for a keystroke (or reads the next entry from the BIOS keyboard buffer). It returns the keystroke's standard or extended keyboard code. FG_GETXJOY and FG_GETYJOY return the horizontal and vertical coordinate position of the specified joystick. The actual coordinates depend on the processor speed and brand of joystick used. FG_INITJOY initializes joystick 1 or 2 and must be called before using fg_getxjoy, fg_getyjoy, or fg_intjoy. It returns a status code indicating whether or not the initialization was successful. FG_INTJOY returns the standard and extended keyboard codes analogous to the current position and button status of the specified joystick. FG_INTKEY reads the next entry from the BIOS keyboard buffer and returns the keystroke's standard or extended keyboard code. It is similar to fg_getkey, but it does not wait for a keystroke if the keyboard buffer is empty. FG_MOUSEBUT returns information about mouse button press or release counts, as well as the mouse cursor position at the time of the last button press or release. FG_MOUSECUR defines the appearance of the mouse cursor in text video modes. FG_MOUSEINI initializes the mouse and must be called before any of Fastgraph's other mouse support routines. It returns an error status if the mouse driver has not been loaded, or if the mouse is not connected. FG_MOUSELIM defines the rectangular area in which the mouse cursor may move. FG_MOUSEMOV moves the mouse cursor to the specified character cell (in text modes) or screen space position (in graphics modes). FG_MOUSEPOS returns the current mouse position and button status. FG_MOUSEPTR defines the shape and appearance of the mouse cursor in graphics video modes. FG_MOUSESPD defines the number of mickey units per eight pixels of cursor movement. This effectively controls the speed at which the mouse cursor moves relative to the movement of the mouse itself. 232 Fastgraph User's Guide FG_MOUSEVIS makes the mouse cursor visible or invisible. FG_NUMLOCK determines the state of the NumLock key. FG_SCRLOCK determines the state of the ScrollLock key (which is not present on some keyboards). FG_SETCAPS controls the state of the CapsLock key. FG_SETNUM controls the state of the NumLock key. FG_WAITKEY flushes the BIOS keyboard buffer (that is, removes any type- ahead characters) and then waits for another keystroke. Chapter 13 Sound Effects 234 Fastgraph User's Guide Overview In the realm of the IBM PC and PS/2 family of systems, a sound is defined by its frequency, duration, and volume. The frequency of a sound is measured in units called Hertz. While the PC and PS/2 can produce sounds ranging from 18 to more than one million Hertz, the average human can hear sounds between 20 and about 20,000 Hertz. The length of a sound, called its duration, is expressed in clock ticks; there are either 18.2 of 72.8 clock ticks per second, depending on the method used to produce the sound. Finally, the volume determines the loudness of the sound. As we'll see in this chapter, we can control a sound's volume only on the PCjr and Tandy 1000 systems. Fastgraph contains several different methods for producing sound effects. These include single tones, a series of tones expressed numerically, or a series of tones expressed as musical notes. The sound effects may be discrete, continuous, or performed at the same time as other activity. The sound-related routines are independent of the other parts of Fastgraph and do not require any initialization routines be called. Sound Sources All members of the PC and PS/2 families can produce sounds using the 8253-5 programmable timer chip and the internal speaker. This method is limited to producing single sounds of given frequencies and durations, although we can combine these sounds to create interesting audio effects or play music. When we use this technique, we have no control over the sound volume. In fact, sound volumes often vary slightly on different systems because the physical properties of the speaker and its housing are not always the same. The PCjr and Tandy 1000 systems have an additional, more powerful chip for producing sounds. This is the Texas Instruments SN76496A sound chip, called the TI sound chip for short. The TI sound chip has three independent voice channels for producing pure tones, and a fourth channel for generating periodic or white noise. Each voice channel has a separate volume control that allows us to control the loudness of the sound it emits. Synchronous Sound A sound effect is said to be synchronous if it is produced while no other activity is being performed. In other words, a program makes a synchronous sound by starting the sound, waiting for a specified duration, and then stopping the sound. The program must wait for the sound to complete before doing anything else. As long as the duration is relatively short, the fact that the sound is synchronous has little or no effect on the program's execution speed. Fastgraph includes routines for producing synchronous sound using either the 8253-5 programmable timer or the TI sound chip. The fg_sound routine uses the programmable timer to produce a sound of a given frequency and duration. The frequency, defined by the first argument, is expressed in Hertz and must be an integer value between 18 and 32,767. The second argument defines the duration and is expressed in clock ticks; there are 18.2 clock ticks per second. If the duration is zero or negative, the sound will continue until it is stopped with the fg_quiet routine. Chapter 13: Sound Effects 235 Example 13-1 uses the fg_sound routine to create different sound effects, pausing for one second between each. It first produces three distinct sounds of 20, 100, and 1,000 Hertz. Each of these sounds lasts for approximately 1/6 of a second (three clock ticks). The program then makes a warbling noise by quickly alternating sounds of similar frequencies. Finally, the program creates a sliding tone of increasing frequencies between 100 and 500 Hertz. Each tone in this sequence lasts for two clock ticks, so it takes about 4.5 seconds to play the entire sequence. In all cases, example 13-1 displays an identifying message just before each sound. Example 13-1. #include #include void main(void); void main() { int freq; printf("20 Hz tone...\n"); fg_sound(20,3); fg_waitfor(18); printf("100 Hz tone...\n"); fg_sound(100,3); fg_waitfor(18); printf("1000 Hz tone...\n"); fg_sound(1000,3); fg_waitfor(18); printf("warble...\n"); fg_sound(400,1); fg_sound(410,1); fg_sound(400,1); fg_sound(410,1); fg_waitfor(18); printf("sliding tone from 100 to 500 Hz...\n"); for (freq = 100; freq <= 500; freq+=10) fg_sound(freq,2); } The fg_voice routine is analogous to the fg_sound routine, but it uses the TI sound chip rather than the programmable timer to create sound. For this reason, the fg_voice routine can only be used on the PCjr or Tandy 1000 systems. The TI sound chip allows us to control the volume of a sound, and it also offers four distinct voice channels. Thus, fg_voice requires two additional arguments besides frequency and duration to define the voice channel and sound volume. The first argument to fg_voice defines the voice channel, as shown below. 236 Fastgraph User's Guide value meaning 1 voice channel #1 2 voice channel #2 3 voice channel #3 4 voice channel #4, periodic noise 5 voice channel #4, white noise If we use voice channels 1, 2, or 3, the second argument defines the sound frequency in Hertz, between 18 and 32,767. If we use voice channel 4, however, the second argument instead is a value that represents a specific frequency, as shown in this table. value frequency 0 512 Hertz 1 1024 Hertz 2 2048 Hertz The third argument defines the sound volume. It must be between 0 and 15, where 0 is silent and 15 is loudest. The fourth argument defines the sound duration in clock ticks. As with the fg_sound routine, there are 18.2 clock ticks per second, and if the duration is zero or negative, the sound will continue until stopped with the fg_quiet routine. Example 13-2 uses the fg_voice routine to create different sound effects using the TI sound chip. As in example 13-1, there is a pause of one second between each. The program first calls the fg_testmode routine to be sure it is running on a PCjr or Tandy 1000 system (video mode 9 is only available on these systems). If so, the program uses voice channel #4 to produce a 2,048 Hertz periodic noise, followed by white noise of the same frequency. Both sounds are emitted at the maximum volume level (15) and last for about 1/6 of a second each (three clock ticks). After these noises, example 13-2 produces a 500 Hertz tone of increasing volume. In all cases, the program displays an identifying message just before each sound. Example 13-2. #include #include #include void main(void); void main() { int volume; if (fg_testmode(9,0) == 0) { Chapter 13: Sound Effects 237 printf("This program requires a PCjr or "); printf("a Tandy 1000 system.\n"); exit(1); } printf("2048 Hz periodic noise...\n"); fg_voice(4,2,15,3); fg_waitfor(18); printf("2048 Hz white noise...\n"); fg_voice(5,2,15,3); fg_waitfor(18); printf("500 Hz tone of increasing volume...\n"); for (volume = 1; volume <= 15; volume++) { fg_voice(1,500,volume,0); fg_waitfor(4); } fg_quiet(); } Note how example 13-2 uses a duration of zero (continuous sound) and the fg_waitfor routine to specify the duration for each volume level the 500 Hertz tone sequence. This causes the transition between changes in volume to blend better with each other. The fg_quiet routine, which stops continuous sound started with the fg_sound or fg_voice routines, ends the sound after the final volume level. The fg_sound and fg_voice routines each produce a single sound. We've seen how to combine sounds to produce sound effects, but still the individual sounds are defined numerically -- that is, by a certain frequency and duration. It is often easier to create sounds from musical notes, and for this reason Fastgraph includes a routine fg_music that produces such sounds. The fg_music routine uses the programmable timer to produce synchronous sound; it does not support the TI sound chip. The fg_music routine has a single argument called the music string, passed by reference as a byte array or character string. The music string is simply a variable-length sequence of music commands, followed by a dollar- sign ($) terminator. Music commands are summarized in the following table. command meaning A thru G Play the specified note in the current octave. # May be appended to a note character (A through G) to make that note sharp. . May be appended to a note character (A through G) or a sharp (#) to extend that note by half its normal length. Multiple dots may be used, and each will again extend the note by half as much as the previous extension. 238 Fastgraph User's Guide Ln Set the length of subsequent notes and pauses. The value of n is an integer between 1 and 64, where 1 indicates a whole note, 2 a half note, 4 a quarter note, and so forth. If no L command is present, L4 is assumed. On Set the octave for subsequent notes. The value of n may be an integer between 0 and 6 to set a specific octave. It also can be a plus (+) or minus (-) character to increment or decrement the current octave number. Octave 4 contains middle C, and if no O command is present, O4 is assumed. P Pause (rest) for the duration specified by the most recent L command. Sn Set the amount of silence between notes. The value of n is an integer between 0 and 2. If n is 0, each note plays for the full period set by the L command (music legato). If n is 1, each note plays for 7/8 the period set by the L command (music normal). If n is 2, each note plays for 3/4 the period set by the L command (music staccato). If no S command is present, S1 is assumed. Tn Set the tempo of the music (the number of quarter notes per minute). The value of n is an integer between 32 and 255. If no T command is present, T120 is assumed. The fg_music routine ignores any other characters in the music string. It also ignores command values outside the allowable range, such as T20 or O8. Example 13-3 illustrates some uses of the fg_music routine. The program plays the first few bars of "Mary Had a Little Lamb", followed by the musical scale (including sharps) in two octaves, and finally the introduction to Beethoven's Fifth Symphony. There is a pause of one second between each piece of music, and the program displays the titles before playing the music. Blank characters appear in the music strings to help make them more readable. Example 13-3. #include #include void main(void); void main() { printf("Mary Had a Little Lamb...\n"); fg_music("T150 L8 EDCDEEE P DDD P EGG P EDCDEEE L16 P L8 EDDEDC$"); fg_waitfor(18); printf("up the scale in two octaves...\n"); fg_music("L16 CC#DD#EFF#GG#AA#B O+ CC#DD#EFF#GG#AA#B$"); Chapter 13: Sound Effects 239 fg_waitfor(18); printf("Beethoven's Fifth Symphony...\n"); fg_music("T180 O2 L2 P L8 P GGG L2 D# L24 P L8 P FFF L2 D$"); } Asynchronous Sound Sounds made concurrently with other activity in a program are said to be asynchronous. Fastgraph's routines that produce asynchronous sound just start the sound and then immediately return control to the calling program. The sounds will automatically stop when the end of the sequence is reached, and you also can suspend or stop it on demand before that time. None of Fastgraph's asynchronous sound routines have any effect if there is already asynchronous sound in progress. In addition, the asynchronous sound routines temporarily disable the synchronous sound routines (fg_sound, fg_voice, and fg_music) while asynchronous sound is in progress. To expand the range of sound effects and to play fast-tempo music, Fastgraph temporarily quadruples the clock tick interrupt rate from 18.2 to 72.8 ticks per second while producing asynchronous sound. Because many disk controllers rely on the 18.2 tick per second clock rate to synchronize disk accesses, your programs should not perform any disk operations when asynchronous sound is in progress. The fg_sounds routine is the asynchronous version of the fg_sound routine. It uses the programmable timer to play a sequence of tones simultaneous to other operations. This routine expects as its first argument a variable-length integer array, passed by reference, containing pairs of frequency and duration values. As with the fg_sound routine, each frequency is expressed in Hertz and must be between 18 and 32,767. The durations are also measured in clock ticks, but because the interrupt rate is quadrupled, there are 72.8 instead of 18.2 ticks per second. The format of the frequency and duration array passed to fg_sounds is shown below. [0] frequency of sound 1 [1] duration of sound 1 [2] frequency of sound 2 [3] duration of sound 2 . . . [2n-2] frequency of sound n [2n-1] duration of sound n [2n] terminator (0) 240 Fastgraph User's Guide Note that a null character (that is, a zero byte) terminates the array. The second argument passed to fg_sounds is an integer value indicating the number of times to cycle through the frequency and duration array. If this value is negative, the sounds will continue until stopped with the fg_hush or fg_hushnext routines. Example 13-4 uses the fg_sounds routine to play the 100 to 500 Hertz sliding tone sequence of example 13-1. To prove the sounds are being made concurrently with other operations, messages are displayed while the sequence is playing. This is controlled by the Fastgraph routine fg_playing, which returns a value of 1 if asynchronous sounds are in progress, and 0 if not. Note how the duration must be specified as 8 clock ticks (instead of 2 as in example 13-1) to compensate for the quadrupled clock tick interrupt rate. Example 13-4. #include #include void main(void); void main() { int i; int freq; int sound_array[83]; i = 0; for (freq = 100; freq <= 500; freq+=10) { sound_array[i++] = freq; sound_array[i++] = 8; } sound_array[i] = 0; fg_sounds(sound_array,1); while(fg_playing()) printf("Still playing...\n"); } Just as the fg_sounds routine is analogous to the fg_sound routine, there is a Fastgraph routine fg_voices that is similar to the fg_voice routine. That is, fg_voices uses the TI sound chip to play an asynchronous sequence of tones. Its arguments are the same as those of the fg_sounds routine, but the structure of the sound array is different. Its structure is: [0] channel # of sound 1 [1] frequency of sound 1 [2] volume of sound 1 [3] duration of sound 1 Chapter 13: Sound Effects 241 [4n-4] channel # of sound n [4n-3] frequency of sound n [4n-2] volume of sound n [4n-1] duration of sound n [4n] terminator (0) The channel numbers, frequencies, volumes, and durations must be in the same ranges as discussed in the description of the fg_voice routine, except the durations are quadrupled because of the accelerated clock tick interrupt rate. Again, note that a null character (that is, a zero byte) terminates the array. Example 13-5 uses the fg_voices routine to play the 500 Hertz tone sequence of increasing volume introduced in example 13-2. As in example 13-4, the program displays messages while the tone sequence is playing to demonstrate the sounds are being made concurrently with other operations. Note how the duration is now 16 clock ticks (instead of 4 as in example 13-2) because of the quadrupled clock tick interrupt rate. Example 13-5. #include #include void main(void); void main() { int voice_array[61]; int i; int volume; if (fg_testmode(9,0) == 0) { printf("This program requires a PCjr or "); printf("a Tandy 1000 system.\n"); exit(1); } i = 0; for (volume = 1; volume <= 15; volume++) { voice_array[i++] = 1; /* use channel 1 */ voice_array[i++] = 500; /* 500 Hz frequency */ voice_array[i++] = volume; /* variable volume */ voice_array[i++] = 16; /* duration */ } voice_array[i] = 0; fg_voices(voice_array,1); 242 Fastgraph User's Guide while(fg_playing()) printf("Still playing...\n"); } There is also an asynchronous version of the fg_music routine. It is called fg_musicb, and it uses the same format music string as the fg_music routine does. However, the fg_musicb routine has a second argument that specifies the number of times to cycle through the music string. If this value is negative, the music will play repetitively until you stop it with the fg_hush or fg_hushnext routine. Example 13-6 plays the same three pieces of music as example 13-3, but it does so concurrently with other operations. As the music plays, the program continuously displays the title of each piece. Note how we can take advantage of the repetition in the music string for the "up the scale" sequence by playing the sequence twice. Example 13-6. #include #include void main(void); void main() { fg_musicb("T150 L8 EDCDEEE P DDD P EGG P EDCDEEE L16 P L8 EDDEDC$",1); while (fg_playing()) printf("Mary Had a Little Lamb...\n"); fg_waitfor(18); fg_musicb("L16 CC#DD#EFF#GG#AA#B O+$",2); while (fg_playing()) printf("up the scale in two octaves...\n"); fg_waitfor(18); fg_musicb("T180 O2 L2 P L8 P GGG L2 D# L24 P L8 P FFF L2 D$",1); while (fg_playing()) printf("Beethoven's Fifth Symphony...\n"); } The next example demonstrates the effects of the Fastgraph routines fg_hush and fg_hushnext, which stop sounds started with the fg_sounds, fg_voices, or fg_musicb routines. The fg_hush routine immediately stops asynchronous sound, whereas the fg_hushnext routine does so when the current cycle finishes. Neither routine has any arguments, and neither routine has any effect if no asynchronous sound is in progress. Furthermore, note that fg_hushnext has no effect unless the asynchronous sound is continuous. Example 13-7 runs in any text or graphics video mode. It displays rectangles in up to 16 colors while playing continuous asynchronous music. The program periodically checks for keystrokes with the fg_intkey routine, and it continues to play the music while there is no keyboard activity. If you press the Escape key, the program uses fg_hush to stop the music immediately; this causes an exit from the while loop. If you press any other Chapter 13: Sound Effects 243 key, the program uses fg_hushnext to stop the music as soon as the current repetition finishes. Once it does, the program exits the while loop because fg_playing will return a value of zero. Example 13-7. #include void main(void); #define ESC 27 void main() { int color; int old_mode; unsigned char key, aux; old_mode = fg_getmode(); fg_setmode(fg_automode()); color = 0; fg_musicb("O4 L16 CC#DD#EFF#GG#AA#B O+ CC#DD#EFF#GG#AA#B$",-1); while (fg_playing()) { color = (color + 1) & 15; fg_setcolor(color); fg_rect(0,fg_getmaxx(),0,fg_getmaxy()); fg_waitfor(4); fg_intkey(&key,&aux); if (key == ESC) fg_hush(); else if (key+aux != 0) fg_hushnext(); } fg_setmode(old_mode); fg_reset(); } Example 13-7 also demonstrates an important side-effect of the fg_musicb routine when playing continuous music. Any length, octave, silence, or tempo values changed within the string are not reset to their original values at the beginning of each repetition. If we did not include the O4 command at the beginning of the string, the later O+ command would cause the music to play in octaves 4 and 5 during the first repetition, 5 and 6 during the second repetition, and octave 6 for all subsequent repetitions (because you cannot increase the octave number above 6). The final two routines relating to asynchronous sound are fg_resume and fg_suspend. The fg_suspend routine suspends music previously started by fg_musicb, while fg_resume restarts the music from the point where it was suspended. Example 13-8 plays the first few bars of "Mary Had a Little Lamb". If you press any key while the song is playing, it stops. Then, after another keystroke, the music resumes and continues until finished. 244 Fastgraph User's Guide Example 13-8. #include #include void main(void); void main() { fg_musicb("T150 L8 EDCDEEE P DDD P EGG P EDCDEEE L16 P L8 EDDEDC$",1); fg_waitkey(); fg_suspend(); printf("Music suspended. Press any key to resume.\n"); fg_waitkey(); fg_resume(); printf("Music resumed.\n"); while (fg_playing()); printf("Music finished.\n"); } The fg_suspend routine has no effect if there is no asynchronous music in progress. Similarly, fg_resume has no effect if there is no suspended music. If you call fg_suspend and then need to cancel the music or exit to DOS instead of restarting the music, call fg_hush instead of fg_resume. Summary of Sound Routines This section summarizes the functional descriptions of the Fastgraph routines presented in this chapter. More detailed information about these routines, including their arguments and return values, may be found in the Fastgraph Reference Manual. FG_HUSH immediately stops asynchronous sound started with the fg_sounds, fg_voices, or fg_musicb routines. FG_HUSHNEXT is similar to fg_hush, but it does not stop the asynchronous sound until the current repetition finishes. FG_MUSIC uses the programmable timer to play a sequence of musical tones. FG_MUSICB is the asynchronous version of the fg_music routine. It uses the programmable timer to play a sequence of musical tones, concurrent with other activity. FG_PLAYING determines whether or not there is any asynchronous sound in progress. FG_QUIET stops continuous synchronous sound started with the fg_sound or fg_voice routines. FG_RESUME restarts asynchronous music previously suspended by fg_suspend. Chapter 13: Sound Effects 245 FG_SOUND produces a tone of a specified frequency and duration using the programmable timer. FG_SOUNDS is the asynchronous version of the fg_sound routine. It can play a series of tones of specified frequencies and durations, concurrent with other activity. FG_SUSPEND suspends asynchronous music previously started by fg_musicb. FG_VOICE produces a tone of a specified frequency, duration, and volume using one of the TI sound chip's four voice channels. FG_VOICES is the asynchronous version of the fg_voice routine. It can play a series of tones of specified frequencies, durations, and volumes, concurrent with other activity. 246 Fastgraph User's Guide Chapter 14 Program Timing 248 Fastgraph User's Guide Overview It is occasionally necessary to delay a program's execution for a brief period, or to determine how long it takes to execute specific sections of a program. Fastgraph includes routines to accomplish these tasks. Some of these routines are said to be real-time, which means they are independent of a system's processor speed, while the speed of others is processor-specific. This chapter describes both classes of timing routines, all of which are independent of the other parts of Fastgraph. Real-Time Routines Real-time operations center around the BIOS time-of-day clock, which is nothing more than a counter that the system automatically increments 18.2 times per second. This number is often called the clock tick interrupt rate because an interrupt routine performs the incrementing. In addition, each increment is usually called a clock tick. The Fastgraph routine fg_waitfor delays a program's execution by the number of clock ticks specified as its argument. Because fg_waitfor uses clock ticks, the actual length of the delay is the same, regardless of the system's processor speed. Even when Fastgraph's asynchronous sound routines quadruple the clock tick interrupt rate, Fastgraph compensates for this internally so fg_waitfor always works as though the actual rate were still 18.2 times per second. Example 14-1 displays a message every five seconds that states how long the program has been running. The fg_waitfor routine produces the five- second delay by pausing 91 (18.2 times 5) clock ticks before the program displays each message. The program returns to DOS when you press any key. Example 14-1. #include #include void main(void); void main() { unsigned int seconds; unsigned char key, aux; seconds = 0; do { fg_waitfor(91); seconds += 5; printf("%u seconds have elapsed.\n",seconds); fg_intkey(&key,&aux); } while (key+aux == 0); } Another common application of the fg_waitfor routine is to slow down a loop that uses the fg_intkey routine to check for keystrokes. In loops that Chapter 14: Program Timing 249 do little else, we may call fg_intkey too rapidly without this delay, and it is then possible that the BIOS may not be able to store characters in its keyboard buffer fast enough. A small delay, even one clock tick, often helps such "tight" loops. The fg_getclock routine provides an efficient way to measure time, especially differences in time. This routine has no arguments and returns a 32-bit unsigned integer (as its function value) representing the number of clock ticks since midnight. Example 14-2 demonstrates the fg_getclock routine. In response to any keystroke (except Escape, which returns control to DOS), the program displays the number of clock ticks since midnight, and the number of ticks since the program started. Example 14-2. #include #include void main(void); #define ESC 27 void main() { unsigned long start, ticks; unsigned char key, aux; start = fg_getclock(); do { ticks = fg_getclock(); printf("%lu ticks since midnight.\n",ticks); printf("%lu ticks since start of program.\n\n",ticks-start); fg_getkey(&key,&aux); } while (key != ESC); } Routines Dependent on the System Speed The fg_waitfor routine described in the previous section is independent of the system's processor speed. This means the actual length of its delay is the same on any system. Another routine, fg_stall, is similar to fg_waitfor, but its delay is proportional to the processor speed. Like fg_waitfor, fg_stall has a single integer argument that specifies the length of the delay. However, instead of being expressed in clock ticks, fg_stall measures the delay in delay units. The fg_stall routine treats the length as an unsigned quantity, so the maximum number of delay units we can specify is 65,535. The following table lists the approximate number of delay units per clock tick on three typical systems. 250 Fastgraph User's Guide system delay units type per clock tick Tandy 1000 HX 675 10 MHz 80286 3,000 25 MHz 80386 11,000 Fastgraph includes a routine that determines the number of delay units per clock tick for the processor being used. This is the fg_measure routine, which has no arguments and returns the number of delay units per clock tick as its function value. Once we determine this value, we can use fg_stall to delay a program's execution in real time. This provides a much more refined delay than the clock tick unit used by fg_waitfor. Example 14-3 is functionally identical to example 14-1, but it uses the fg_stall routine instead of fg_waitfor to delay the program execution. The program first calls the fg_measure routine to determine number of delay units equivalent to one clock tick. It then passes this value to fg_stall, called 91 times inside the for loop to create the five-second delay (because 91 clock ticks equals five seconds). The program returns to DOS when you press any key. Example 14-3. #include #include void main(void); void main() { int i; int units_per_tick; unsigned int seconds; unsigned char key, aux; seconds = 0; printf("Benchmarking system speed...\n"); units_per_tick = fg_measure(); printf("Benchmark completed.\n\n"); do { for (i = 0; i < 91; i++) fg_stall(units_per_tick); seconds += 5; printf("%u seconds have elapsed.\n",seconds); fg_intkey(&key,&aux); } while (key+aux == 0); } One final point: the fg_measure routine takes a few seconds to benchmark the system speed accurately. For this reason, you should only call fg_measure once (typically at the beginning of the program) and use its return value instead of calling fg_measure throughout the program. Chapter 14: Program Timing 251 Summary of Timing Routines This section summarizes the functional descriptions of the Fastgraph routines presented in this chapter. More detailed information about these routines, including their arguments and return values, may be found in the Fastgraph Reference Manual. FG_GETCLOCK returns the number of clock ticks since midnight as its function value. This quantity is a 32-bit unsigned integer. FG_MEASURE returns the approximate number of delay units per clock tick as its function value. This quantity is proportional to the system's processor speed. FG_STALL delays a program's execution for a given number of processor- specific delay units. FG_WAITFOR delays a program's execution for a given number of clock ticks. There are 18.2 clock ticks per second, regardless of the system's processor speed. 252 Fastgraph User's Guide Chapter 15 Miscellaneous Routines 254 Fastgraph User's Guide Overview There are a few remaining Fastgraph routines that really don't fit into any of the categories discussed so far. For this reason, they are described separately in this chapter. Determining Available Memory The fg_memavail routine returns the amount of free conventional memory (in bytes) available to DOS. It returns the amount of memory as its function value, which is a 32-bit unsigned integer. Fg_memavail has no arguments. Example 15-1 uses fg_memavail to show the effects of creating and releasing virtual pages. When run in a video mode in which video pages 1 and 2 are physical pages, the amount of free memory remains the same because these pages use memory that is resident on the video adapter. However, in modes where pages 1 and 2 are virtual pages, the amount of free memory decreases after each call to fg_allocate and returns to its original value after the calls to fg_freepage. Note how the program requests and validates the video mode. Example 15-1. #include #include #include void main(void); void main() { long original, mem0, mem1, mem2; int mode, old_mode; printf("Which video mode? "); scanf("%d",&mode); if (fg_testmode(mode,0) == 0) { printf("Your system does not support that video mode.\n"); exit(1); } if (fg_testmode(mode,3) == 0) { printf("Your system does not have enough memory.\n"); exit(1); } original = fg_memavail(); old_mode = fg_getmode(); fg_setmode(mode); mem0 = fg_memavail(); fg_allocate(1); mem1 = fg_memavail(); fg_allocate(2); mem2 = fg_memavail(); fg_freepage(1); fg_freepage(2); Chapter 15: Miscellaneous Routines 255 fg_setmode(old_mode); fg_reset(); printf("originally = %ld\n",original); printf("after setmode = %ld\n",mem0); printf("after 1st page = %ld\n",mem1); printf("after 2nd page = %ld\n",mem2); printf("at end = %ld\n",memavail()); } Choosing the Video Memory Update Function In Chapter 10, we saw how to use the fg_setfunc routine to perform XOR animation in native EGA and VGA graphics modes (modes 13 to 18). In these video modes, fg_setfunc controls the logical operation applied when the contents of video memory change. The specific operation is defined by its argument, as shown below. value of logical argument operation 0 replacement 1 and 2 or 3 exclusive or If your program does not use the fg_setfunc routine, replacement mode is always used. That is, information written to video memory replaces whatever was there before. The fg_setfunc routine does nothing in CGA, Tandy/PCjr, Hercules, or MCGA graphics modes, or in any text modes. Example 15-2 demonstrates the fg_setfunc routine. The program is similar to example 6-10 which displays 200 random rectangles on the screen. However, example 15-2 displays the rectangles in XOR mode, which means the rectangle intersections will appear in different colors. Example 15-2. #include #include #include void main(void); #define RECTANGLES 200 #define SWAP(a,b,temp) { temp = a; a = b; b = temp; } void main() { int i; int minx, maxx, miny, maxy; int old_mode; int temp; 256 Fastgraph User's Guide int xres, yres; if (fg_egacheck() == 0) { printf("This program requires EGA or VGA.\n"); exit(1); } old_mode = fg_getmode(); fg_setmode(fg_automode()); fg_setfunc(3); xres = fg_getmaxx() + 1; yres = fg_getmaxy() + 1; for (i = 0; i < RECTANGLES; i++) { minx = rand() % xres; maxx = rand() % xres; miny = rand() % yres; maxy = rand() % yres; if (minx > maxx) SWAP(minx,maxx,temp); if (miny > maxy) SWAP(miny,maxy,temp); fg_setcolor(rand()%16); fg_rect(minx,maxx,miny,maxy); } fg_setmode(old_mode); fg_reset(); } Summary of Miscellaneous Routines This section summarizes the functional descriptions of the Fastgraph routines presented in this chapter. More detailed information about these routines, including their arguments and return values, may be found in the Fastgraph Reference Manual. FG_MEMAVAIL returns the amount of memory available to DOS. FG_SETFUNC specifies the logical operation (replacement, or, and, exclusive or) applied when video memory changes in the native EGA and VGA graphics modes. This routine has no effect in other video modes. Appendix A Fastgraph Utilities 258 Fastgraph User's Guide Overview This appendix describes utilities for creating and managing pixel run files used with the fg_dispfile routine. By default, the Fastgraph installation procedure places these utilities in the \FG directory. To use these utilities, you must either (1) copy the .EXE file from \FG to your current directory, (2) make \FG your current directory, or (3) include the \FG directory in your DOS path specification. SNAPSHOT Utility The SNAPSHOT utility is a terminate and stay resident program (TSR) to capture graphic images. It stores the image in Fastgraph's standard pixel run format. To load SNAPSHOT, just enter the command SNAPSHOT at the DOS prompt, and you'll see messages similar to the following if SNAPSHOT loads successfully. C> SNAPSHOT SNAPSHOT Version 1.01 Copyright (c) 1991 Ted Gruber Software. All Rights Reserved. Press - to activate. After SNAPSHOT loads, control returns to the DOS prompt. At this point, you can use any method whatsoever to display a graphic image and then press the Alt and left shift keys at the same time to capture the image. You don't need to load SNAPSHOT for each image capture, just once per system boot. SNAPSHOT uses about 14,000 bytes of conventional memory once loaded. To illustrate the use of SNAPSHOT, suppose you have drawn and saved an image with a commercial paint program, and you want to incorporate this image into a Fastgraph application. Once you load SNAPSHOT, start the paint program and retrieve your image. Then press the Alt and left shift keys simultaneously and wait for the success tone (three quick medium-pitched sounds). Finally, exit the paint program to return to the DOS prompt. The sequence described in the preceding paragraph will store the captured image in Fastgraph's standard pixel run format, in a file named SNAPSHOT.nnn in the current directory. The file type nnn will be the first sequence of digits that does not result in a duplicate file name. That is, if there are no captured image files in the current directory, SNAPSHOT will use the file name SNAPSHOT.000. The next time you capture an image, SNAPSHOT will store it in SNAPSHOT.001, then SNAPSHOT.002, and so forth. If you rename or delete one of these files, SNAPSHOT will again use that file name. For example, if you delete SNAPSHOT.000 but keep SNAPSHOT.001, SNAPSHOT will store the next image it captures in SNAPSHOT.000. If SNAPSHOT is unable to capture the image, it will produce its error tone (a single low-pitched sound). The most common cause of this is trying to capture an image from a text video mode, but it also will occur if there is not enough disk space or if all 1,000 image file names are already being used. Appendix A: Fastgraph Utilities 259 CLIP Utility The SNAPSHOT utility described in the previous section captures the entire screen. While this might be desirable in some cases, other times you'll just need a portion of the screen. CLIP is an interactive utility that you can use to reduce the size of any image stored in Fastgraph's standard or packed pixel run format. The syntax of the command for invoking the CLIP utility from the DOS prompt is CLIP input_file output_file options where input_file is the name of the original image file, and output_file is the name of the new image file. CLIP does not modify the input_file in any way, but it will overwrite the output_file if an identically named file exists in the current directory. The options list specifies one or more optional switches as shown below. option meaning /M:mode Specifies the video mode number in which to display the image. The mode value must be an integer between 0 and 23. If that video mode is a text mode, an unsupported graphics mode, or an unavailable graphics mode, CLIP displays an error message stating this. If the /M switch is not present, CLIP uses the first available video mode from the list 16, 15, 19, 13, 9, 4, 12. /P Indicates the input_file is in Fastgraph's packed pixel run format. If the /P switch is not present, CLIP assumes it is in standard pixel run format. The output_file will be in the same format as the input_file. /W:width Specifies the image width in pixels. The width value must be an integer between 1 and the horizontal resolution of the selected video mode. If the /W switch is not present, CLIP uses the horizontal resolution of the selected video mode. For example, if you wanted to create the image file PARTIAL.PPR from the packed pixel run file SCREEN.PPR, and use the native 320 by 200 EGA graphics video mode (mode 13), you would start CLIP with the following command. CLIP PARTIAL.PPR SCREEN.PPR /P /M:13 Because no /W switch appears in the above command and the horizontal resolution of mode 13 is 320 pixels, CLIP assumes the image width is 320 pixels. 260 Fastgraph User's Guide When CLIP displays the image and the plus-shaped cursor, you are ready to define one corner of the clipping region (that part of the image used to create the output_file). To do this, use the directional keys on the numeric keypad to move the cursor to the desired position, then press the Enter key. You are then ready to define the clipping region's opposite corner. Again, use the directional keys to move the cursor to the desired position. When defining the second corner, however, CLIP uses a rectangular box instead of the plus-shaped cursor to simplify marking the clipping region's boundaries. After you press Enter to define the second corner, CLIP creates the output_file and displays the resulting image width and the number of pixel runs the image contains. CLIP includes some features that may help you define the clipping region. You can change the distance the cursor moves in response to the directional keys, display the current (x,y) pixel coordinates of the cursor, and change the cursor color. The following table explains the keystrokes that CLIP recognizes when you are defining the clipping region. key meaning F1 Displays the (x,y) coordinate bar at the top of the screen. If the coordinate bar is already on, F1 removes it. F2 Displays the (x,y) coordinate bar at the bottom of the screen. If the coordinate bar is already on, F2 removes it. F3 Changes the cursor or box color from white to black, or from black to white. F4 Displays a summary of the keys CLIP recognizes when defining the clipping region. KP1 Moves the cursor one unit down and to the left. KP2 Moves the cursor one unit down. KP3 Moves the cursor one unit down and to the right. KP4 Moves the cursor one unit to the left. KP6 Moves the cursor one unit to the right. KP7 Moves the cursor one unit up and to the left. KP8 Moves the cursor one unit up. KP9 Moves the cursor one unit up and to the right. + Increases the unit of cursor movement by one pixel. The default cursor movement is one pixel. - Decreases the unit of cursor movement by one pixel. Enter Defines a corner of the clipping region at the cursor position. Esc Exits to DOS without creating the output_file. CLIP will first issue an "Exit to DOS?" prompt in case you pressed the Esc key accidentally. Appendix A: Fastgraph Utilities 261 The CLIP utility requires two video pages to run. Thus, you cannot use it in video modes 17, 18, and 23 because they have only one physical video page and no virtual video pages. CONVERT Utility The CONVERT utility lets you translate files between Fastgraph's supported image file formats. The syntax of the command for invoking CONVERT from the DOS prompt is CONVERT input_file output_file where input_file is the name of the original image file, and output_file is the name of the new translated image file. CONVERT does not modify the input_file in any way, but it will overwrite the output_file if an identically named file exists in the current directory. By default, the file type of the input_file and output_file determine the image format of that file. If the file type is .PPR, CONVERT assumes the image is in Fastgraph's packed pixel run format. If the file type is .SPR, CONVERT assumes it is in the Fastgraph's standard pixel run format. If your image files use other file types, you can explicitly specify the file's image format by appending one of the switches /PPR or /SPR to the file name. The input_file and output_file must not both specify the same image format (CONVERT will display an error message if this is so). The following command will translate the standard pixel run file PICTURE.SPR to packed format. The packed image will be stored in the file PICTURE.IMG, so we must append the switch /PPR to tell CONVERT that it will be a packed file. CONVERT PICTURE.SPR PICTURE.IMG/PPR EDITSPR Utility The EDITSPR utility changes all pixel runs of one color to another color in an image file stored in Fastgraph's standard pixel run (.SPR) format. The syntax of the command for invoking the EDITSPR utility from the DOS command prompt is EDITSPR input_file output_file where input_file is the name of the original image file, and output_file is the name of the new image file. EDITSPR does not modify the input_file in any way, but it will overwrite the output_file if an identically named file exists in the current directory. After it reads the pixel runs from the input_file, EDITSPR will perform the requested color changes. It does this iteratively by asking for an old color value followed by a new color value (each value must be between 0 and 262 Fastgraph User's Guide 255). EDITSPR then finds the pixel runs of the old color value and changes them to the new color value. Following this, EDITSPR displays a message stating how many pixel runs it changed. This process repeats until you enter a negative number for either color value. EDITSPR will next combine adjacent pixel runs of like colors. For example, suppose the original image file contained a color 1 pixel run of length 50, followed by a color 2 pixel run of length 20, followed by another color 1 pixel run of length 10. If you changed all color 2 pixel runs to color 1, EDITSPR will combine these three pixel runs into a single run of length 80. Finally, EDITSPR will close the output_file. GrabRGB Utility The GrabRGB utility is a terminate and stay resident program (TSR) to capture the current red, green, and blue color components of video DAC registers in the 256-color MCGA and VGA graphics modes. You can use GrabRGB together with Fastgraph's SNAPSHOT utility to preserve the original colors of a captured image. To load GrabRGB, just enter the command GRABRGB at the DOS prompt. After GrabRGB loads, control returns to the DOS prompt. At this point, you can use any method whatsoever to display a 256-color graphic image and then press the Alt and right shift keys at the same time to capture the current DAC values. You don't need to load GrabRGB for each image, just once per system boot. GrabRGB uses about 15,000 bytes of conventional memory once loaded. To illustrate the use of GrabRGB, suppose you have drawn and saved a 256-color image with a commercial paint program, and you want to incorporate this image into a Fastgraph application. Once you load SNAPSHOT and GrabRGB, start the paint program and retrieve your image. Then press the Alt and left shift keys to capture the image with SNAPSHOT. After SNAPSHOT's success tone (three quick medium-pitched sounds), press Alt and right shift to capture the RGB components of each DAC register with GrabRGB, and wait for GrabRGB's success tone. Finally, exit the paint program and return to the DOS prompt. The sequence described in the preceding paragraph will write the RGB color components for each DAC register to a file named GRABRGB.nnn in the current directory. The file type nnn will be the first sequence of digits that does not result in a duplicate file name. That is, if there are no GrabRGB output files in the current directory, GrabRGB will use the file name GRABRGB.000. The next time you use GrabRGB, it will store the RGB information in GRABRGB.001, then GRABRGB.002, and so forth. If you rename or delete one of these files, GrabRGB will again use that file name. For example, if you delete GRABRGB.000 but keep GRABRGB.001, GrabRGB will next use the file name GRABRGB.000. If GrabRGB is unable to obtain the RGB components of each DAC register, it will produce its error tone (a single low-pitched sound). The most common cause of this is trying to capture an image from a text video mode, or from a graphics video mode with fewer than 256 colors. It also will occur if there is not enough disk space or if all 1,000 output file names are already being used. Appendix A: Fastgraph Utilities 263 Each line in the output file created by GrabRGB is of the form nnn,rr,gg,bb, where nnn is a DAC register index (between 0 and 255), rr is the red component of that DAC register, gg is the green component, and bb is the blue component. Each color component is between 0 and 63. You can edit and reformat these lines as necessary for inclusion in a C initializer list, a QuickBASIC or FORTRAN data statement, or a Turbo Pascal array-type constant list. Such an array of RGB components, but without the nnn indices, is in the format expected by fg_setdacs. By default, GrabRGB captures information for all 256 DAC registers. If you want to consider only the DAC registers with color components different from their initial values, just include the /D option when you load GrabRGB (that is, use the command GRABRGB /D). If you specify the /D option and all 256 DACs use their default values, the output file will contain a message stating this. HERCFIX Utility The HERCFIX utility allows you to use SNAPSHOT (and possibly other TSRs) with programs that do not update the BIOS data area when establishing the 720x348 Hercules graphics mode. If you use SNAPSHOT with such a program, it will think the monochrome text mode (video mode 7) is active and will produce its low-pitched error tone when activated. If this occurs, use HERCFIX to load the application from which you are trying to capture the image. To do this, enter HERCFIX command at the DOS prompt, where command is the command that starts the application. For example, suppose you use the command PAINTER /H to run a commercial paint program in Hercules graphics mode. To load the paint program with HERCFIX, you would enter the command HERCFIX PAINTER /H. 264 Fastgraph User's Guide Appendix B Using Fastgraph from Assembly Language 266 Fastgraph User's Guide Fastgraph uses the same naming and calling conventions as Microsoft C and Turbo C. The details of these conventions that are important to assembly language programming are summarized below. If you are calling Fastgraph routines from an assembly language program, the program must follow these conventions. All arrays and pointers are passed by reference All other items are passed by value Arguments are pushed onto the stack in reverse order 16-bit function values are returned in the AX register 32-bit function values are returned in the DX:AX register pair Fastgraph routine names are prefixed with an underscore character The small and medium model Fastgraph libraries pass arrays and pointers by near reference, while the large model library does so by far reference. This is consistent with the run-time libraries for the supported compilers. All Fastgraph routines preserve the BP, DS, DI, and SI registers. The contents of any other registers are unknown upon return from a Fastgraph routine (except for the AX register, which will either contain zero or the routine's return value). The following DOS commands show how to assemble a program (using the Microsoft Macro Assembler) and then link it with Fastgraph. In all cases, we'll assume the file EXAMPLE.ASM contains the source code for the program. The resulting executable file will be called EXAMPLE.EXE. small memory model MASM EXAMPLE.ASM; LINK /CP:4096 /E EXAMPLE,,NUL.MAP,FGS medium memory model MASM EXAMPLE.ASM; LINK /CP:4096 /E EXAMPLE,,NUL.MAP,FGM large memory model MASM EXAMPLE.ASM; LINK /CP:4096 /E EXAMPLE,,NUL.MAP,FGL Example B-1 calls the fg_getmode, fg_setmode, fg_reset, and fg_version routines from an assembly language program. The fg_getmode routine returns its function value in the AX register. The fg_setmode routine has a single argument, while fg_reset has no arguments. The fg_version routine has two arguments, both passed by reference. Notice how they are pushed on the stack in reverse order. This example would work with either the medium or large memory model Fastgraph libraries. To make it work with the small model library, all you would need to do is change the word "far" to "near" in the EXTRN declarations, and change the name of the code segment from "main_TEXT" to "_TEXT". Appendix B: Using Fastgraph from Assembly Language 267 Example B-1. EXTRN _fg_getmode:far ; Fastgraph's GETMODE routine EXTRN _fg_reset:far ; Fastgraph's RESET routine EXTRN _fg_setmode:far ; Fastgraph's SETMODE routine EXTRN _fg_version:far ; Fastgraph's VERSION routine stackseg SEGMENT stack ; suppress the linker's stackseg ENDS ; "no stack segment" error message _DATA SEGMENT word public 'DATA' major dw ? ; major version number minor dw ? ; minor version number old_mode dw ? ; original video mode _DATA ENDS dgroup GROUP _DATA ASSUME cs:main_TEXT,ds:dgroup main_TEXT SEGMENT byte public 'CODE' start: mov ax,_DATA ; load segment location mov ds,ax ; into DS register call _fg_getmode ; AX = current video mode mov old_mode,ax ; save it mov ax,4 ; use video mode 4 push ax ; pass argument to SETMODE call _fg_setmode ; establish CGA four-color mode add sp,2 ; remove SETMODE argument push old_mode ; pass argument to SETMODE call _fg_setmode ; restore original video mode add sp,2 ; remove SETMODE argument call _fg_reset ; restore screen attributes lea ax,minor ; get address of minor variable push ax ; pass argument #2 to VERSION lea ax,major ; get address of major variable push ax ; pass argument #1 to VERSION call _fg_version ; get the Fastgraph version number add sp,4 ; remove VERSION arguments mov ah,76 ; function 76: terminate process xor al,al ; errorlevel 0 int 21h ; exit to DOS main_TEXT ENDS END start 268 Fastgraph User's Guide Appendix C Interrupts and Fastgraph 270 Fastgraph User's Guide Interrupts Used by Fastgraph DOS maintains an interrupt vector table that contains the addresses of 256 interrupt handlers, or routines, that perform various functions. The handlers are usually referenced by their hexadecimal interrupt number, between 00 and FF. Of these, only interrupts 60 through 66 and F1 through FF are not used by DOS, the ROM BIOS, or other software and are thus available for user applications. Certain Fastgraph routines use some of the available interrupts. Namely, the fg_music routine uses interrupt 60, the asynchronous sound routines (fg_musicb, fg_sounds, and fg_voices) use interrupts 60 and 61, and all Fastgraph/Light routines use interrupt 62. If your program defines its own interrupt handlers, it must not use any of the interrupts reserved for Fastgraph (unless, of course, it doesn't use any of the Fastgraph routines that would create a conflict). Extending the Time-of-Day Interrupt As mentioned in Chapter 14, the BIOS time-of-day clock is incremented by an interrupt handler. The routine that does this is interrupt 08, a hardware interrupt automatically activated 18.2 times per second. After incrementing the clock, interrupt 08 invokes interrupt 1C, which by default references a "do-nothing" interrupt handler. While changing interrupt 08 can be tricky, it is fairly straightforward to define our own handler for interrupt 1C. This handler also will be executed automatically 18.2 times per second. Example C-1 illustrates how to do this. When we discussed joysticks in Chapter 12, we said there were two ways to monitor joystick button status. One is to intersperse calls to the fg_button routine at strategic places in your program and then take necessary action depending on the button status. However, the problem with this scheme is the chance of missing a button press -- if you press the joystick button and then release it between calls to fg_button, the program will not detect the joystick activity. A preferable method is to call fg_button from a handler for interrupt 1C, which essentially provides continuous monitoring of the joystick buttons. When we need the button status within our program, all we need to do is examine a global variable. Example C-1 consists of a main program (written in C) and an assembly language subroutine named int1C (suitable for the medium memory model). The main program calls int1C to define a handler for interrupt 1C. In response to any keystroke (except Escape), the program displays the button press information for each joystick since the previous keystroke (refer to the discussion of the fg_button routine for the meanings of the status values). When you press the Escape key, the program exits to DOS, but not before calling int1C to restore the original interrupt 1C handler. Example C-1 (main program). #include #include void main(void); #define ESC 27 Appendix C: Interrupts and Fastgraph 271 int status1, status2; void main() { unsigned char key, aux; int1C(1); status1 = 0; status2 = 0; do { printf("\n"); printf("Joystick 1 status: %d\n",status1); printf("Joystick 2 status: %d\n",status2); status1 = 0; status2 = 0; fg_getkey(&key,&aux); } while (key != ESC); int1C(0); } We'll now examine the int1C assembly language subroutine. It actually consists of three parts: a portion to enable our interrupt handler, our handler itself, and a portion to disable the handler. When we call int1C with a nonzero argument, it saves the original data segment (so we can access the global variables within the handler), saves the original handler's address (called the vector) for interrupt 1C, and then enables our handler, which takes the form of a far procedure. The handler routine then begins to be activated at 18.2 times per second. After saving all the important registers, the handler calls the Fastgraph routine fg_button twice, once for each joystick. The return values are logically ORed with the status1 and status2 C global variables to update the button status information. Finally, the handler restores the original registers and returns control to the point of the interrupt. Before the main program exits, it calls int1C with a zero argument to restore the original handler for interrupt 1C. No provision is made in the program to check if we had previously defined our own handler (and hence saved the original interrupt 1C vector), but this could be added with little difficulty. Example C-1 (assembly language subroutine). EXTRN _status1:word ; C global variable for button 1 status EXTRN _status2:word ; C global variable for button 2 status EXTRN _fg_button:far ; Fastgraph routine 272 Fastgraph User's Guide int1C_TEXT SEGMENT byte public 'CODE' ASSUME cs:int1C_TEXT int1C_CS dw ? ; holds original INT 1C segment address int1C_IP dw ? ; holds original INT 1C offset orig_DS dw ? ; holds original data segment _int1C PROC far PUBLIC _int1C push bp ; save caller's BP register mov bp,sp ; make BP point to argument list push si ; save caller's SI register push di ; save caller's DI register mov dx,[bp+6] ; get the flag parameter or dx,dx ; replace the old interrupt handler? jz replace ; yes, branch to that processing ; define a new handler for INT 1C define: mov ax,ds ; put current data segment in AX mov cs:orig_DS,ax ; save it in the control information area mov al,1Ch ; interrupt vector to save mov ah,53 ; function 53: get interrupt vector int 21h ; get the interrupt vector mov cs:int1C_CS,es; save the segment mov cs:int1C_IP,bx; save the offset Appendix C: Interrupts and Fastgraph 273 push ds ; save our DS register mov dx,offset handler ; get offset of interrupt handler mov ax,seg handler; get segment of interrupt handler mov ds,ax ; put it in DS mov al,1Ch ; interrupt vector to change mov ah,37 ; function 37: set interrupt vector int 21h ; change the INT 1C vector to our handler pop ds ; restore our DS register jmp short return ; return to the caller ; replace the original handler for INT 1C replace: push ds ; save our DS register mov dx,cs:int1C_IP; put original INT 1C offset in DX mov ds,cs:int1C_CS; put original INT 1C segment in DS mov ah,37 ; function 37: set interrupt vector mov al,1Ch ; interrupt vector 1C int 21h ; restore original INT 1C vector pop ds ; restore our DS register return: xor ax,ax ; in case int1C was called as a function pop di ; restore our DI register pop si ; restore our SI register pop bp ; restore our BP register ret _int1C ENDP 274 Fastgraph User's Guide handler PROC far ; interrupt handler that replaces INT 1C cli ; disable interrupts while handler active push ax ; save registers that may be altered push bx push cx push dx push di push si push ds push es mov ds,cs:orig_DS ; retrieve the original data segment mov ax,1 ; use joystick 1 push ax ; pass joystick number to button routine call _fg_button ; AX = button status for joystick 1 add sp,2 ; remove the argument or _status1,ax ; update status variable for joystick 1 mov ax,2 ; use joystick 2 push ax ; pass joystick number to button routine call _fg_button ; AX = button status for joystick 2 add sp,2 ; remove the argument or _status2,ax ; update status variable for joystick 2 pop es ; restore altered registers Appendix C: Interrupts and Fastgraph 275 pop ds pop si pop di pop dx pop cx pop bx pop ax iret ; return from the interrupt routine handler ENDP int1C_TEXT ENDS END The example just presented is not meant to be a tutorial on interrupts; there are many good references on DOS that explain them in detail. However, an example specific to Fastgraph should be helpful. 276 Fastgraph User's Guide Appendix D Contents of the Compiler-Specific Libraries 278 Fastgraph User's Guide For each of the supported Fastgraph compilers except QuickBASIC, there is a compiler-specific Fastgraph library (also called the extended Fastgraph library) that contains the following routines: fg_circlew fg_getworld fg_rectw fg_setworld fg_clprect fg_initw fg_restorew fg_swchar fg_dashrw fg_moverw fg_savew fg_swlength fg_dashw fg_movew fg_setangle fg_swtext fg_drawrw fg_paintw fg_setclipw fg_xscreen fg_draww fg_panw fg_setratio fg_xworld fg_drectw fg_pointw fg_setsize fg_yscreen fg_ellipsew fg_polygonw fg_setsizew fg_yworld These routines use the world space coordinate system, either directly or internally. Note that none of them are included in Fastgraph/Light. As mentioned in Chapter 1, if your program uses any of these routines, you must link it with the standard Fastgraph library (FGx.LIB) and the compiler-specific Fastgraph library (FGcccx.LIB). Index 279 I n d e x 8253-5 programmable timer chip 234 Active page 119 Animation 188 dynamic frame 194 dynamic page flipping 196 page flipping 196 simple 188 static frame 192 static page flipping 196 summary 198 XOR 190 ANSI.SYS 28, 29 Assembly language 266 Attribute 48, 104, 105 Available memory 254 Background color 49, 50 Bit maps 136 CGA 141, 142, 149 EGA 144, 151 filler bits 137 Hercules 144 MCGA 145 memory requirements 172 mode-independent 136 mode-specific 140 PCjr 143, 151 retrieving 169 subscript order 137, 138, 147 Tandy 143, 151 text modes 146 VGA 144, 145, 151 Bit-mapped characters 114 Borland C++ 3, 7 Byte boundary 174, 175 CapsLock 212-214 CGA palettes 51 Character cells 18 Character space 40, 100 Characters bit-mapped 114 hardware 101 software 108 Circles 83, 84 Clearing the screen 76 CLIP 259, 260 /M option 259 /P option 259 /W option 259 Clipping 76 Clock tick 248-250 Clock tick interrupt 239, 241, 248 Closed shapes 83 Color 48 Color indices 69 280 Fastgraph User's Guide Color number 50 Color value 50 Compilation 5 Compiler-specific Fastgraph library 278 CONVERT 261 /PPR switch 261 /SPR switch 261 Coordinate conversion 44, 107 Current color 50 Cursor mask 220, 222 Dash pattern 82 Delay units 249, 250 Display patterns 158 CGA 159, 160 EGA 161 Hercules 161 MCGA 162, 163 PCjr 160 Tandy 160 VGA 162, 163 Dithering 88 Dithering matrix 89 alignment 94 CGA 89, 90 EGA 92 Hercules 91 MCGA 93 PCjr 91 Tandy 91 VGA 92, 93 EDITSPR 261, 262 Ellipses 83, 84 EMM386.EXE 129 EMS 129 Expanded memory 129 Expanded Memory Manager 129 Extended Fastgraph library 278 Extended memory 129 Fade 66, 67 FASTGRAF.BI 6 FASTGRAF.H 5 Fastgraph 2 Fastgraph routines fg_allocate 122, 124, 125, 127, 129, 132, 175, 178, 254 fg_alloccms 129 fg_allocems 129, 132 fg_allocxms 129, 132 fg_automode 33, 34, 36, 77, 84, 86, 206 fg_bestmode 30, 34, 35, 36, 95, 122, 124, 125 fg_box 87, 96 fg_boxdepth 87, 88, 96 fg_button 228, 229, 231, 270, 271 fg_capslock 213, 231 fg_chgattr 104, 114 fg_chgtext 104, 114 fg_circle 84, 85, 96 Fastgraph routines (continued) fg_circlew 84, 96 fg_clipmask 166, 167, 184 fg_clpimage 148, 149, 150, 166, 167, 169, 184 fg_clprect 86, 96, 188, 195, 196 fg_clprectw 86, 96 fg_copypage 129, 131, 133, 175, 176, 184 fg_cursor 28, 36, 73, 105, 178 fg_dash 82, 96 fg_dashrel 82, 97 fg_dashrw 82, 97 fg_dashw 82, 97 fg_defcolor 70, 73, 94 fg_dispfile 157, 158, 184, 198, 258 fg_display 153-158, 184, 198 fg_displayp 154, 155, 156-158, 184, 198 fg_disppcx 164, 166, 184 fg_draw 79, 80, 97, 204 fg_drawmap 114, 136, 138-141, 152, 166, 168-172, 181, 185, 198, 222, 226 fg_drawmask 166, 167, 168, 185 fg_drawrel 79, 80, 95, 97 fg_drawrw 79, 97 fg_draww 79, 97 fg_drect 88, 89, 92, 93, 94, 97 fg_drectw 89, 94, 97 fg_drwimage 114, 141, 142-144, 146, 147, 148-150, 152, 166, 167-169, 172, 185, 198, 226 fg_egacheck 32, 36 fg_ellipse 84, 85, 97, 192 fg_ellipsew 84, 97 fg_erase 76, 97 fg_fadein 200, 201, 208 fg_fadeout 200, 208 fg_flipmask 166, 167, 185 fg_flpimage 149, 150, 166, 167, 169, 185 fg_freepage 122, 124, 125, 129, 132, 133, 254 fg_getaddr 127, 128, 133 fg_getattr 105, 114 fg_getchar 105, 114 fg_getclock 249, 251 fg_getcolor 49-51, 57, 73 fg_getdacs 66, 67, 73 fg_gethpage 176, 185 fg_getimage 105, 169, 172, 173, 185, 198 fg_getindex 73 fg_getkey 212, 231 fg_getlines 31, 36 fg_getmap 169-173, 181, 185, 198 fg_getmaxx 41, 42, 44, 86 fg_getmaxy 41, 42, 44, 86 fg_getmode 30, 36, 266 fg_getpage 127, 133 fg_getpixel 76, 77, 97 fg_getrgb 65, 66, 73 fg_getvpage 127, 133 fg_getworld 43, 44 fg_getxjoy 228-231 282 Fastgraph User's Guide Fastgraph routines (continued) fg_getxpos 79, 97 fg_getyjoy 228-231 fg_getypos 79, 97 fg_hush 240, 242, 244 fg_hushnext 240, 242, 243, 244 fg_imagesiz 172, 173, 185 fg_initems 129, 132, 133 fg_initjoy 132, 227, 228, 231 fg_initw 42, 44, 79, 108, 109 fg_initxms 129, 132, 133 fg_intjoy 229-231 fg_intkey 212, 213, 229, 231, 242, 248, 249 fg_locate 101-103, 107, 114, 120, 126, 173 fg_makepcx 164, 166, 185 fg_maprgb 68, 73 fg_measure 250, 251 fg_memavail 254, 256 fg_mousebut 218, 219, 231 fg_mousecur 220, 221, 231 fg_mouseini 132, 215, 216, 219, 231 fg_mouselim 216, 217, 231 fg_mousemov 216, 217, 231 fg_mousepos 218, 219, 231 fg_mouseptr 220, 225, 231 fg_mousespd 217, 231 fg_mousevis 216, 217, 219, 232 fg_move 79, 80, 82, 95, 98, 139, 140, 156, 170, 173, 181, 204 fg_moverel 79, 80, 98 fg_moverw 79, 98 fg_movew 79, 98 fg_music 237-239, 242, 244, 270 fg_musicb 242-244, 270 fg_numlock 213, 232 fg_paint 95, 98, 204 fg_paintw 95, 98 fg_palette 51-60, 62, 64, 65, 68, 69, 74 fg_palettes 69, 74 fg_pan 205-208 fg_panw 205, 206, 208 fg_pattern 158, 159, 163, 185 fg_playing 240, 243, 244 fg_point 76, 77, 98 fg_pointw 76, 98 fg_polygon 83, 98 fg_polygonw 83, 98 fg_quiet 234, 236, 237, 244 fg_rect 60, 65, 86, 87, 88, 98, 107, 120, 138, 141, 178, 188, 195 fg_rectw 86, 98, 113 fg_reset 29, 30, 36, 266 fg_resize 131-133, 207, 208 fg_restore 176-180, 185, 195, 201 fg_restorew 177, 185 fg_resume 243, 244 fg_revimage 149, 150, 166, 167, 169, 185 fg_revmask 166, 167, 185 Index 283 Fastgraph routines (continued) fg_save 176-180, 185 fg_savew 177, 185 fg_scrlock 213, 232 fg_scroll 202-204, 208 fg_setangle 112, 114 fg_setattr 48-50, 74, 101, 102, 104, 114, 120, 121, 123, 125 fg_setcaps 214, 232 fg_setclip 76, 98, 148 fg_setclipw 76, 98 fg_setcolor 28, 49, 50, 51-60, 62, 64, 65, 74, 76, 79, 82, 92, 93, 101, 104, 108, 115, 120, 121, 123, 125, 138, 189, 190, 202, 203 fg_setdacs 66, 67, 74, 263 fg_setfunc 190, 255, 256 fg_sethpage 176, 177, 178, 185, 200, 202 fg_setlines 31, 36 fg_setmode 28, 30, 37, 50, 51, 57, 64, 69, 70, 76, 77, 79, 84, 86, 91, 92, 101, 102, 104, 109, 119, 120, 126, 132, 151, 159, 160, 161, 162, 210, 215, 227, 266 fg_setnum 214, 232 fg_setpage 119, 120, 133, 177 fg_setratio 109, 111, 115 fg_setrgb 53, 56, 60, 62, 64-66, 68, 73, 74, 163 fg_setsize 109, 115 fg_setsizew 109, 115 fg_setvpage 119, 128, 133, 196, 216 fg_setworld 42-44, 109 fg_sound 234-237, 239, 240, 245 fg_sounds 239, 240, 242, 245, 270 fg_stall 249-251 fg_suspend 243-245 fg_swchar 108-113, 115 fg_swlength 113, 115 fg_swtext 112, 113, 115 fg_tcmask 183, 185 fg_tcxfer 183, 186 fg_testmode 29, 30, 35, 37, 120, 122, 151, 177, 236 fg_text 28, 101, 102, 103, 104, 108, 115, 119 fg_transfer 179, 180, 181-183, 186, 194, 195, 196, 201 fg_version 5, 266 fg_voice 235-237, 239, 240, 241, 245 fg_voices 240-242, 245, 270 fg_waitfor 66, 188, 194, 196, 204, 206, 213, 217, 237, 248-251 fg_waitkey 29, 212, 232 fg_where 102, 103, 115, 126 fg_xalpha 44, 107, 115 fg_xconvert 44, 45, 107, 115 fg_xscreen 44, 45, 77 fg_xworld 44, 45, 79 fg_yalpha 44, 107, 115 fg_yconvert 44, 45, 107, 115 fg_yscreen 44, 45, 77 fg_yworld 44, 45, 79 Fastgraph/Light 2 Fastgraph/Light Video Driver 2, 16 284 Fastgraph User's Guide FGDRIVER 16 /U option 16 FGTP unit 6 FGTPX unit 6 Filler bits 137 Foreground color 48, 49 FORTRAN see Microsoft FORTRAN Game port 227 GrabRGB 158, 262, 263 /D option 263 output file format 263 Graphics cursor 79 Graphics modes 18, 21, 50 CGA 21, 51, 52 EGA 23, 56, 58, 59 extended VGA 24 Hercules 22, 54, 55 MCGA 24, 61, 64 native EGA 23 native VGA 24 PCjr 22, 53 Tandy 53 Tandy 1000 22 VGA 24, 61, 63, 64 Hardware characters 101 graphics modes 105 side effects 106 text modes 101 HERCFIX 263 Hidden page 119, 176 HIMEM.SYS 129 Hot spot 224 Image array 147 Image transfer routines 175 Images 136 clipped 148 regular 141 reversed 149 reversed clipped 149 INCLUDE environment variable 5, 6 Input devices 210 INSTALL program 4 /L option 4 Installation 4 Interrupts 270 INTRFACE.FOR 6 Joystick 227 button status 228, 229, 270 calibration 228 characteristics 228 horizontal position 228 initialize 227 keyboard emulation 229 special considerations 230 vertical position 228 Index 285 Keyboard 210 extended codes 210 standard codes 210 Keyboard buffer 212, 249 Keyboard state light 214 Lines dashed 82 solid 79 Linking 5 Logical pages 129 creating 129 releasing 129 Masking map 166, 167 Memory models 3 large 3 medium 3 small 3 Memory update function 255 Mickeys 217 Microsoft C 3, 8 Microsoft FORTRAN 3, 9 Microsoft Mouse 215 Microsoft QuickBASIC 3, 10 Microsoft QuickC 3, 11 Mode X 26 Mouse 214 button status 218 CGA Considerations 226 cursor 216, 220 cursor mask 220, 222 cursor visibility 216 default cursor 222 hot spot 224 initialize 215 limits 216 position 216, 218 screen mask 220, 222 screen updates 216 speed 217 Mouse cursor 216, 220 graphics modes 222 hot spot 224 text modes 220 Mouse driver 215 Music 237 asynchronous 242 restarting 243 stopping 242 suspending 243 synchronous 237 Music commands 237 Music string 237, 242 Musical notes 237 Naming conventions 4 NumLock 212-214 Overscan 51 Packed pixel run map 154, 155 286 Fastgraph User's Guide Palette 19 Palette number 53, 56, 58, 59, 62 Palette registers 53, 56, 58, 59, 61, 63, 69, 70 Palette value 53, 56, 58, 59 Palettes 53, 56, 58, 59, 61, 63 Panning 131, 207 PC Paintbrush 164 PCOPTION environment variable 5 PCX file creating 164 displaying 164 video mode compatibility 165 PCX images 164 Periodic noise 236 Physical pages 118 Pixel bleeding 226 Pixel run 152 Pixel run file 156-158, 258 Pixel run map 152, 153, 158 Pixels 18 Points 76 Polygons 83 Power C 3, 13 Put_string 103 QuickBASIC see Microsoft QuickBASIC QuickC see Microsoft QuickC Real-time operations 248 Rectangles dithered 88 solid 86 unfilled 87 Region fill 95 Register preservation 266 Resolution 18 Reverse video 104 RGB color mapping 68 Screen dissolving 200 Screen mask 220, 222 Screen origin 205 Screen space 40, 41 Scrolling 202 circular 202 end-off 202 Scrolling increment 202 Scrolling region 202 ScrollLock 212, 213 Shadow 104 Sliding tone 235 SNAPSHOT 158, 258, 259, 262, 263 error tone 258 image files 258 success tone 258 Software characters 108 alternate font 108, 109, 112 angle 112 aspect ratio 109 Software characters (continued) font change operator (\) 109 primary font 108, 109 size 109 string length 113 subscript operator (\v) 110 superscript operator (\^) 110 underline operator (_) 110 Sound 234 asynchronous 239 disk accesses during 239 duration 234 frequency 234 PCjr 234 stopping 242 synchronous 234 Tandy 234 volume 234 Sound effects 234-236 Splitter cable 227 Standard color set 50 Strlen 103 Stroke characters 108 Texas Instruments SN76496A sound chip 234 Text cursor 28, 101, 126 Text modes 18, 19, 48 43 lines 31 50 lines 31 color 48 monochrome 49 TI sound chip 234 Transparent color 183 Turbo C 3, 14 Turbo C++ 3, 14 Turbo Pascal 3, 15 Utilities 258 Vector characters 108 Video DAC registers 61, 62, 63-66, 69 Video modes 18 mode 00 20 mode 01 20 mode 02 20 mode 03 20 mode 04 21 mode 05 21 mode 06 22 mode 07 20 mode 09 22 mode 11 23 mode 12 23 mode 13 23 mode 14 24 mode 15 24 mode 16 24 mode 17 25 mode 18 25 mode 19 25 mode 20 25 Index 287 Video modes (continued) mode 21 25 mode 22 26 mode 23 26 mode X 26 summary 18 Video page 118 Video page resizing 207 Video pages 19 active 119 hidden 119, 176 information 127 logical 129 physical 118 resizing 131 segment address 127 virtual 118, 122 visual 119 Video subsystem 19 Virtual colors 69, 70 Virtual pages 118 creating 122 releasing 122 special considerations 128 Visual effects 201 Visual page 119 Warbling 235 White noise 236 World space 40, 42 XMS 129